barker/barker/barker/routers/voucher/__init__.py

183 lines
7.0 KiB
Python

import uuid
from datetime import datetime
from decimal import Decimal
from typing import List, Set
import barker.schemas.voucher as schemas
from barker.models.bill import Bill
from fastapi import HTTPException, status
from sqlalchemy import func
from sqlalchemy.orm import Session
from sqlalchemy.sql import expression
from ...models.guest_book import GuestBook
from ...models.overview import Overview
from ...models.regime import Regime
from ...models.settle_option import SettleOption
from ...models.settlement import Settlement
from ...models.voucher import Voucher
from ...models.voucher_type import VoucherType
from ...schemas.receive_payment import ReceivePaymentItem as SettleSchema
def get_tax(tax, voucher_type):
if voucher_type in [VoucherType.STAFF, VoucherType.NO_CHARGE]:
return 0
elif voucher_type in [VoucherType.KOT, VoucherType.REGULAR_BILL]:
return tax
else:
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
detail="Unexpected Voucher Type",
)
def do_update_table(item: Voucher, guest_book: GuestBook | None, db: Session):
status_ = "running" if item.voucher_type == VoucherType.KOT else "printed"
if item.status is None:
item.status = Overview(
voucher_id=item.id,
food_table_id=item.food_table_id,
guest_book_id=guest_book.id if guest_book is not None else None,
status=status_,
)
db.add(item.status)
else:
item.status.status = status_
if guest_book is not None:
item.status.guest_book_id = guest_book.id
def check_permissions(item: Voucher | None, voucher_type: VoucherType, permissions: list[str]):
if voucher_type == VoucherType.KOT and "print-kot" not in permissions:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="You are not allowed to print a kot",
)
if voucher_type != VoucherType.KOT and "print-bill" not in permissions:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="You are not allowed to print bill",
)
if item is None:
return
if item.voucher_type != VoucherType.KOT and "edit-printed-bill" not in permissions:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="You are not allowed to edit a printed bill",
)
if item.voucher_type != VoucherType.KOT and voucher_type == VoucherType.KOT:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="This Bill is already printed\nCannot add a Kot to it.",
)
if item.voucher_type == VoucherType.VOID:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail=f"This Bill is already void.\nReason: {item.reason}",
)
def get_guest_book(id_: uuid.UUID | None, db: Session) -> GuestBook | None:
if id_ is None:
return id_
return db.execute(expression.select(GuestBook).where(GuestBook.id == id_)).scalar_one()
def do_update_settlements(voucher: Voucher, others: list[SettleSchema], db: Session) -> bool:
fully_settled = True
settlements: list[SettleSchema] = []
settlements += others
total_amount = voucher.amount
settlements.append(SettleSchema(id=SettleOption.AMOUNT(), amount=-total_amount))
round_off = round(total_amount) - total_amount
if round_off != 0:
settlements.append(SettleSchema(id=SettleOption.ROUND_OFF(), amount=-round_off))
unsettled = sum(x.amount for x in settlements)
if len(others) and unsettled != 0: # This should not be allowed
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
detail="Payment received is not equal to bill amount",
)
if unsettled != 0:
settlements.append(SettleSchema(id=SettleOption.UNSETTLED(), amount=unsettled))
fully_settled = False
for settlement in settlements:
old = [vs for vs in voucher.settlements if vs.settled == settlement.id_]
if len(old) == 1:
old[0].amount = settlement.amount
else:
ns = Settlement(voucher.id, settlement.id_, settlement.amount)
voucher.settlements.append(ns)
db.add(ns)
for removable in (os for os in voucher.settlements if os.settled not in [x.id_ for x in settlements]):
voucher.settlements.remove(removable)
db.delete(removable)
return fully_settled
def do_update_bill_numbers(voucher: Voucher, db: Session) -> bool:
regimes: Set[int] = set()
old_regimes: Set[int] = set()
old_regimes = set(
db.execute(expression.select(Bill.regime_id).where(Bill.voucher_id == voucher.id)).scalars().all()
)
if voucher.voucher_type != VoucherType.REGULAR_BILL:
regimes = set([int(voucher.voucher_type)])
else:
regimes = set(i.tax.regime.id for k in voucher.kots for i in k.inventories)
db.execute(
expression.update(Bill)
.where(Bill.voucher_id == voucher.id, Bill.regime_id.in_(old_regimes - regimes))
.values(is_valid=False)
)
if Regime.KOT() not in old_regimes | regimes:
regimes.add(Regime.KOT())
for r in regimes - old_regimes:
bill_id = db.execute(
expression.select(func.coalesce(func.max(Bill.bill_number), 0) + 1).where(Bill.regime_id == r)
).scalar_one()
bill = Bill(regime_id=r, is_valid=True, bill_number=bill_id)
voucher.bills.append(bill)
db.add(bill)
return True
def happy_hour_items_balanced(inventories: list[schemas.Inventory]) -> bool:
happy = set((i.product.id_, i.quantity) for i in inventories if i.is_happy_hour)
products = set(i.product.id_ for i in inventories if i.is_happy_hour)
other = set((i.product.id_, i.quantity) for i in inventories if not i.is_happy_hour and i.product.id_ in products)
return happy == other
def happy_hour_has_discount(inventories: list[schemas.Inventory]) -> bool:
happy = set(i.product.id_ for i in inventories if i.is_happy_hour)
offenders = [i for i in inventories if i.product.id_ in happy and i.discount != 0]
return len(offenders) > 0
# This is for the whole bill. eg. Kot 1 => Reg 2 + HH 2; Kot 2 => Reg 4; Kot 3 => Reg - 4
# This is pass okay in happy hours items balanced, but overall this is wrong. Hence this check
def happy_hour_items_more_than_regular(kots: list[schemas.Kot]) -> bool:
inventories = {}
for kot in kots:
for inventory in kot.inventories:
if inventory.product.id_ not in inventories:
inventories[inventory.product.id_] = {"normal": Decimal(0), "happy": Decimal(0)}
if inventory.is_happy_hour:
inventories[inventory.product.id_]["happy"] += inventory.quantity
else:
inventories[inventory.product.id_]["normal"] += inventory.quantity
for value in inventories.values():
if value["happy"] > value["normal"]:
return True
return False