Deduplicated the settle options function for save/update/void/receive payment

Ported:
  Void
  Receive payment
This commit is contained in:
2020-09-24 08:47:09 +05:30
parent 6c06271315
commit 578385f866
11 changed files with 164 additions and 130 deletions

View File

@ -26,7 +26,7 @@ from .routers.reports import (
sale_report, sale_report,
tax_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 .db.base_class import Base
from .core.config import settings 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(show.router, prefix="/api/voucher", tags=["voucher"])
app.include_router(save.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(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(issue_grid.router, prefix="/api/issue-grid", tags=["vouchers"])
# app.include_router(batch.router, prefix="/api/batch", tags=["vouchers"]) # app.include_router(batch.router, prefix="/api/batch", tags=["vouchers"])

View File

@ -1,4 +1,5 @@
import uuid import uuid
from typing import Optional, List
from fastapi import HTTPException from fastapi import HTTPException
from sqlalchemy import func from sqlalchemy import func
@ -14,6 +15,8 @@ from barker.models import (
GuestBook, GuestBook,
) )
from barker.schemas.receive_payment import ReceivePaymentItem as SettleSchema
def get_tax(tax, voucher_type): def get_tax(tax, voucher_type):
if voucher_type in [VoucherType.STAFF, VoucherType.NO_CHARGE]: if voucher_type in [VoucherType.STAFF, VoucherType.NO_CHARGE]:
@ -39,7 +42,7 @@ def get_bill_id(voucher_type, db):
return bill_id 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" status_ = "running" if item.voucher_type == VoucherType.KOT else "printed"
if item.status is None: if item.status is None:
item.status = Overview( item.status = Overview(
@ -53,7 +56,7 @@ def do_update_table(item, guest_book, db):
item.status.status = status_ 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: if voucher_type == VoucherType.KOT and "print-kot" not in permissions:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN, detail="You are not allowed to print a kot", 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() return db.query(GuestBook).filter(GuestBook.id == id_).first()
def do_update_settlements(voucher, db: Session): def do_update_settlements(voucher: Voucher, others: List[SettleSchema], db: Session):
settlements = [] settlements: List[SettleSchema] = []
settlements += others
total_amount = voucher.amount 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 round_off = round(total_amount) - total_amount
if round_off != 0: if round_off != 0:
settlements.append({"id": SettleOption.ROUND_OFF(), "amount": -round_off}) settlements.append(SettleSchema(id=SettleOption.ROUND_OFF(), amount=-round_off))
settlements.append({"id": SettleOption.UNSETTLED(), "amount": round(total_amount)}) 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: for i in settlements:
amount = i["amount"] old = [s for s in voucher.settlements if s.settled == i.id_]
settlement_type_id = i["id"]
old = [s for s in voucher.settlements if s.settled == settlement_type_id]
if len(old) == 1: if len(old) == 1:
old[0].amount = amount old[0].amount = i.amount
else: else:
s = Settlement(voucher.id, settlement_type_id, amount) s = Settlement(voucher.id, i.id_, i.amount)
voucher.settlements.append(s) voucher.settlements.append(s)
db.add(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) voucher.settlements.remove(i)
db.delete(i) db.delete(i)

View File

@ -1,63 +1,59 @@
import uuid import uuid
from decimal import Decimal
import transaction from fastapi import APIRouter, HTTPException, status, Depends, Security
from pyramid.view import view_config from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import Session
from barker.models import SettleOption, Voucher, Settlement, Overview, VoucherType from . import do_update_settlements
from barker.models.validation_exception import ValidationError 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( # Dependency
request_method="POST", def get_db():
route_name="v1_vouchers_id", try:
renderer="json", db = SessionLocal()
request_param="receive-payment", yield db
permission="Settle Bill", finally:
trans=False, db.close()
)
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()
if item.voucher_type in [VoucherType.NO_CHARGE, VoucherType.STAFF]:
item.reason = json["name"].strip().title()
total_amount = item.amount @router.post("/receive-payment/{id_}")
amounts.append({"id": SettleOption.AMOUNT(), "amount": -total_amount}) def update(
round_off = round(total_amount) - total_amount id_: uuid.UUID,
if round_off != 0: data: schemas.ReceivePayment,
amounts.append({"id": SettleOption.ROUND_OFF(), "amount": -round_off}) 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: if item.voucher_type in [VoucherType.NO_CHARGE, VoucherType.STAFF]:
raise ValidationError("Payment received is not equal to bill amount") item.reason = data.name.title()
for i in amounts: do_update_settlements(item, amounts, db)
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)
allowed = [a["id"] for a in amounts] if update_table:
for i in (s for s in item.settlements if s.settled not in allowed): db.query(Overview).filter(Overview.voucher_id == item.id).delete()
item.settlements.remove(i) db.commit()
request.dbsession.delete(i) return True
except SQLAlchemyError as e:
if update_table: db.rollback()
request.dbsession.query(Overview).filter(Overview.voucher_id == item.id).delete() raise HTTPException(
transaction.commit() status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e),
return True )
except Exception:
db.rollback()
raise

View File

@ -1,6 +1,5 @@
import uuid import uuid
from datetime import datetime from datetime import datetime
from decimal import Decimal
from typing import Optional from typing import Optional
from fastapi import APIRouter, HTTPException, status, Depends, Security from fastapi import APIRouter, HTTPException, status, Depends, Security
@ -90,7 +89,7 @@ def save(
mod = InventoryModifier(None, m.id_, 0) mod = InventoryModifier(None, m.id_, 0)
inv.modifiers.append(mod) inv.modifiers.append(mod)
db.add(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: if len(item.kots) == 0 or len(item.kots[0].inventories) == 0:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail="Please add some products", status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail="Please add some products",

View File

@ -142,6 +142,6 @@ def voucher_blank(table, guest):
"pax": table.seats if guest is None else guest.pax, "pax": table.seats if guest is None else guest.pax,
"table": {"id": table.id, "name": table.name}, "table": {"id": table.id, "name": table.name},
"voucherType": VoucherType.KOT.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": [], "kots": [],
} }

View File

@ -1,6 +1,5 @@
import uuid import uuid
from datetime import datetime from datetime import datetime
from decimal import Decimal
from typing import Optional from typing import Optional
from fastapi import APIRouter, HTTPException, status, Depends, Security from fastapi import APIRouter, HTTPException, status, Depends, Security
@ -96,7 +95,7 @@ def update(
mod = InventoryModifier(None, m.id_, 0) mod = InventoryModifier(None, m.id_, 0)
inv.modifiers.append(mod) inv.modifiers.append(mod)
db.add(mod) db.add(mod)
do_update_settlements(item, db) do_update_settlements(item, [], db)
if update_table: if update_table:
do_update_table(item, guest_book, db) do_update_table(item, guest_book, db)
db.commit() db.commit()

View File

@ -1,58 +1,54 @@
import uuid import uuid
import transaction from fastapi import APIRouter, HTTPException, status, Depends, Security
from pyramid.view import view_config 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( # Dependency
request_method="POST", def get_db():
route_name="v1_vouchers_id", try:
renderer="json", db = SessionLocal()
request_param="void-bill", yield db
permission="Void Bill", finally:
trans=True, db.close()
)
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
def do_void_settlements(item, dbsession): @router.post("/void-bill/{id_}")
settlements = [] def update(
total_amount = item.amount id_: uuid.UUID,
settlements.append({"id": SettleOption.AMOUNT(), "amount": -total_amount}) u: bool, # Update table?
round_off = round(total_amount) - total_amount reason: str,
if round_off != 0: db: Session = Depends(get_db),
settlements.append({"id": SettleOption.ROUND_OFF(), "amount": -round_off}) user: UserToken = Security(get_user, scopes=["void-bill"]),
settlements.append({"id": SettleOption.VOID(), "amount": round(total_amount)}) ):
try:
item: Voucher = db.query(Voucher).filter(Voucher.id == id_).first()
for i in settlements: item.void = True
amount = i["amount"] item.reason = reason
settlement_type_id = i["id"] item.voucher_type = VoucherType.VOID
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)
for i in (i for i in item.settlements if i.settled not in [x["id"] for x in settlements]): do_update_settlements(item, [SettleSchema(id=SettleOption.VOID(), amount=round(item.amount))], db)
item.settlements.remove(i)
dbsession.delete(i) 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

View File

@ -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

View File

@ -26,7 +26,7 @@
</ng-container> </ng-container>
<ng-container matColumnDef="table-details"> <ng-container matColumnDef="table-details">
<mat-header-cell *matHeaderCellDef class="deep-purple-50 bold right-align">{{ bs.bill.table.name }} <mat-header-cell *matHeaderCellDef class="deep-purple-50 bold right-align">{{ bs.bill.table.name }}
/ {{ bs.bill.pax }} / {{ bs.bill.customer.name }}</mat-header-cell> / {{ bs.bill.pax }} / {{ bs.bill.customer?.name }}</mat-header-cell>
</ng-container> </ng-container>
<!-- Checkbox Column --> <!-- Checkbox Column -->

View File

@ -13,7 +13,7 @@ import { TablesDialogComponent } from '../tables-dialog/tables-dialog.component'
import { TableService } from '../../tables/table.service'; import { TableService } from '../../tables/table.service';
import { ToasterService } from '../../core/toaster.service'; import { ToasterService } from '../../core/toaster.service';
import { AuthService } from '../../auth/auth.service'; import { AuthService } from '../../auth/auth.service';
import { PaxComponent } from "../pax/pax.component"; import { PaxComponent } from '../pax/pax.component';
@Component({ @Component({
selector: 'app-bills', selector: 'app-bills',
@ -46,8 +46,8 @@ export class BillsComponent implements OnInit {
} }
getPax(): void { getPax(): void {
if (this.bs.bill.id || this.bs.bill.customer.id) { if (this.bs.bill.id || this.bs.bill.customer) {
return return;
} }
const dialogRef = this.dialog.open(PaxComponent, { const dialogRef = this.dialog.open(PaxComponent, {
// width: '750px', // width: '750px',

View File

@ -76,19 +76,24 @@ export class VoucherService {
} }
} }
receivePayment(id: string, amounts: { id: number; name: string; amount: number }[], name: string, updateTable: boolean): Observable<boolean> { receivePayment(
const options = {params: new HttpParams().set('receive-payment', '').set('u', updateTable.toString())}; id: string,
amounts: { id: number; name: string; amount: number }[],
name: string,
updateTable: boolean
): Observable<boolean> {
const options = {params: new HttpParams().set('u', updateTable.toString())};
return <Observable<boolean>>this.http.post<boolean>( return <Observable<boolean>>this.http.post<boolean>(
`${url}/${id}`, {name: name, amounts: amounts}, options `${url}/receive-payment/${id}`, {name: name, amounts: amounts}, options
).pipe( ).pipe(
catchError(this.log.handleError(serviceName, 'receivePayment')) catchError(this.log.handleError(serviceName, 'receivePayment'))
); );
} }
voidBill(id: string, reason: string, updateTable: boolean): Observable<boolean> { voidBill(id: string, reason: string, updateTable: boolean): Observable<boolean> {
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 <Observable<boolean>>this.http.post<boolean>( return <Observable<boolean>>this.http.post<boolean>(
`${url}/${id}`, {reason: reason}, options `${url}/void-bill/${id}`, {}, options
).pipe( ).pipe(
catchError(this.log.handleError(serviceName, 'voidBill')) catchError(this.log.handleError(serviceName, 'voidBill'))
); );