From 578385f86674e4bb692d994d49a5eac1e93b997b Mon Sep 17 00:00:00 2001 From: tanshu Date: Thu, 24 Sep 2020 08:47:09 +0530 Subject: [PATCH] Chore: Deduplicated the settle options function for save/update/void/receive payment Ported: Void Receive payment --- barker/main.py | 4 +- barker/routers/voucher/__init__.py | 34 +++--- barker/routers/voucher/receive_payment.py | 102 +++++++++--------- barker/routers/voucher/save.py | 3 +- barker/routers/voucher/show.py | 2 +- barker/routers/voucher/update.py | 3 +- barker/routers/voucher/void.py | 94 ++++++++-------- barker/schemas/receive_payment.py | 29 +++++ .../src/app/sales/bills/bills.component.html | 2 +- bookie/src/app/sales/bills/bills.component.ts | 6 +- bookie/src/app/sales/bills/voucher.service.ts | 15 ++- 11 files changed, 164 insertions(+), 130 deletions(-) create mode 100644 barker/schemas/receive_payment.py diff --git a/barker/main.py b/barker/main.py index c0e7e8a..5517a85 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 +from .routers.voucher import show, save, update, receive_payment, void from .db.base_class import Base from .core.config import settings @@ -72,6 +72,8 @@ app.include_router(guest_book.router, prefix="/api/guest-book", tags=["guest-boo app.include_router(show.router, prefix="/api/voucher", tags=["voucher"]) app.include_router(save.router, prefix="/api/voucher", tags=["voucher"]) 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(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 9eac669..bc645d3 100644 --- a/barker/routers/voucher/__init__.py +++ b/barker/routers/voucher/__init__.py @@ -1,4 +1,5 @@ import uuid +from typing import Optional, List from fastapi import HTTPException from sqlalchemy import func @@ -14,6 +15,8 @@ from barker.models import ( GuestBook, ) +from barker.schemas.receive_payment import ReceivePaymentItem as SettleSchema + def get_tax(tax, voucher_type): if voucher_type in [VoucherType.STAFF, VoucherType.NO_CHARGE]: @@ -39,7 +42,7 @@ def get_bill_id(voucher_type, db): return bill_id -def do_update_table(item, guest_book, db): +def do_update_table(item: Voucher, guest_book: Optional[GuestBook], db: Session): status_ = "running" if item.voucher_type == VoucherType.KOT else "printed" if item.status is None: item.status = Overview( @@ -53,7 +56,7 @@ def do_update_table(item, guest_book, db): item.status.status = status_ -def check_permissions(item, voucher_type, permissions): +def check_permissions(item: Optional[Voucher], 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", @@ -87,26 +90,31 @@ def get_guest_book(id_: uuid.UUID, db: Session): return db.query(GuestBook).filter(GuestBook.id == id_).first() -def do_update_settlements(voucher, db: Session): - settlements = [] +def do_update_settlements(voucher: Voucher, others: List[SettleSchema], db: Session): + settlements: List[SettleSchema] = [] + settlements += others total_amount = voucher.amount - settlements.append({"id": SettleOption.AMOUNT(), "amount": -total_amount}) + settlements.append(SettleSchema(id=SettleOption.AMOUNT(), amount=-total_amount)) round_off = round(total_amount) - total_amount if round_off != 0: - settlements.append({"id": SettleOption.ROUND_OFF(), "amount": -round_off}) - settlements.append({"id": SettleOption.UNSETTLED(), "amount": round(total_amount)}) + 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)) for i in settlements: - amount = i["amount"] - settlement_type_id = i["id"] - old = [s for s in voucher.settlements if s.settled == settlement_type_id] + old = [s for s in voucher.settlements if s.settled == i.id_] if len(old) == 1: - old[0].amount = amount + old[0].amount = i.amount else: - s = Settlement(voucher.id, settlement_type_id, amount) + s = Settlement(voucher.id, i.id_, i.amount) voucher.settlements.append(s) db.add(s) - for i in (i for i in voucher.settlements if i.settled not in [x["id"] for x in settlements]): + for i in (i for i in voucher.settlements if i.settled not in [x.id_ for x in settlements]): voucher.settlements.remove(i) db.delete(i) diff --git a/barker/routers/voucher/receive_payment.py b/barker/routers/voucher/receive_payment.py index 46988e8..c812c21 100644 --- a/barker/routers/voucher/receive_payment.py +++ b/barker/routers/voucher/receive_payment.py @@ -1,63 +1,59 @@ import uuid -from decimal import Decimal -import transaction -from pyramid.view import view_config +from fastapi import APIRouter, HTTPException, status, Depends, Security +from sqlalchemy.exc import SQLAlchemyError +from sqlalchemy.orm import Session -from barker.models import SettleOption, Voucher, Settlement, Overview, VoucherType -from barker.models.validation_exception import ValidationError +from . import do_update_settlements +from ...schemas.auth import UserToken +import barker.schemas.receive_payment as schemas +from ...core.security import get_current_active_user as get_user +from ...db.session import SessionLocal +from ...models import Voucher, VoucherType, SettleOption, Settlement, Overview + +router = APIRouter() -@view_config( - request_method="POST", - route_name="v1_vouchers_id", - renderer="json", - request_param="receive-payment", - permission="Settle Bill", - trans=False, -) -def receive_payment(request): - update_table = request.GET["u"] - json = request.json_body - amounts = [ - j - for j in json["amounts"] - if j["amount"] != 0 and j["id"] not in [SettleOption.AMOUNT(), SettleOption.ROUND_OFF()] - ] - for item in amounts: - item["amount"] = round(Decimal(item["amount"]), 0) - id_ = uuid.UUID(request.matchdict["id"]) - item = request.dbsession.query(Voucher).filter(Voucher.id == id_).first() +# Dependency +def get_db(): + try: + db = SessionLocal() + yield db + finally: + db.close() - if item.voucher_type in [VoucherType.NO_CHARGE, VoucherType.STAFF]: - item.reason = json["name"].strip().title() - total_amount = item.amount - amounts.append({"id": SettleOption.AMOUNT(), "amount": -total_amount}) - round_off = round(total_amount) - total_amount - if round_off != 0: - amounts.append({"id": SettleOption.ROUND_OFF(), "amount": -round_off}) +@router.post("/receive-payment/{id_}") +def update( + id_: uuid.UUID, + data: schemas.ReceivePayment, + u: bool, # Update table? + db: Session = Depends(get_db), + user: UserToken = Security(get_user, scopes=["settle-bill"]), +): + try: + update_table = u + amounts = [ + j for j in data.amounts if j.amount != 0 and j.id_ not in [SettleOption.AMOUNT(), SettleOption.ROUND_OFF()] + ] + for i in amounts: + i.amount = round(i.amount, 0) + item: Voucher = db.query(Voucher).filter(Voucher.id == id_).first() - if sum([i["amount"] for i in amounts]) != 0: - raise ValidationError("Payment received is not equal to bill amount") + if item.voucher_type in [VoucherType.NO_CHARGE, VoucherType.STAFF]: + item.reason = data.name.title() - for i in amounts: - amount = i["amount"] - settlement_type_id = i["id"] - old = [s for s in item.settlements if s.settled == settlement_type_id] - if len(old) == 1: - old[0].amount = amount - else: - s = Settlement(item.id, settlement_type_id, amount) - item.settlements.append(s) - request.dbsession.add(s) + do_update_settlements(item, amounts, db) - allowed = [a["id"] for a in amounts] - for i in (s for s in item.settlements if s.settled not in allowed): - item.settlements.remove(i) - request.dbsession.delete(i) - - if update_table: - request.dbsession.query(Overview).filter(Overview.voucher_id == item.id).delete() - transaction.commit() - return True + if update_table: + db.query(Overview).filter(Overview.voucher_id == item.id).delete() + db.commit() + return True + except SQLAlchemyError as e: + db.rollback() + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e), + ) + except Exception: + db.rollback() + raise diff --git a/barker/routers/voucher/save.py b/barker/routers/voucher/save.py index 45c84dc..e178f8b 100644 --- a/barker/routers/voucher/save.py +++ b/barker/routers/voucher/save.py @@ -1,6 +1,5 @@ import uuid from datetime import datetime -from decimal import Decimal from typing import Optional from fastapi import APIRouter, HTTPException, status, Depends, Security @@ -90,7 +89,7 @@ def save( mod = InventoryModifier(None, m.id_, 0) inv.modifiers.append(mod) db.add(mod) - do_update_settlements(item, db) + 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", diff --git a/barker/routers/voucher/show.py b/barker/routers/voucher/show.py index 389964a..e33d5f4 100644 --- a/barker/routers/voucher/show.py +++ b/barker/routers/voucher/show.py @@ -142,6 +142,6 @@ def voucher_blank(table, guest): "pax": table.seats if guest is None else guest.pax, "table": {"id": table.id, "name": table.name}, "voucherType": VoucherType.KOT.name, - "customer": {"id": guest.customer_id, "name": guest.customer.name} if guest is not None else {}, + "customer": {"id": guest.customer_id, "name": guest.customer.name} if guest is not None else None, "kots": [], } diff --git a/barker/routers/voucher/update.py b/barker/routers/voucher/update.py index 42e8d57..e3c9326 100644 --- a/barker/routers/voucher/update.py +++ b/barker/routers/voucher/update.py @@ -1,6 +1,5 @@ import uuid from datetime import datetime -from decimal import Decimal from typing import Optional from fastapi import APIRouter, HTTPException, status, Depends, Security @@ -96,7 +95,7 @@ def update( mod = InventoryModifier(None, m.id_, 0) inv.modifiers.append(mod) db.add(mod) - do_update_settlements(item, db) + do_update_settlements(item, [], db) if update_table: do_update_table(item, guest_book, db) db.commit() diff --git a/barker/routers/voucher/void.py b/barker/routers/voucher/void.py index ab31991..61a591c 100644 --- a/barker/routers/voucher/void.py +++ b/barker/routers/voucher/void.py @@ -1,58 +1,54 @@ import uuid -import transaction -from pyramid.view import view_config +from fastapi import APIRouter, HTTPException, status, Depends, Security +from sqlalchemy.exc import SQLAlchemyError +from sqlalchemy.orm import Session -from barker.models import Voucher, SettleOption, Settlement, Overview, VoucherType +from . import do_update_settlements +from ...schemas.auth import UserToken +from ...core.security import get_current_active_user as get_user +from ...db.session import SessionLocal +from ...models import Voucher, VoucherType, SettleOption, Overview +from barker.schemas.receive_payment import ReceivePaymentItem as SettleSchema + +router = APIRouter() -@view_config( - request_method="POST", - route_name="v1_vouchers_id", - renderer="json", - request_param="void-bill", - permission="Void Bill", - trans=True, -) -def void_voucher(request): - id_ = uuid.UUID(request.matchdict["id"]) - reason = request.json_body["reason"] - update_table = request.GET["u"] == "true" - - item = request.dbsession.query(Voucher).filter(Voucher.id == id_).first() - - item.void = True - item.reason = reason - item.voucher_type = VoucherType.VOID - - do_void_settlements(item, request.dbsession) - - if update_table: - request.dbsession.query(Overview).filter(Overview.voucher_id == item.id).delete() - transaction.commit() - return True +# Dependency +def get_db(): + try: + db = SessionLocal() + yield db + finally: + db.close() -def do_void_settlements(item, dbsession): - settlements = [] - total_amount = item.amount - settlements.append({"id": SettleOption.AMOUNT(), "amount": -total_amount}) - round_off = round(total_amount) - total_amount - if round_off != 0: - settlements.append({"id": SettleOption.ROUND_OFF(), "amount": -round_off}) - settlements.append({"id": SettleOption.VOID(), "amount": round(total_amount)}) +@router.post("/void-bill/{id_}") +def update( + id_: uuid.UUID, + u: bool, # Update table? + reason: str, + db: Session = Depends(get_db), + user: UserToken = Security(get_user, scopes=["void-bill"]), +): + try: + item: Voucher = db.query(Voucher).filter(Voucher.id == id_).first() - for i in settlements: - amount = i["amount"] - settlement_type_id = i["id"] - old = [s for s in item.settlements if s.settled == settlement_type_id] - if len(old) == 1: - old[0].amount = amount - else: - s = Settlement(item.id, settlement_type_id, amount) - item.settlements.append(s) - dbsession.add(s) + item.void = True + item.reason = reason + item.voucher_type = VoucherType.VOID - for i in (i for i in item.settlements if i.settled not in [x["id"] for x in settlements]): - item.settlements.remove(i) - dbsession.delete(i) + do_update_settlements(item, [SettleSchema(id=SettleOption.VOID(), amount=round(item.amount))], db) + + if u: # Update table + db.query(Overview).filter(Overview.voucher_id == item.id).delete() + db.commit() + return True + except SQLAlchemyError as e: + db.rollback() + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e), + ) + except Exception: + db.rollback() + raise diff --git a/barker/schemas/receive_payment.py b/barker/schemas/receive_payment.py new file mode 100644 index 0000000..e4d1ca0 --- /dev/null +++ b/barker/schemas/receive_payment.py @@ -0,0 +1,29 @@ +import uuid +from typing import Optional, List +from decimal import Decimal + +from pydantic import BaseModel, Field + +from barker.schemas import to_camel +from barker.schemas.customer import CustomerLink +from barker.schemas.modifier import ModifierLink +from barker.schemas.product import ProductLink +from barker.schemas.table import TableLink +from barker.schemas.tax import TaxLink + + +class ReceivePaymentItem(BaseModel): + id_: int + amount: Decimal + + class Config: + fields = {"id_": "id"} + + +class ReceivePayment(BaseModel): + name: str + amounts: List[ReceivePaymentItem] + + class Config: + alias_generator = to_camel + anystr_strip_whitespace = True diff --git a/bookie/src/app/sales/bills/bills.component.html b/bookie/src/app/sales/bills/bills.component.html index 4cac2f4..1eac467 100644 --- a/bookie/src/app/sales/bills/bills.component.html +++ b/bookie/src/app/sales/bills/bills.component.html @@ -26,7 +26,7 @@ {{ bs.bill.table.name }} - / {{ bs.bill.pax }} / {{ bs.bill.customer.name }} + / {{ bs.bill.pax }} / {{ bs.bill.customer?.name }} diff --git a/bookie/src/app/sales/bills/bills.component.ts b/bookie/src/app/sales/bills/bills.component.ts index c50443b..e091f83 100644 --- a/bookie/src/app/sales/bills/bills.component.ts +++ b/bookie/src/app/sales/bills/bills.component.ts @@ -13,7 +13,7 @@ import { TablesDialogComponent } from '../tables-dialog/tables-dialog.component' import { TableService } from '../../tables/table.service'; import { ToasterService } from '../../core/toaster.service'; import { AuthService } from '../../auth/auth.service'; -import { PaxComponent } from "../pax/pax.component"; +import { PaxComponent } from '../pax/pax.component'; @Component({ selector: 'app-bills', @@ -46,8 +46,8 @@ export class BillsComponent implements OnInit { } getPax(): void { - if (this.bs.bill.id || this.bs.bill.customer.id) { - return + if (this.bs.bill.id || this.bs.bill.customer) { + return; } const dialogRef = this.dialog.open(PaxComponent, { // width: '750px', diff --git a/bookie/src/app/sales/bills/voucher.service.ts b/bookie/src/app/sales/bills/voucher.service.ts index e23696b..c73be61 100644 --- a/bookie/src/app/sales/bills/voucher.service.ts +++ b/bookie/src/app/sales/bills/voucher.service.ts @@ -76,19 +76,24 @@ export class VoucherService { } } - receivePayment(id: string, amounts: { id: number; name: string; amount: number }[], name: string, updateTable: boolean): Observable { - const options = {params: new HttpParams().set('receive-payment', '').set('u', updateTable.toString())}; + receivePayment( + id: string, + amounts: { id: number; name: string; amount: number }[], + name: string, + updateTable: boolean + ): Observable { + const options = {params: new HttpParams().set('u', updateTable.toString())}; return >this.http.post( - `${url}/${id}`, {name: name, amounts: amounts}, options + `${url}/receive-payment/${id}`, {name: name, amounts: amounts}, options ).pipe( catchError(this.log.handleError(serviceName, 'receivePayment')) ); } voidBill(id: string, reason: string, updateTable: boolean): Observable { - const options = {params: new HttpParams().set('void-bill', '').set('u', updateTable.toString())}; + const options = {params: new HttpParams().set('reason', reason).set('u', updateTable.toString())}; return >this.http.post( - `${url}/${id}`, {reason: reason}, options + `${url}/void-bill/${id}`, {}, options ).pipe( catchError(this.log.handleError(serviceName, 'voidBill')) );