import uuid from datetime import datetime, timedelta from decimal import Decimal import barker.schemas.voucher as schemas from fastapi import APIRouter, HTTPException, Security, status from sqlalchemy import and_, func, or_, select from sqlalchemy.exc import SQLAlchemyError from ...core.config import settings from ...core.security import get_current_active_user as get_user from ...db.session import SessionFuture from ...models.inventory import Inventory from ...models.inventory_modifier import InventoryModifier from ...models.kot import Kot from ...models.product_version import ProductVersion from ...models.voucher import Voucher from ...models.voucher_type import VoucherType from ...printing.bill import print_bill from ...printing.kot import print_kot from ...routers.voucher import ( check_permissions, do_update_bill_numbers, do_update_settlements, do_update_table, get_guest_book, get_tax, happy_hour_has_discount, happy_hour_items_balanced, happy_hour_items_more_than_regular, ) from ...schemas.user_token import UserToken router = APIRouter() @router.put("/update/{id_}") def update_route( id_: uuid.UUID, data: schemas.VoucherIn, u: bool, # Update table? p: int, # Print type g: uuid.UUID | None = None, # Guest book id user: UserToken = Security(get_user), ): try: with SessionFuture() as db: now = datetime.utcnow() product_date = ( now + timedelta(minutes=settings.TIMEZONE_OFFSET_MINUTES - settings.NEW_DAY_OFFSET_MINUTES) ).date() update_table = u voucher_type = VoucherType(p) guest_book = get_guest_book(g, db) need_to_print_kot = False item: Voucher = db.execute(select(Voucher).where(Voucher.id == id_)).scalar_one() check_permissions(item, voucher_type, user.permissions) item.pax = data.pax if guest_book is not None: item.pax = guest_book.pax item.food_table_id = data.table.id_ if data.customer is not None: item.customer_id = data.customer.id_ if item.voucher_type != VoucherType.KOT: raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Internal error, reprints should not reach here", ) if item.voucher_type == VoucherType.KOT and voucher_type != VoucherType.KOT: item.date = now item.voucher_type = voucher_type item.user_id = user.id_ item.last_edit_date = now if happy_hour_items_more_than_regular(data.kots): 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.", ) for ik in item.kots: for iki in ik.inventories: iki.tax_rate = get_tax(iki.tax_rate, voucher_type) # TODO: Need to check from the database product for the max discount # However simple relationship does not work as we need the product validity as well # Still we should not fret too much as we are checking this in the frontend. iki.discount = next( round(inv.discount, 5) for ko in data.kots for inv in ko.inventories if inv.id_ == iki.id ) for dk in data.kots: # Filter out nil inventories dk.inventories = [dki for dki in dk.inventories if round(dki.quantity, 2) != 0] # Filter out nil kots data.kots = [k for k in data.kots if len(k.inventories) > 0] for dk in data.kots: if happy_hour_has_discount(dk.inventories): raise HTTPException( status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail="Discount is not allowed on happy hour products.", ) if not happy_hour_items_balanced(dk.inventories): raise HTTPException( status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail="Happy hour products are not balanced.", ) for nk in data.kots: if nk.id_ is not None: continue need_to_print_kot = True code = db.execute(select(func.coalesce(func.max(Kot.code), 0) + 1)).scalar_one() kot = Kot(item.id, code, item.food_table_id, now, item.user_id) item.kots.append(kot) db.add(kot) for index, nki in enumerate(nk.inventories): product: ProductVersion = db.execute( select(ProductVersion).where( and_( ProductVersion.product_id == nki.product.id_, or_( ProductVersion.valid_from == None, # noqa: E711 ProductVersion.valid_from <= product_date, ), or_( ProductVersion.valid_till == None, # noqa: E711 ProductVersion.valid_till >= product_date, ), ) ) ).scalar_one() if round(nki.quantity, 2) < 0: if "edit-printed-product" not in user.permissions: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail=f"You are not allowed to delete printed products." f"\n In this case {product.full_name}", ) minimum: Decimal = round( Decimal( sum( inv.quantity for ko in item.kots for inv in ko.inventories if ko.id is not None and inv.product_id == product.product_id ) ) * -1, 2, ) if round(nki.quantity, 2) < minimum: raise HTTPException( status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=f"Quantity of {product.full_name} cannot be less than {minimum}", ) tax_rate = get_tax(product.sale_category.tax.rate, voucher_type) inv = Inventory( kot_id=kot.id, product_id=product.product_id, quantity=round(nki.quantity, 2), price=product.price, discount=round(min(nki.discount, product.sale_category.discount_limit), 5), is_hh=nki.is_happy_hour, tax_rate=tax_rate, sort_order=index, tax=product.sale_category.tax, ) kot.inventories.append(inv) db.add(inv) for m in nki.modifiers: mod = InventoryModifier(None, m.id_, Decimal(0)) inv.modifiers.append(mod) db.add(mod) do_update_bill_numbers(item, db) do_update_settlements(item, [], db) if update_table: do_update_table(item, guest_book, db) db.commit() voucher_id = item.id with SessionFuture() as db: if need_to_print_kot: print_kot(voucher_id, db) if item.voucher_type != VoucherType.KOT: print_bill(voucher_id, db) except SQLAlchemyError as e: raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e), )