barker/barker/barker/routers/voucher/split.py

133 lines
4.6 KiB
Python

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