diff --git a/barker/.env b/barker/.env index dedd4e9..f3c5180 100644 --- a/barker/.env +++ b/barker/.env @@ -18,6 +18,7 @@ ALGORITHM=HS256 JWT_TOKEN_EXPIRE_MINUTES=30 NEW_DAY_OFFSET_MINUTES =420 +TIMEZONE_OFFSET_MINUTES=330 ALEMBIC_LOG_LEVEL=INFO ALEMBIC_SQLALCHEMY_LOG_LEVEL=WARN diff --git a/barker/main.py b/barker/main.py index 7836486..e8a2757 100644 --- a/barker/main.py +++ b/barker/main.py @@ -26,7 +26,7 @@ from .routers.reports import ( sale_report, tax_report ) -from .routers.voucher import show, save, update, receive_payment, void, merge_move +from .routers.voucher import show, save, update, receive_payment, void, merge_move, split from .db.base_class import Base from .core.config import settings @@ -75,6 +75,7 @@ app.include_router(update.router, prefix="/api/voucher", tags=["voucher"]) app.include_router(receive_payment.router, prefix="/api/voucher", tags=["voucher"]) app.include_router(void.router, prefix="/api/voucher", tags=["voucher"]) app.include_router(merge_move.router, prefix="/api", tags=["voucher"]) +app.include_router(split.router, prefix="/api", tags=["voucher"]) # app.include_router(issue_grid.router, prefix="/api/issue-grid", tags=["vouchers"]) # app.include_router(batch.router, prefix="/api/batch", tags=["vouchers"]) diff --git a/barker/routers/voucher/__init__.py b/barker/routers/voucher/__init__.py index bc645d3..4c2b3fd 100644 --- a/barker/routers/voucher/__init__.py +++ b/barker/routers/voucher/__init__.py @@ -68,7 +68,7 @@ def check_permissions(item: Optional[Voucher], voucher_type: VoucherType, permis if item is None: return - if item.voucher_type != VoucherType.KOT and "Edit Printed Bill" not in permissions: + 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", ) diff --git a/barker/routers/voucher/split.py b/barker/routers/voucher/split.py index 6adf7e7..66d1948 100644 --- a/barker/routers/voucher/split.py +++ b/barker/routers/voucher/split.py @@ -1,101 +1,114 @@ import uuid - -import transaction from datetime import datetime -from pyramid.view import view_config +from typing import Optional, List + +from fastapi import APIRouter, HTTPException, status, Depends, Security from sqlalchemy import func +from sqlalchemy.exc import SQLAlchemyError +from sqlalchemy.orm import Session -from barker.models import Voucher, Overview, Kot, Inventory, InventoryModifier -from barker.models.validation_exception import ValidationError -from barker.views.voucher import get_bill_id, do_update_settlements, do_update_table -from barker.views.voucher.update import check_permissions -from barker.views.voucher.void import do_void_settlements - - -@view_config( - request_method="POST", - route_name="v1_vouchers_id", - renderer="json", - request_param="split-bill", - permission="Split Bill", - trans=True, +from ...schemas.auth import UserToken +import barker.schemas.split as schemas +from ...core.security import get_current_active_user as get_user +from ...db.session import SessionLocal +from ...models import Voucher, VoucherType, Kot, Inventory, InventoryModifier, SettleOption, Overview +from ...routers.voucher import ( + do_update_settlements, + get_bill_id, + do_update_table, + check_permissions, ) -def split_voucher(request): - json = request.json_body - now = datetime.now() - id_ = uuid.UUID(request.matchdict["id"]) - update_table = request.GET["u"] == "true" - item = request.dbsession.query(Voucher).filter(Voucher.id == id_).first() - item.void = True - item.reason = "Bill Split" - do_void_settlements(item, request.dbsession) - if update_table: - request.dbsession.query(Overview).filter(Overview.voucher_id == item.id).delete() +from barker.schemas.receive_payment import ReceivePaymentItem as SettleSchema - inventories = [uuid.UUID(i) for i in json["inventories"]] +router = APIRouter() - one_inventories = [i for k in item.kots for i in k.inventories if i.id in inventories] - two_inventories = [i for k in item.kots for i in k.inventories if i.id not in inventories] - save( - one_inventories, - now, - item.voucher_type, - 0, - uuid.UUID(json["table"]["id"]), - item.customer_id, - update_table, - uuid.UUID(request.authenticated_userid), - request.effective_principals, - request.dbsession, - ) - save( - two_inventories, - now, - item.voucher_type, - item.pax, - item.food_table_id, - item.customer_id, - update_table, - uuid.UUID(request.authenticated_userid), - request.effective_principals, - request.dbsession, - ) +# Dependency +def get_db(): + try: + db = SessionLocal() + yield db + finally: + db.close() - transaction.commit() - return True + +@router.post("/split-bill/{id_}") +def split( + id_: uuid.UUID, + data: schemas.Split, + u: bool, # Update table? + db: Session = Depends(get_db), + user: UserToken = Security(get_user, scopes=["split-bill"]), +): + try: + now = datetime.now() + update_table = u + item: Voucher = db.query(Voucher).filter(Voucher.id == id_).first() + item.void = True + item.reason = "Bill Split" + do_update_settlements(item, [SettleSchema(id=SettleOption.VOID(), amount=round(item.amount))], db) + if update_table: + db.query(Overview).filter(Overview.voucher_id == item.id).delete() + check_permissions(None, item.voucher_type, user.permissions) + + one_inventories = [i for k in item.kots for i in k.inventories if i.id in data.inventories] + two_inventories = [i for k in item.kots for i in k.inventories if i.id not in data.inventories] + + one = save(one_inventories, now, item.voucher_type, 0, data.table_id, item.customer_id, user.id_, db,) + if update_table: + do_update_table(one, None, db) + two = save( + two_inventories, now, item.voucher_type, item.pax, item.food_table_id, item.customer_id, user.id_, db, + ) + if update_table: + do_update_table(two, None, db) + db.commit() + except SQLAlchemyError as e: + db.rollback() + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e), + ) + except Exception: + db.rollback() + raise def save( - inventories, now, voucher_type, pax, table_id, customer_id, update_table, user_id, permissions, dbsession, + inventories: List[Inventory], + now: datetime, + voucher_type: VoucherType, + pax: int, + table_id: uuid.UUID, + customer_id: Optional[uuid.UUID], + user_id: uuid.UUID, + db: Session, ): product_quantities = {} - skip_products = set() for i in inventories: - if i.product_id in product_quantities: - product_quantities[i.product_id] += i.quantity + if (i.product_id, i.is_happy_hour) in product_quantities: + product_quantities[(i.product_id, i.is_happy_hour)][1] += i.quantity else: - product_quantities[i.product_id] = i.quantity - for product, quantity in product_quantities.items(): + product_quantities[(i.product_id, i.is_happy_hour)] = (i, i.quantity) + for (product, happy_hour), (inventory, quantity) in product_quantities.items(): if quantity < 0: - raise ValidationError("Quantity is negative") - elif quantity == 0: - skip_products.add(product) - check_permissions(None, voucher_type, permissions) - bill_id = get_bill_id(voucher_type, dbsession) - kot_id = dbsession.query(func.coalesce(func.max(Voucher.kot_id), 0) + 1).scalar() + raise HTTPException( + status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail="Quantity of a product is negative", + ) + bill_id = get_bill_id(voucher_type, db) + kot_id = db.query(func.coalesce(func.max(Voucher.kot_id), 0) + 1).scalar() - item = Voucher(now, pax, bill_id, kot_id, table_id, customer_id, voucher_type, user_id) - dbsession.add(item) - code = dbsession.query(func.coalesce(func.max(Kot.code), 0) + 1).scalar() + item: Voucher = Voucher(now, pax, bill_id, kot_id, table_id, customer_id, voucher_type, user_id) + db.add(item) + code = db.query(func.coalesce(func.max(Kot.code), 0) + 1).scalar() kot = Kot(item.id, code, item.food_table_id, item.date, item.user_id) item.kots.append(kot) - dbsession.add(kot) - for index, old_inventory in enumerate([i for i in inventories if i.product_id not in skip_products]): + db.add(kot) # Multiple inventories of the same product give a key error, but combining messes with modifiers, this + # will get important when modifiers have a price. + for index, (old_inventory, q) in enumerate([i for i in product_quantities.values() if i[1] != 0]): inv = Inventory( kot.id, old_inventory.product_id, - old_inventory.quantity, + q, old_inventory.price, old_inventory.discount, old_inventory.is_happy_hour, @@ -104,15 +117,16 @@ def save( index, ) kot.inventories.append(inv) - dbsession.add(inv) + db.add(inv) for m in old_inventory.modifiers: mod = InventoryModifier(None, m.modifier_id, m.price) inv.modifiers.append(mod) - dbsession.add(mod) - do_update_settlements(item, dbsession) + db.add(mod) + do_update_settlements(item, [], db) if len(kot.inventories) == 0: - raise ValidationError("Please add some products!") - - if update_table: - do_update_table(item, None, dbsession) + raise HTTPException( + status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail="No inventories selected", + ) + db.flush() + return item diff --git a/barker/routers/voucher/update.py b/barker/routers/voucher/update.py index e3c9326..5ef7873 100644 --- a/barker/routers/voucher/update.py +++ b/barker/routers/voucher/update.py @@ -58,6 +58,11 @@ def update( 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.bill_id = get_bill_id(voucher_type, db) diff --git a/barker/schemas/split.py b/barker/schemas/split.py new file mode 100644 index 0000000..752d9f4 --- /dev/null +++ b/barker/schemas/split.py @@ -0,0 +1,15 @@ +import uuid +from typing import List + +from pydantic import BaseModel + +from barker.schemas import to_camel + + +class Split(BaseModel): + inventories: List[uuid.UUID] + table_id: uuid.UUID + + class Config: + alias_generator = to_camel + fields = {"id_": "id"} diff --git a/bookie/src/app/home/home.component.html b/bookie/src/app/home/home.component.html index 1e081f6..aed58cd 100644 --- a/bookie/src/app/home/home.component.html +++ b/bookie/src/app/home/home.component.html @@ -1,68 +1,68 @@