210 lines
6.8 KiB
Python
210 lines
6.8 KiB
Python
import uuid
|
|
|
|
from collections import defaultdict
|
|
from datetime import datetime
|
|
from decimal import Decimal
|
|
from typing import List, Optional
|
|
|
|
import barker.schemas.split as schemas
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, Security, status
|
|
from sqlalchemy import func
|
|
from sqlalchemy.exc import SQLAlchemyError
|
|
from sqlalchemy.orm import Session
|
|
|
|
from ...core.security import get_current_active_user as get_user
|
|
from ...db.session import SessionLocal
|
|
from ...models.inventory import Inventory
|
|
from ...models.inventory_modifier import InventoryModifier
|
|
from ...models.kot import Kot
|
|
from ...models.overview import Overview
|
|
from ...models.settle_option import SettleOption
|
|
from ...models.voucher import Voucher
|
|
from ...models.voucher_type import VoucherType
|
|
from ...routers.voucher import (
|
|
check_permissions,
|
|
do_update_settlements,
|
|
do_update_table,
|
|
get_bill_id,
|
|
)
|
|
from ...schemas.receive_payment import ReceivePaymentItem as SettleSchema
|
|
from ...schemas.user_token import UserToken
|
|
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
# Dependency
|
|
def get_db():
|
|
try:
|
|
db = SessionLocal()
|
|
yield db
|
|
finally:
|
|
db.close()
|
|
|
|
|
|
@router.post("/split-bill/{id_}")
|
|
def split(
|
|
id_: uuid.UUID,
|
|
data: schemas.Split,
|
|
u: bool, # Update table?
|
|
db: Session = Depends(get_db),
|
|
user: UserToken = Security(get_user, scopes=["split-bill"]),
|
|
):
|
|
try:
|
|
now = datetime.utcnow()
|
|
update_table = u
|
|
item: Voucher = db.query(Voucher).filter(Voucher.id == id_).first()
|
|
item.bill_id = None
|
|
original_voucher_type = item.voucher_type
|
|
item.voucher_type = VoucherType.VOID
|
|
item.reason = "Bill Split"
|
|
do_update_settlements(item, [SettleSchema(id=SettleOption.VOID(), amount=round(item.amount))], db)
|
|
if update_table:
|
|
db.query(Overview).filter(Overview.voucher_id == item.id).delete()
|
|
check_permissions(None, item.voucher_type, user.permissions)
|
|
|
|
one_inventories = [i for k in item.kots for i in k.inventories if i.id in data.inventories]
|
|
two_inventories = [i for k in item.kots for i in k.inventories if i.id not in data.inventories]
|
|
|
|
one = save(
|
|
one_inventories,
|
|
now,
|
|
original_voucher_type,
|
|
0,
|
|
data.table_id,
|
|
item.customer_id,
|
|
user.id_,
|
|
db,
|
|
)
|
|
if update_table:
|
|
do_update_table(one, None, db)
|
|
two = save(
|
|
two_inventories,
|
|
now,
|
|
original_voucher_type,
|
|
item.pax,
|
|
item.food_table_id,
|
|
item.customer_id,
|
|
user.id_,
|
|
db,
|
|
)
|
|
if update_table:
|
|
do_update_table(two, None, db)
|
|
db.commit()
|
|
except SQLAlchemyError as e:
|
|
db.rollback()
|
|
raise HTTPException(
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
detail=str(e),
|
|
)
|
|
except Exception:
|
|
db.rollback()
|
|
raise
|
|
|
|
|
|
def save(
|
|
inventories: List[Inventory],
|
|
now: datetime,
|
|
voucher_type: VoucherType,
|
|
pax: int,
|
|
table_id: uuid.UUID,
|
|
customer_id: Optional[uuid.UUID],
|
|
user_id: uuid.UUID,
|
|
db: Session,
|
|
):
|
|
if not are_product_quantities_positive(inventories):
|
|
raise HTTPException(
|
|
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
|
detail="Quantity of a product is negative",
|
|
)
|
|
if len(inventories) == 0:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
|
detail="No inventories selected",
|
|
)
|
|
if happy_hour_items_more_than_regular(inventories):
|
|
raise HTTPException(
|
|
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
|
detail="When product has happy hours\n"
|
|
"Minimum same number of regular items also needed in the whole bill.",
|
|
)
|
|
bill_id = get_bill_id(voucher_type, db)
|
|
kot_id = db.query(func.coalesce(func.max(Voucher.kot_id), 0) + 1).scalar()
|
|
|
|
item: Voucher = Voucher(now, pax, bill_id, kot_id, table_id, customer_id, voucher_type, user_id)
|
|
db.add(item)
|
|
for split_inventories in split_into_kots(inventories):
|
|
if not happy_hour_items_balanced(split_inventories):
|
|
raise HTTPException(
|
|
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
|
detail="Happy hour products are not balanced.",
|
|
)
|
|
code = db.query(func.coalesce(func.max(Kot.code), 0) + 1).scalar()
|
|
kot = Kot(item.id, code, item.food_table_id, item.date, item.user_id)
|
|
item.kots.append(kot)
|
|
db.add(kot)
|
|
db.flush()
|
|
for old_inventory in split_inventories:
|
|
inv = Inventory(
|
|
kot.id,
|
|
old_inventory.product_id,
|
|
old_inventory.quantity,
|
|
old_inventory.price,
|
|
old_inventory.discount,
|
|
old_inventory.is_happy_hour,
|
|
old_inventory.tax_id,
|
|
old_inventory.tax_rate,
|
|
old_inventory.sort_order,
|
|
)
|
|
kot.inventories.append(inv)
|
|
db.add(inv)
|
|
for m in old_inventory.modifiers:
|
|
mod = InventoryModifier(None, m.modifier_id, m.price)
|
|
inv.modifiers.append(mod)
|
|
db.add(mod)
|
|
db.flush()
|
|
do_update_settlements(item, [], db)
|
|
db.flush()
|
|
return item
|
|
|
|
|
|
def split_into_kots(inventories: List[Inventory]) -> list:
|
|
kots = defaultdict(list)
|
|
for item in inventories:
|
|
kots[item.kot_id].append(item)
|
|
return [k for k in kots.values() if len(k) > 0]
|
|
|
|
|
|
def happy_hour_items_balanced(inventories: List[Inventory]) -> bool:
|
|
happy = set((i.product_id, i.quantity) for i in inventories if i.is_happy_hour)
|
|
products = set(i.product_id for i in inventories if i.is_happy_hour)
|
|
other = set((i.product_id, i.quantity) for i in inventories if not i.is_happy_hour and i.product_id in products)
|
|
return happy == other
|
|
|
|
|
|
def are_product_quantities_positive(inventories: List[Inventory]) -> bool:
|
|
quantities = defaultdict(Decimal)
|
|
for i in inventories:
|
|
key = (i.product_id, i.is_happy_hour)
|
|
quantities[key] += i.quantity
|
|
for i in quantities.values():
|
|
if i < 0:
|
|
return False
|
|
return True
|
|
|
|
|
|
def happy_hour_items_more_than_regular(invs: List[Inventory]) -> bool:
|
|
inventories = {}
|
|
for inventory in invs:
|
|
if inventory.product_id not in inventories:
|
|
inventories[inventory.product_id] = {"normal": Decimal(0), "happy": Decimal(0)}
|
|
if inventory.is_happy_hour:
|
|
inventories[inventory.product_id]["happy"] += inventory.quantity
|
|
else:
|
|
inventories[inventory.product_id]["normal"] += inventory.quantity
|
|
for value in inventories.values():
|
|
if value["happy"] > value["normal"]:
|
|
return True
|
|
return False
|