diff --git a/barker/main.py b/barker/main.py index e8a2757..ee5ae2e 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, split +from .routers.voucher import show, save, update, receive_payment, void, merge_move, split, change from .db.base_class import Base from .core.config import settings @@ -76,6 +76,7 @@ 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(change.router, prefix="/api/voucher", 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/models/voucher.py b/barker/models/voucher.py index c39cca5..6e672e5 100644 --- a/barker/models/voucher.py +++ b/barker/models/voucher.py @@ -194,8 +194,8 @@ class Settlement(Base): settle_option = relationship("SettleOption") - def __init__(self, voucher_id=None, settled=None, amount=None, id=None): - self.id = id + def __init__(self, voucher_id=None, settled=None, amount=None, id_=None): + self.id = id_ self.voucher_id = voucher_id self.settled = settled self.amount = amount @@ -211,8 +211,8 @@ class Reprint(Base): user = relationship("User", backref="reprints") - def __init__(self, voucher_id=None, user_id=None, id=None): - self.id = id + def __init__(self, voucher_id=None, user_id=None, id_=None): + self.id = id_ self.date = datetime.now() self.voucher_id = voucher_id self.user_id = user_id diff --git a/barker/routers/voucher/change.py b/barker/routers/voucher/change.py index 266425d..76fe47b 100644 --- a/barker/routers/voucher/change.py +++ b/barker/routers/voucher/change.py @@ -1,42 +1,91 @@ -# import uuid -# -# import transaction -# from pyramid.view import view_config -# -# from barker.models import Voucher, Overview -# from barker.views.voucher.save import save -# from barker.views.voucher.show import voucher_info -# -# -# @view_config( -# request_method="POST", -# route_name="voucher_reprint", -# renderer="json", -# permission="Edit Printed Bill", -# trans=True, -# ) # Permission error -# def voucher_change(request): -# json = request.json_body -# id_ = uuid.UUID(request.matchdict["id"]) -# item = save(json, request.dbsession) -# old = request.dbsession.query(Voucher).filter(Voucher.id == id_).first() -# old.void = True -# old.void_reason = "Bill Discounted / Changed. New Bill ID is {0}".format( -# item.full_bill_id -# ) -# # TODO: Set the Void Settlement -# -# if old.status is None: -# item.status = Overview( -# voucher_id=None, food_table_id=item.food_table_id, status="printed" -# ) -# request.dbsession.add(item.status) -# else: -# request.dbsession.query(Overview).filter(Overview.voucher_id == old.id).update( -# {Overview.voucher_id: item.id} -# ) -# transaction.commit() -# item = request.dbsession.query(Voucher).filter(Voucher.id == item.id).first() -# return voucher_info(item) -# -# +import uuid +from decimal import Decimal +from typing import Optional + +from fastapi import APIRouter, HTTPException, status, Depends, Security +from sqlalchemy.exc import SQLAlchemyError +from sqlalchemy.orm import Session + +from .save import do_save +from ...schemas.auth import UserToken +import barker.schemas.voucher as schemas +from ...core.security import get_current_active_user as get_user +from ...db.session import SessionLocal +from ...models import Voucher, SettleOption, Overview, Reprint +from ...routers.voucher import ( + do_update_settlements, + get_guest_book, +) +from barker.schemas.receive_payment import ReceivePaymentItem as SettleSchema + +router = APIRouter() + + +# Dependency +def get_db(): + try: + db = SessionLocal() + yield db + finally: + db.close() + + +@router.put("/change/{id_}") +def change( + id_: uuid.UUID, + data: schemas.VoucherIn, + u: bool, # Update table? + g: Optional[uuid.UUID] = None, # Guest book id + db: Session = Depends(get_db), + user: UserToken = Security(get_user), +): + try: + old: Voucher = db.query(Voucher).filter(Voucher.id == id_).first() + amount_changed: bool = old.amount != round( + sum( + Decimal((0 if i.is_happy_hour else i.price) * i.quantity * (1 - i.discount) * (1 + i.tax_rate)) + for k in data.kots + for i in k.inventories + ), + 2, + ) + items_changed: bool = len([i for k in old.kots for i in k.inventories]) != len( + [i for k in data.kots for i in k.inventories] + ) + if amount_changed or items_changed: + void_and_issue_new_bill(data, u, g, old, db, user) + else: + reprint_bill(id_, user.id_, 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 reprint_bill(voucher_id: uuid.UUID, user_id: uuid.UUID, db: Session): + item = Reprint(voucher_id=voucher_id, user_id=user_id) + db.add(item) + + +def void_and_issue_new_bill( + data: schemas.VoucherIn, u: bool, g: Optional[uuid.UUID], old: Voucher, db: Session, user: UserToken +): + update_table = u + guest_book = get_guest_book(g, db) + item = do_save(data, old.voucher_type, guest_book, db, user) + db.flush() + old.void = True + old.void_reason = f"Bill Discounted / Changed. New Bill ID is {item.full_bill_id}" + do_update_settlements(old, [SettleSchema(id=SettleOption.VOID(), amount=round(old.amount))], db) + + if update_table: + if old.status is None: + item.status = Overview(voucher_id=None, food_table_id=item.food_table_id, status="printed") + db.add(item.status) + else: + db.query(Overview).filter(Overview.voucher_id == old.id).update({Overview.voucher_id: item.id}) diff --git a/barker/routers/voucher/save.py b/barker/routers/voucher/save.py index e178f8b..e02a458 100644 --- a/barker/routers/voucher/save.py +++ b/barker/routers/voucher/save.py @@ -11,7 +11,7 @@ from ...schemas.auth import UserToken import barker.schemas.voucher as schemas from ...core.security import get_current_active_user as get_user from ...db.session import SessionLocal -from ...models import Voucher, VoucherType, Kot, Product, Inventory, InventoryModifier +from ...models import Voucher, VoucherType, Kot, Product, Inventory, InventoryModifier, GuestBook from ...routers.voucher import ( get_tax, do_update_settlements, @@ -43,58 +43,10 @@ def save( user: UserToken = Security(get_user), ): try: - now = datetime.now() update_table = u voucher_type = VoucherType[p] guest_book = get_guest_book(g, db) - - check_permissions(None, voucher_type, user.permissions) - - 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, - guest_book.pax if guest_book is not None else data.pax, - bill_id, - kot_id, - data.table.id_, - data.customer.id_ if data.customer is not None else None, - voucher_type, - user.id_, - ) - db.add(item) - for k in data.kots: - 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) - db.add(kot) - for index, i in enumerate(k.inventories): - product = db.query(Product).filter(Product.id == i.product.id_).first() - tax_rate = get_tax(product.sale_category.tax.rate, voucher_type) - inv = Inventory( - kot.id, - product.id, - round(i.quantity, 2), - product.price, - round(i.discount, 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_, 0) - inv.modifiers.append(mod) - db.add(mod) - do_update_settlements(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", - ) - + item: Voucher = do_save(data, voucher_type, guest_book, db, user) if update_table: do_update_table(item, guest_book, db) db.commit() @@ -106,3 +58,64 @@ def save( except Exception: db.rollback() raise + + +def do_save( + data: schemas.VoucherIn, + voucher_type: VoucherType, + guest_book: GuestBook, + db: Session, + user: UserToken, +): + now = datetime.now() + + check_permissions(None, voucher_type, user.permissions) + + 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, + guest_book.pax if guest_book is not None else data.pax, + bill_id, + kot_id, + data.table.id_, + data.customer.id_ if data.customer is not None else None, + voucher_type, + user.id_, + ) + db.add(item) + for k in data.kots: + if not len(k.inventories): + continue + 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) + db.add(kot) + for index, i in enumerate(k.inventories): + product = db.query(Product).filter(Product.id == i.product.id_).first() + tax_rate = get_tax(product.sale_category.tax.rate, voucher_type) + inv = Inventory( + kot.id, + product.id, + round(i.quantity, 2), + product.price, + round(i.discount, 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_, 0) + inv.modifiers.append(mod) + db.add(mod) + db.flush() + do_update_settlements(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 diff --git a/barker/routers/voucher/show.py b/barker/routers/voucher/show.py index 0905798..ba50689 100644 --- a/barker/routers/voucher/show.py +++ b/barker/routers/voucher/show.py @@ -49,9 +49,9 @@ def from_bill( @router.get("/from-table/{id_}") def from_bill( - id_: str, # Table ID - v: Optional[uuid.UUID] = None, # Voucher ID - g: Optional[uuid.UUID] = None, # Guest ID + id_: str, # table id + v: Optional[uuid.UUID] = None, # voucher id + g: Optional[uuid.UUID] = None, # guest id db: Session = Depends(get_db), user: UserToken = Security(get_user), ): @@ -116,16 +116,8 @@ def voucher_info(item: Voucher): "taxRate": i.tax_rate, "tax": {"id": i.tax_id, "name": i.tax.name}, "discount": i.discount, - "inventoryModifier": [ - { - "modifier": { - "id": m.modifier.id, - "name": m.modifier.name, - "showInBill": m.modifier.show_in_bill, - }, - "price": m.price, - } - for m in i.modifiers + "modifiers": [ + {"id": m.modifier.id, "name": m.modifier.name, "price": m.price,} for m in i.modifiers ], } for i in k.inventories @@ -137,7 +129,7 @@ def voucher_info(item: Voucher): } -def voucher_blank(table, guest): +def voucher_blank(table: FoodTable, guest: Optional[GuestBook]): return { "pax": table.seats if guest is None else guest.pax, "table": {"id": table.id, "name": table.name}, diff --git a/barker/schemas/voucher.py b/barker/schemas/voucher.py index e37eb44..d95e398 100644 --- a/barker/schemas/voucher.py +++ b/barker/schemas/voucher.py @@ -16,9 +16,9 @@ class Inventory(BaseModel): id_: Optional[uuid.UUID] product: ProductLink quantity: Decimal = Field(ge=0, multiple_of=0.01) - rate: Optional[Decimal] + price: Optional[Decimal] tax: Optional[TaxLink] - taxRate: Optional[Decimal] + tax_rate: Optional[Decimal] discount: Decimal = Field(ge=0, multiple_of=0.00001, le=1) is_happy_hour: bool modifiers: Optional[List[ModifierLink]] diff --git a/bookie/src/app/sales/bills/voucher.service.ts b/bookie/src/app/sales/bills/voucher.service.ts index 7a0f3e0..e2be3b8 100644 --- a/bookie/src/app/sales/bills/voucher.service.ts +++ b/bookie/src/app/sales/bills/voucher.service.ts @@ -69,11 +69,24 @@ export class VoucherService { ); } + change(voucher: Bill, printType: PrintType, guest_book_id: string, updateTable: boolean): Observable { + const options = {params: new HttpParams().set('u', updateTable.toString())}; + if (guest_book_id !== null) { + options.params = options.params.set('g', guest_book_id); + } + return >this.http.put(`${url}/change/${voucher.id}`, voucher, options) + .pipe( + catchError(this.log.handleError(serviceName, 'change')) + ); + } + saveOrUpdate(voucher: Bill, printType: PrintType, guest_book_id: string, updateTable: boolean): Observable { if (!voucher.id) { return this.save(voucher, printType, guest_book_id, updateTable); - } else { + } else if (voucher.voucherType === PrintType.Kot) { return this.update(voucher, printType, guest_book_id, updateTable); + } else { + return this.change(voucher, printType, guest_book_id, updateTable); } } diff --git a/bookie/src/app/sales/home/sales-home.component.ts b/bookie/src/app/sales/home/sales-home.component.ts index 8524657..78efe71 100644 --- a/bookie/src/app/sales/home/sales-home.component.ts +++ b/bookie/src/app/sales/home/sales-home.component.ts @@ -73,17 +73,18 @@ export class SalesHomeComponent implements OnInit { } showDiscount(): Observable { - const dialogRef = this.dialog.open(DiscountComponent, { - // width: '750px', - data: this.mcSer.listForDiscount() - }); - return dialogRef.afterClosed(); + if (this.discountAllowed()) { + const dialogRef = this.dialog.open(DiscountComponent, { + // width: '750px', + data: this.mcSer.listForDiscount() + }); + return dialogRef.afterClosed(); + } else { + return observableOf(false); + } } discount(): void { - if (!this.discountAllowed()) { - return; - } this.showDiscount().subscribe((result: boolean | { id: string, name: string, discount: number }[]) => { if (!!result) { this.bs.discount(result as { id: string, name: string, discount: number }[]); @@ -92,13 +93,18 @@ export class SalesHomeComponent implements OnInit { } billTypeDialog() { - return this.dialog.open(BillTypeComponent).afterClosed().pipe( - tap(x => { - if (!x) { - throwError ('No Bill Type Chosen'); - } - }) - ); + if (this.bs.bill.voucherType !== PrintType.Kot) { + return observableOf(this.bs.bill.voucherType); + } else { + return this.dialog.open(BillTypeComponent).afterClosed().pipe( + tap(x => { + if (!x) { + throwError('No Bill Type Chosen'); + } + }) + ); + } + } confirmTableDialog(table: Table): Observable<{table: Table, confirmed: boolean}> { @@ -143,9 +149,7 @@ export class SalesHomeComponent implements OnInit { if (this.route.snapshot.queryParamMap.has('guest')) { guestBookId = this.route.snapshot.queryParamMap.get('guest'); } - const discountObservable: Observable = (this.discountAllowed()) ? this.showDiscount() : observableOf(''); - - discountObservable.pipe( + this.showDiscount().pipe( tap((result: boolean | { id: string, name: string, discount: number }[]) => { if (!!result) { this.bs.discount(result as { id: string, name: string, discount: number }[]);