barker/barker/barker/routers/voucher/update.py

192 lines
8.3 KiB
Python

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),
)