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:
2023-03-17 08:27:30 +05:30
parent a62018ad1f
commit 1a08066c2d
15 changed files with 170 additions and 39 deletions

View 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 ###

View File

@ -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"])

View File

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

View File

@ -47,3 +47,7 @@ class SettleOption:
@classmethod
def VOID(cls):
return 9
@classmethod
def CANCEL(cls):
return 12

View File

@ -7,3 +7,4 @@ class VoucherType(enum.IntEnum):
NO_CHARGE = 2
STAFF = 4
VOID = 8
CANCEL = 12

View File

@ -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():

View File

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

View File

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