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.
This commit is contained in:
parent
a62018ad1f
commit
1a08066c2d
127
barker/alembic/versions/1d95eb10ea20_cancel_bill.py
Normal file
127
barker/alembic/versions/1d95eb10ea20_cancel_bill.py
Normal file
@ -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 ###
|
@ -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"])
|
||||
|
@ -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)
|
||||
|
||||
|
@ -47,3 +47,7 @@ class SettleOption:
|
||||
@classmethod
|
||||
def VOID(cls):
|
||||
return 9
|
||||
|
||||
@classmethod
|
||||
def CANCEL(cls):
|
||||
return 12
|
||||
|
@ -7,3 +7,4 @@ class VoucherType(enum.IntEnum):
|
||||
NO_CHARGE = 2
|
||||
STAFF = 4
|
||||
VOID = 8
|
||||
CANCEL = 12
|
||||
|
@ -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():
|
||||
|
@ -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,
|
||||
|
@ -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))
|
@ -4,7 +4,7 @@ export class ProductSaleReportItem {
|
||||
regularBill: number;
|
||||
noCharge: number;
|
||||
staff: number;
|
||||
void: number;
|
||||
cancel: number;
|
||||
|
||||
public constructor(init?: Partial<ProductSaleReportItem>) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -72,10 +72,10 @@
|
||||
<mat-cell *matCellDef="let row" class="right">{{ row.staff | number : '1.2-2' }}</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Void Column -->
|
||||
<ng-container matColumnDef="void">
|
||||
<mat-header-cell *matHeaderCellDef class="right">Void</mat-header-cell>
|
||||
<mat-cell *matCellDef="let row" class="right">{{ row.void | number : '1.2-2' }}</mat-cell>
|
||||
<!-- Cancelled Column -->
|
||||
<ng-container matColumnDef="cancel">
|
||||
<mat-header-cell *matHeaderCellDef class="right">Cancelled</mat-header-cell>
|
||||
<mat-cell *matCellDef="let row" class="right">{{ row.cancel | number : '1.2-2' }}</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
|
||||
|
@ -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,
|
||||
|
@ -271,8 +271,8 @@ export class BillService {
|
||||
return this.ser.mergeKotWithOldBill(this.bill.id as string, kotId, table);
|
||||
}
|
||||
|
||||
voidBill(reason: string): Observable<boolean> {
|
||||
return this.ser.voidBill(this.bill.id as string, reason, this.updateTable);
|
||||
cancelBill(reason: string): Observable<boolean> {
|
||||
return this.ser.cancelBill(this.bill.id as string, reason, this.updateTable);
|
||||
}
|
||||
|
||||
updateAmounts() {
|
||||
|
@ -123,13 +123,13 @@ export class VoucherService {
|
||||
.pipe(catchError(this.log.handleError(serviceName, 'receivePayment'))) as Observable<boolean>;
|
||||
}
|
||||
|
||||
voidBill(id: string, reason: string, updateTable: boolean): Observable<boolean> {
|
||||
cancelBill(id: string, reason: string, updateTable: boolean): Observable<boolean> {
|
||||
const options = {
|
||||
params: new HttpParams().set('reason', reason).set('u', updateTable.toString()),
|
||||
};
|
||||
return this.http
|
||||
.post<boolean>(`${url}/void-bill/${id}`, {}, options)
|
||||
.pipe(catchError(this.log.handleError(serviceName, 'voidBill'))) as Observable<boolean>;
|
||||
.post<boolean>(`${url}/cancel-bill/${id}`, {}, options)
|
||||
.pipe(catchError(this.log.handleError(serviceName, 'cancelBill'))) as Observable<boolean>;
|
||||
}
|
||||
|
||||
moveTable(id: string, table: Table): Observable<boolean> {
|
||||
|
@ -58,11 +58,11 @@
|
||||
<mat-card
|
||||
class="flex flex-col square-button mr-5, mb-5"
|
||||
matRipple
|
||||
(click)="voidBill()"
|
||||
[class.disabled]="!voidBillAllowed()"
|
||||
[class.face]="voidBillAllowed()"
|
||||
(click)="cancelBill()"
|
||||
[class.disabled]="!cancelBillAllowed()"
|
||||
[class.face]="cancelBillAllowed()"
|
||||
>
|
||||
<h3 class="item-name">Void Bill</h3>
|
||||
<h3 class="item-name">Cancel Bill</h3>
|
||||
</mat-card>
|
||||
<mat-card
|
||||
class="flex flex-col square-button mr-5, mb-5"
|
||||
|
@ -112,11 +112,11 @@ export class SalesHomeComponent {
|
||||
.pipe(map((x: boolean) => (x ? table : x)));
|
||||
}
|
||||
|
||||
confirmVoidDialog(reason: string): Observable<string | boolean> {
|
||||
confirmCancelDialog(reason: string): Observable<string | boolean> {
|
||||
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(
|
||||
|
Loading…
x
Reference in New Issue
Block a user