barker/barker/barker/routers/voucher/save.py

178 lines
6.3 KiB
Python

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