From 1a08066c2d5f0004a62147a76384670017c318b7 Mon Sep 17 00:00:00 2001 From: Amritanshu Date: Fri, 17 Mar 2023 08:27:30 +0530 Subject: [PATCH] Feature: Cancelled bills are now separate from void bills. Void bills are automatically generated when printed bills are changed. Manually cancelled bills are called cancelled bills. --- .../versions/1d95eb10ea20_cancel_bill.py | 127 ++++++++++++++++++ barker/barker/main.py | 4 +- barker/barker/models/regime.py | 2 +- barker/barker/models/settle_option.py | 4 + barker/barker/models/voucher_type.py | 1 + barker/barker/printing/cashier_report.py | 2 +- .../barker/routers/reports/cashier_report.py | 9 +- .../routers/voucher/{void.py => cancel.py} | 8 +- .../product-sale-report-item.ts | 4 +- .../product-sale-report.component.html | 8 +- .../product-sale-report.component.ts | 2 +- bookie/src/app/sales/bill.service.ts | 4 +- bookie/src/app/sales/bills/voucher.service.ts | 6 +- .../app/sales/home/sales-home.component.html | 8 +- .../app/sales/home/sales-home.component.ts | 20 +-- 15 files changed, 170 insertions(+), 39 deletions(-) create mode 100644 barker/alembic/versions/1d95eb10ea20_cancel_bill.py rename barker/barker/routers/voucher/{void.py => cancel.py} (90%) diff --git a/barker/alembic/versions/1d95eb10ea20_cancel_bill.py b/barker/alembic/versions/1d95eb10ea20_cancel_bill.py new file mode 100644 index 0000000..dad4b2b --- /dev/null +++ b/barker/alembic/versions/1d95eb10ea20_cancel_bill.py @@ -0,0 +1,127 @@ +"""Cancel Bill + +Revision ID: 1d95eb10ea20 +Revises: 5967e6603c3e +Create Date: 2023-03-17 01:04:25.809115 + +""" +import sqlalchemy as sa + +from alembic import op +from sqlalchemy import column, table +from sqlalchemy.dialects import postgresql +from sqlalchemy.dialects.postgresql import ENUM + + +# revision identifiers, used by Alembic. +revision = "1d95eb10ea20" +down_revision = "5967e6603c3e" +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + # op.execute("ALTER TYPE voucher_type ADD VALUE 'CANCEL';") + reporting_level = sa.Enum("Skip", "Aggregate", "Detailed", name="reportinglevel") + reporting_level.create(op.get_bind()) + voucher_type = sa.Enum("KOT", "REGULAR_BILL", "NO_CHARGE", "STAFF", "VOID", "CANCEL", name="vouchertype") + voucher_type.create(op.get_bind()) + + op.execute("ALTER TABLE settle_options ALTER COLUMN voucher_type DROP DEFAULT;") + op.execute("ALTER TABLE settle_options ALTER COLUMN reporting_level DROP DEFAULT;") + op.execute( + "ALTER TABLE settle_options ALTER COLUMN voucher_type TYPE vouchertype USING voucher_type::text::vouchertype;" + ) + op.execute( + "ALTER TABLE settle_options " + + "ALTER COLUMN reporting_level " + + "TYPE reportinglevel USING reporting_level::text::reportinglevel;" + ) + op.execute("ALTER TABLE vouchers ALTER COLUMN voucher_type DROP DEFAULT;") + op.execute("ALTER TABLE vouchers ALTER COLUMN voucher_type TYPE vouchertype USING voucher_type::text::vouchertype;") + + op.execute("DROP TYPE voucher_type;") + op.execute("DROP TYPE reporting_level;") + op.create_index(op.f("ix_vouchers_date"), "vouchers", ["date"], unique=False) + + settle_options() + regimes() + # ### end Alembic commands ### + + +def settle_options(): + reporting_level = ENUM("Skip", "Aggregate", "Detailed", name="reporting_level", create_type=False) + voucher_type = ENUM( + "KOT", "REGULAR_BILL", "NO_CHARGE", "STAFF", "VOID", "CANCEL", name="voucher_type", create_type=False + ) + so = table( + "settle_options", + column("id", sa.Integer()), + column("name", sa.Unicode(length=255)), + column("has_reason", sa.Boolean()), + column("reporting_level", reporting_level), + column("voucher_type", voucher_type), + column("is_fixture", sa.Boolean()), + ) + + op.execute( + so.insert().values( + id=12, + name="Cancelled", + has_reason=True, + reporting_level="Detailed", + voucher_type="CANCEL", + is_fixture=True, + ) + ) + + +def regimes(): + re = sa.table( + "regimes", + sa.column("id", sa.Integer()), + sa.column("name", sa.Unicode(length=255)), + sa.column("header", sa.Unicode(length=255)), + sa.column("prefix", sa.Unicode(length=255)), + sa.column("is_fixture", sa.Boolean()), + ) + op.execute( + re.insert().values( + id=12, + name="Cancelled", + header="", + prefix="C", + is_fixture=True, + ) + ) + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_index(op.f("ix_vouchers_date"), table_name="vouchers") + op.alter_column( + "vouchers", + "voucher_type", + existing_type=sa.Enum("KOT", "REGULAR_BILL", "NO_CHARGE", "STAFF", "VOID", "CANCEL", name="vouchertype"), + type_=postgresql.ENUM("KOT", "REGULAR_BILL", "NO_CHARGE", "STAFF", "VOID", name="voucher_type"), + existing_nullable=False, + existing_server_default=sa.text("'KOT'::voucher_type"), + ) + op.alter_column( + "settle_options", + "reporting_level", + existing_type=sa.Enum("Skip", "Aggregate", "Detailed", name="reportinglevel"), + type_=postgresql.ENUM("Skip", "Aggregate", "Detailed", name="reporting_level"), + existing_nullable=False, + existing_server_default=sa.text("'Skip'::reporting_level"), + ) + op.alter_column( + "settle_options", + "voucher_type", + existing_type=sa.Enum("KOT", "REGULAR_BILL", "NO_CHARGE", "STAFF", "VOID", "CANCEL", name="vouchertype"), + type_=postgresql.ENUM("KOT", "REGULAR_BILL", "NO_CHARGE", "STAFF", "VOID", name="voucher_type"), + existing_nullable=False, + existing_server_default=sa.text("'KOT'::voucher_type"), + ) + # ### end Alembic commands ### diff --git a/barker/barker/main.py b/barker/barker/main.py index 49683cb..a4e1b90 100644 --- a/barker/barker/main.py +++ b/barker/barker/main.py @@ -41,6 +41,7 @@ from .routers.reports import ( tax_report, ) from .routers.voucher import ( + cancel, change, merge_move, receive_payment, @@ -48,7 +49,6 @@ from .routers.voucher import ( show, split, update, - void, ) @@ -109,7 +109,7 @@ 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(cancel.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"]) diff --git a/barker/barker/models/regime.py b/barker/barker/models/regime.py index f6853fc..5ecac1c 100644 --- a/barker/barker/models/regime.py +++ b/barker/barker/models/regime.py @@ -17,7 +17,7 @@ class Regime: id: Mapped[int] = mapped_column("id", Integer, primary_key=True) name: Mapped[str] = mapped_column("name", Unicode(255), nullable=False, unique=True) - header: Mapped[str] = mapped_column("header", Unicode(255), nullable=False, unique=True) + header: Mapped[str] = mapped_column("header", Unicode(255), nullable=False) prefix: Mapped[str] = mapped_column("prefix", Unicode(255), nullable=False, unique=True) is_fixture: Mapped[bool] = mapped_column("is_fixture", Boolean, nullable=False) diff --git a/barker/barker/models/settle_option.py b/barker/barker/models/settle_option.py index 0b4f409..1f763c5 100644 --- a/barker/barker/models/settle_option.py +++ b/barker/barker/models/settle_option.py @@ -47,3 +47,7 @@ class SettleOption: @classmethod def VOID(cls): return 9 + + @classmethod + def CANCEL(cls): + return 12 diff --git a/barker/barker/models/voucher_type.py b/barker/barker/models/voucher_type.py index 279c29c..1d5de9b 100644 --- a/barker/barker/models/voucher_type.py +++ b/barker/barker/models/voucher_type.py @@ -7,3 +7,4 @@ class VoucherType(enum.IntEnum): NO_CHARGE = 2 STAFF = 4 VOID = 8 + CANCEL = 12 diff --git a/barker/barker/printing/cashier_report.py b/barker/barker/printing/cashier_report.py index 498d179..e5e2d51 100644 --- a/barker/barker/printing/cashier_report.py +++ b/barker/barker/printing/cashier_report.py @@ -45,7 +45,7 @@ def design_cashier_report(report: CashierReport): s += "\n\r" f"{report.start_date:%d-%b-%Y} To {report.finish_date:%d-%b-%Y}".center(42) s += "\n\r" + "-" * 42 for item in report.amounts: - s += f"\n\r{item.name} : {currency_format(item.amount): >26}" + s += f"\n\r{item.name: <21} : {currency_format(item.amount): >16}" s += "\n\r" + "=" * 42 s += f"\n\rActive Cashiers : {report.cashiers}" for so, it in report.info.items(): diff --git a/barker/barker/routers/reports/cashier_report.py b/barker/barker/routers/reports/cashier_report.py index 63a0ce3..81072e0 100644 --- a/barker/barker/routers/reports/cashier_report.py +++ b/barker/barker/routers/reports/cashier_report.py @@ -105,14 +105,13 @@ def get_id(id_: uuid.UUID, start_date: date, finish_date: date, user: UserLink, info: dict[str, list[InfoItem]] = {} amounts = {} for item in vouchers: - for so in ( - so for so in item.settlements if so.settle_option.reporting_level >= ReportingLevel.Aggregate - ): # Only Roundoff and Amount is skipped + for so in (so for so in item.settlements if so.settle_option.reporting_level >= ReportingLevel.Aggregate): if so.settle_option.name not in info: - info[so.settle_option.name] = [] + if so.settle_option.reporting_level == ReportingLevel.Detailed: + info[so.settle_option.name] = [] amounts[so.settle_option.name] = Decimal(0) amounts[so.settle_option.name] += so.amount - if so.settle_option.reporting_level == ReportingLevel.Detailed: # Only additionally Cash is skipped + if so.settle_option.reporting_level == ReportingLevel.Detailed: info[so.settle_option.name].append( InfoItem( date=item.date, diff --git a/barker/barker/routers/voucher/void.py b/barker/barker/routers/voucher/cancel.py similarity index 90% rename from barker/barker/routers/voucher/void.py rename to barker/barker/routers/voucher/cancel.py index 7a2ce77..82edf16 100644 --- a/barker/barker/routers/voucher/void.py +++ b/barker/barker/routers/voucher/cancel.py @@ -18,8 +18,8 @@ from . import do_update_bill_numbers, do_update_settlements router = APIRouter() -@router.post("/void-bill/{id_}") -def void( +@router.post("/cancel-bill/{id_}") +def cancel( id_: uuid.UUID, u: bool, # Update table? reason: str, @@ -29,9 +29,9 @@ def void( with SessionFuture() as db: item: Voucher = db.execute(select(Voucher).where(Voucher.id == id_)).scalar_one() item.reason = f"{reason}" - item.voucher_type = VoucherType.VOID + item.voucher_type = VoucherType.CANCEL do_update_bill_numbers(item, db) - do_update_settlements(item, [SettleSchema(id=SettleOption.VOID(), amount=round(item.amount))], db) + do_update_settlements(item, [SettleSchema(id=SettleOption.CANCEL(), amount=round(item.amount))], db) if u: # Update table db.execute(delete(Overview).where(Overview.voucher_id == item.id)) diff --git a/bookie/src/app/product-sale-report/product-sale-report-item.ts b/bookie/src/app/product-sale-report/product-sale-report-item.ts index 7da0235..cd8e3cc 100644 --- a/bookie/src/app/product-sale-report/product-sale-report-item.ts +++ b/bookie/src/app/product-sale-report/product-sale-report-item.ts @@ -4,7 +4,7 @@ export class ProductSaleReportItem { regularBill: number; noCharge: number; staff: number; - void: number; + cancel: number; public constructor(init?: Partial) { this.name = ''; @@ -12,7 +12,7 @@ export class ProductSaleReportItem { this.regularBill = 0; this.noCharge = 0; this.staff = 0; - this.void = 0; + this.cancel = 0; Object.assign(this, init); } } diff --git a/bookie/src/app/product-sale-report/product-sale-report.component.html b/bookie/src/app/product-sale-report/product-sale-report.component.html index 1752363..36cb1fb 100644 --- a/bookie/src/app/product-sale-report/product-sale-report.component.html +++ b/bookie/src/app/product-sale-report/product-sale-report.component.html @@ -72,10 +72,10 @@ {{ row.staff | number : '1.2-2' }} - - - Void - {{ row.void | number : '1.2-2' }} + + + Cancelled + {{ row.cancel | number : '1.2-2' }} diff --git a/bookie/src/app/product-sale-report/product-sale-report.component.ts b/bookie/src/app/product-sale-report/product-sale-report.component.ts index bd897dd..8d7ac63 100644 --- a/bookie/src/app/product-sale-report/product-sale-report.component.ts +++ b/bookie/src/app/product-sale-report/product-sale-report.component.ts @@ -24,7 +24,7 @@ export class ProductSaleReportComponent implements OnInit { }>; /** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */ - displayedColumns = ['name', 'unbilled', 'sale', 'noCharge', 'staff', 'void']; + displayedColumns = ['name', 'unbilled', 'sale', 'noCharge', 'staff', 'cancel']; constructor( private route: ActivatedRoute, diff --git a/bookie/src/app/sales/bill.service.ts b/bookie/src/app/sales/bill.service.ts index 6c8ae87..c9bd788 100644 --- a/bookie/src/app/sales/bill.service.ts +++ b/bookie/src/app/sales/bill.service.ts @@ -271,8 +271,8 @@ export class BillService { return this.ser.mergeKotWithOldBill(this.bill.id as string, kotId, table); } - voidBill(reason: string): Observable { - return this.ser.voidBill(this.bill.id as string, reason, this.updateTable); + cancelBill(reason: string): Observable { + return this.ser.cancelBill(this.bill.id as string, reason, this.updateTable); } updateAmounts() { diff --git a/bookie/src/app/sales/bills/voucher.service.ts b/bookie/src/app/sales/bills/voucher.service.ts index 4e9333d..f609dc8 100644 --- a/bookie/src/app/sales/bills/voucher.service.ts +++ b/bookie/src/app/sales/bills/voucher.service.ts @@ -123,13 +123,13 @@ export class VoucherService { .pipe(catchError(this.log.handleError(serviceName, 'receivePayment'))) as Observable; } - voidBill(id: string, reason: string, updateTable: boolean): Observable { + cancelBill(id: string, reason: string, updateTable: boolean): Observable { const options = { params: new HttpParams().set('reason', reason).set('u', updateTable.toString()), }; return this.http - .post(`${url}/void-bill/${id}`, {}, options) - .pipe(catchError(this.log.handleError(serviceName, 'voidBill'))) as Observable; + .post(`${url}/cancel-bill/${id}`, {}, options) + .pipe(catchError(this.log.handleError(serviceName, 'cancelBill'))) as Observable; } moveTable(id: string, table: Table): Observable { diff --git a/bookie/src/app/sales/home/sales-home.component.html b/bookie/src/app/sales/home/sales-home.component.html index a303993..f26d2f4 100644 --- a/bookie/src/app/sales/home/sales-home.component.html +++ b/bookie/src/app/sales/home/sales-home.component.html @@ -58,11 +58,11 @@ -

