import uuid from datetime import datetime from typing import Optional, List from fastapi import APIRouter, HTTPException, status, Depends, Security from sqlalchemy import func from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.orm import Session from ...schemas.auth import UserToken import barker.schemas.split as schemas from ...core.security import get_current_active_user as get_user from ...db.session import SessionLocal from ...models import Voucher, VoucherType, Kot, Inventory, InventoryModifier, SettleOption, Overview from ...routers.voucher import ( do_update_settlements, get_bill_id, do_update_table, check_permissions, ) from barker.schemas.receive_payment import ReceivePaymentItem as SettleSchema 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.now() update_table = u item: Voucher = db.query(Voucher).filter(Voucher.id == id_).first() item.void = True 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, item.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, item.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, ): product_quantities = {} for i in inventories: if (i.product_id, i.is_happy_hour) in product_quantities: product_quantities[(i.product_id, i.is_happy_hour)][1] += i.quantity else: product_quantities[(i.product_id, i.is_happy_hour)] = (i, i.quantity) for (product, happy_hour), (inventory, quantity) in product_quantities.items(): if quantity < 0: raise HTTPException( status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail="Quantity of a product is negative", ) 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) 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) # Multiple inventories of the same product give a key error, but combining messes with modifiers, this # will get important when modifiers have a price. for index, (old_inventory, q) in enumerate([i for i in product_quantities.values() if i[1] != 0]): inv = Inventory( kot.id, old_inventory.product_id, q, old_inventory.price, old_inventory.discount, old_inventory.is_happy_hour, old_inventory.tax_id, old_inventory.tax_rate, index, ) 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) do_update_settlements(item, [], db) if len(kot.inventories) == 0: raise HTTPException( status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail="No inventories selected", ) db.flush() return item