import traceback 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 sqlalchemy.orm import Session from ...core.config import settings from ...core.security import get_current_active_user as get_user from ...db.session import SessionFuture from ...models.guest_book import GuestBook 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, ) from ...schemas.user_token import UserToken router = APIRouter() @router.post("/save") def save( 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: update_table = u voucher_type = VoucherType(p) guest_book = get_guest_book(g, db) item: Voucher = do_save(data, voucher_type, guest_book, db, user) if update_table: do_update_table(item, guest_book, db) db.commit() voucher_id = item.id with SessionFuture() as db: print_kot(voucher_id, db) if item.voucher_type != VoucherType.KOT: print_bill(voucher_id, db) except SQLAlchemyError as e: traceback.print_exc() raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e), ) def do_save( data: schemas.VoucherIn, voucher_type: VoucherType, guest_book: GuestBook | None, db: Session, user: UserToken, ): now = datetime.utcnow() product_date = (now + timedelta(minutes=settings.TIMEZONE_OFFSET_MINUTES - settings.NEW_DAY_OFFSET_MINUTES)).date() check_permissions(None, voucher_type, user.permissions) kot_id = db.execute(select(func.coalesce(func.max(Voucher.kot_id), 0) + 1)).scalar_one() item = Voucher( now, guest_book.pax if guest_book is not None else data.pax, kot_id, data.table.id_, data.customer.id_ if data.customer is not None else None, voucher_type, user.id_, ) db.add(item) 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 k in data.kots: if not happy_hour_items_balanced(k.inventories): raise HTTPException( status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail="Happy hour products are not balanced.", ) if happy_hour_has_discount(k.inventories): raise HTTPException( status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail="Discount is not allowed on happy hour products.", ) code = db.execute(select(func.coalesce(func.max(Kot.code), 0) + 1)).scalar_one() kot = Kot(item.id, code, item.food_table_id, item.date, item.user_id) item.kots.append(kot) db.add(kot) for index, i in enumerate(k.inventories): if round(i.quantity, 2) == 0: continue total_quantity: Decimal = round( Decimal( sum(inv.quantity for ko in data.kots for inv in ko.inventories if inv.product.id_ == i.product.id_) ), 2, ) product: ProductVersion = db.execute( select(ProductVersion).where( and_( ProductVersion.product_id == i.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 total_quantity < 0: raise HTTPException( status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=f"Quantity of {product.full_name} cannot be less than 0", ) if round(i.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}", ) tax_rate = get_tax(product.sale_category.tax.rate, voucher_type) inv = Inventory( kot.id, product.product_id, round(i.quantity, 2), product.price, round(min(i.discount, product.sale_category.discount_limit), 5), i.is_happy_hour, product.sale_category.tax_id, tax_rate, index, ) kot.inventories.append(inv) db.add(inv) for m in i.modifiers: mod = InventoryModifier(None, m.id_, Decimal(0)) inv.modifiers.append(mod) db.add(mod) db.flush() do_update_settlements(item, [], db) do_update_bill_numbers(item, db) if len(item.kots) == 0 or len(item.kots[0].inventories) == 0: raise HTTPException( status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail="Please add some products", ) return item