Void Bill

+

Cancel Bill

(x ? table : x))); } - confirmVoidDialog(reason: string): Observable { + confirmCancelDialog(reason: string): Observable { return this.dialog .open(ConfirmDialogComponent, { width: '250px', - data: { title: 'Void Bill?', content: 'Are you sure?' }, + data: { title: 'Cancel Bill?', content: 'Are you sure?' }, }) .afterClosed() .pipe(map((x: boolean) => (x ? reason : x))); @@ -267,7 +267,7 @@ export class SalesHomeComponent { ); } - voidBillAllowed(): boolean { + cancelBillAllowed(): boolean { if (!this.auth.allowed('void-bill')) { return false; } @@ -277,15 +277,15 @@ export class SalesHomeComponent { return true; } - voidBill() { - if (!this.voidBillAllowed()) { + cancelBill() { + if (!this.cancelBillAllowed()) { return; } this.dialog .open(ReasonComponent, { // width: '750px' data: { - title: 'Void Reason', + title: 'Cancel Reason', reasons: [ 'Discount', 'Printing fault', @@ -301,15 +301,15 @@ export class SalesHomeComponent { .pipe( switchMap((x: boolean | string) => { if (x) { - return this.confirmVoidDialog(x as string); + return this.confirmCancelDialog(x as string); } - throw new Error('Please choose a reason to void the bill'); + throw new Error('Please choose a reason to cancel the bill'); }), switchMap((x: boolean | string) => { if (x) { - return this.bs.voidBill(x as string); + return this.bs.cancelBill(x as string); } - throw new Error('You chose not to void the bill'); + throw new Error('You chose not to cancel the bill'); }), ) .subscribe(