import traceback import uuid from datetime import datetime import barker.schemas.merge_move as schemas from fastapi import APIRouter, HTTPException, Security, status from sqlalchemy import delete, func, select, update 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 SessionFuture from ...models.bill import Bill from ...models.kot import Kot from ...models.overview import Overview from ...models.settlement import Settlement from ...models.voucher import Voucher from ...models.voucher_type import VoucherType from ...routers.voucher import ( do_update_bill_numbers, do_update_settlements, do_update_table, ) from ...schemas.user_token import UserToken router = APIRouter() @router.post("/move-kot/merge") def merge_kot( data: schemas.MergeKot, user: UserToken = Security(get_user, scopes=["merge-kots"]), ): try: with SessionFuture() as db: check_if_voucher_is_unprinted(data.voucher_id, db) kots: int = db.execute(select(func.count(Kot.id)).where(Kot.voucher_id == data.voucher_id)).scalar_one() if kots <= 1: raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Single kot cannot be used, move table instead. This error should not show up in frontend", ) check_if_voucher_is_unprinted(data.new_voucher_id, db) db.execute( update(Kot) .where(Kot.id == data.kot_id, Kot.voucher_id == data.voucher_id) .values(voucher_id=data.new_voucher_id) ) donor: Voucher = db.execute(select(Voucher).where(Voucher.id == data.voucher_id)).scalar_one() recipent: Voucher = db.execute(select(Voucher).where(Voucher.id == data.new_voucher_id)).scalar_one() if has_negatives(donor.kots) or happy_hour_exceeds_regular(donor.kots): raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Donor voucher will have negative products or too many happy hours", ) if has_negatives(recipent.kots) or happy_hour_exceeds_regular(recipent.kots): raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Recipient voucher will have negative products or too many happy hours", ) do_update_settlements(donor, [], db) do_update_settlements(recipent, [], db) db.commit() except SQLAlchemyError as e: raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e), ) @router.post("/move-kot/move") def move_kot( data: schemas.MoveKot, user: UserToken = Security(get_user, scopes=["move-kot-to-new-table"]), ): try: with SessionFuture() as db: now = datetime.utcnow() check_if_voucher_is_unprinted(data.voucher_id, db) kots: int = db.execute(select(func.count(Kot.id)).where(Kot.voucher_id == data.voucher_id)).scalar_one() if kots <= 1: raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Single kot cannot be used, move table instead. This error should not show up in frontend", ) kot = db.execute(select(Kot).where(Kot.id == data.kot_id)).scalar_one() kot_id = db.execute(select(func.coalesce(func.max(Voucher.kot_id), 0) + 1)).scalar_one() item = Voucher( now, kot.voucher.pax, kot_id, data.table_id, kot.voucher.customer_id if kot.voucher.customer is not None else None, kot.voucher.voucher_type, user.id_, ) db.add(item) item.kots.append(kot) db.flush() do_update_bill_numbers(item, db) do_update_table(item, None, db) donor: Voucher = db.execute(select(Voucher).where(Voucher.id == data.voucher_id)).scalar_one() if has_negatives(donor.kots) or happy_hour_exceeds_regular(donor.kots): raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Donor voucher will have negative products or too many happy hours", ) if has_negatives(item.kots) or happy_hour_exceeds_regular(item.kots): raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Recipient voucher will have negative products or too many happy hours", ) do_update_settlements(donor, [], db) do_update_settlements(item, [], db) db.commit() except SQLAlchemyError as e: raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e), ) @router.post("/move-table/move") def move_table( data: schemas.MoveTable, user: UserToken = Security(get_user, scopes=["move-table"]), ): try: with SessionFuture() as db: db.execute( update(Overview).where(Overview.voucher_id == data.voucher_id).values(food_table_id=data.table_id) ) db.execute(update(Voucher).where(Voucher.id == data.voucher_id).values(food_table_id=data.table_id)) db.commit() except SQLAlchemyError as e: raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e), ) @router.post("/move-table/merge") def merge_table( data: schemas.MergeTable, user: UserToken = Security(get_user, scopes=["merge-tables"]), ): try: with SessionFuture() as db: check_if_voucher_is_unprinted(data.voucher_id, db) check_if_voucher_is_unprinted(data.new_voucher_id, db) db.execute(update(Kot).where(Kot.voucher_id == data.voucher_id).values(voucher_id=data.new_voucher_id)) db.execute(delete(Bill).where(Bill.voucher_id == data.voucher_id)) db.execute(delete(Overview).where(Overview.voucher_id == data.voucher_id)) db.execute(delete(Settlement).where(Settlement.voucher_id == data.voucher_id)) db.execute(delete(Voucher).where(Voucher.id == data.voucher_id)) recipent: Voucher = db.execute(select(Voucher).where(Voucher.id == data.new_voucher_id)).scalar_one() do_update_settlements(recipent, [], db) db.commit() except SQLAlchemyError as e: traceback.print_exc() raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e), ) def check_if_voucher_is_unprinted(voucher_id: uuid.UUID, db: Session) -> None: voucher_type: VoucherType = db.execute(select(Voucher.voucher_type).where(Voucher.id == voucher_id)).scalar_one() if voucher_type is None: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Original bill not found", ) if voucher_type != VoucherType.KOT: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Bill is printed or void.", ) def has_negatives(kots: list[Kot]) -> bool: inv = [i for k in kots for i in k.inventories] products = set((i.product_id, i.is_happy_hour) for i in inv) for id_, hh in products: quantity = sum(i.quantity for i in inv if i.product_id == id_ and i.is_happy_hour == hh) if quantity < 0: return True return False # This is for the whole bill. eg. Kot 1 => Reg 2 + HH 2; Kot 2 => Reg 4; Kot 3 => Reg - 4 # This is pass okay in happy hours items balanced, but overall this is wrong. Hence this check def happy_hour_exceeds_regular(kots: list[Kot]) -> bool: inv = [i for k in kots for i in k.inventories] products = set(i.product_id for i in inv if i.is_happy_hour) for p in products: r = sum(i.quantity for i in inv if i.product_id == p and not i.is_happy_hour) h = sum(i.quantity for i in inv if i.product_id == p and i.is_happy_hour) if r < h: return True return False