182 lines
7.0 KiB
Python
182 lines
7.0 KiB
Python
import asyncio
|
|
import locale
|
|
import re
|
|
import uuid
|
|
|
|
from datetime import timedelta
|
|
from decimal import Decimal
|
|
|
|
from arq import ArqRedis, create_pool
|
|
from barker.core.config import settings
|
|
from sqlalchemy import and_, or_, select
|
|
from sqlalchemy.orm import Session
|
|
|
|
from ..core.arq import settings as redis_settings
|
|
from ..models.db_setting import DbSetting
|
|
from ..models.inventory import Inventory
|
|
from ..models.printer import Printer
|
|
from ..models.product_version import ProductVersion
|
|
from ..models.section_printer import SectionPrinter
|
|
from ..models.voucher import Voucher
|
|
from ..models.voucher_type import VoucherType
|
|
from . import currency_format, format_no_decimals
|
|
|
|
|
|
def print_bill(voucher_id: uuid.UUID, db: Session):
|
|
locale.setlocale(locale.LC_MONETARY, "en_IN")
|
|
voucher: Voucher = db.execute(select(Voucher).where(Voucher.id == voucher_id)).scalar_one()
|
|
|
|
printer = db.execute(
|
|
select(Printer)
|
|
.join(SectionPrinter.printer)
|
|
.where(SectionPrinter.section_id == voucher.food_table.section_id)
|
|
.where(SectionPrinter.sale_category_id == None) # noqa: E711
|
|
).scalar_one()
|
|
|
|
items_dict: dict[tuple[uuid.UUID, bool, Decimal, set[uuid.UUID]], Inventory] = {}
|
|
tax: dict[tuple[uuid.UUID, int], tuple[str, Decimal]] = {}
|
|
for i in [i for k in voucher.kots for i in k.inventories]:
|
|
key = (
|
|
i.product_id,
|
|
i.is_happy_hour,
|
|
i.discount,
|
|
frozenset(m.modifier_id for m in i.modifiers if m.modifier.show_in_bill),
|
|
)
|
|
if key in items_dict:
|
|
items_dict[key].quantity += i.quantity
|
|
else:
|
|
items_dict[key] = Inventory(
|
|
None,
|
|
i.product_id,
|
|
i.quantity,
|
|
i.price,
|
|
i.discount,
|
|
i.is_happy_hour,
|
|
None,
|
|
i.tax_rate,
|
|
0,
|
|
i.product,
|
|
i.tax,
|
|
)
|
|
|
|
for i in [it for it in items_dict.values() if it.quantity != 0]:
|
|
get_tax_item(i.tax.id, i.tax.name, i.tax_amount, tax)
|
|
data = design_bill(voucher, list(items_dict.values()), list(tax.values()), db)
|
|
loop = asyncio.new_event_loop()
|
|
redis: ArqRedis = loop.run_until_complete(create_pool(redis_settings))
|
|
loop.run_until_complete(
|
|
redis.enqueue_job(
|
|
"sent_to_printer", data, printer.address, printer.cut_code, _queue_name=f"barker:print:{printer.name}"
|
|
)
|
|
)
|
|
loop.close()
|
|
|
|
|
|
def get_tax_item(
|
|
id_: uuid.UUID, tax_name: str, amount: Decimal, tax_dict: dict[tuple[uuid.UUID, int], tuple[str, Decimal]]
|
|
) -> None:
|
|
items = tax_name.split(";")
|
|
if len(items) == 1:
|
|
key = (id_, 0)
|
|
old_amount = tax_dict[key][1] if key in tax_dict else Decimal(0)
|
|
tax_dict[key] = tax_name, round(amount + old_amount, 2)
|
|
else:
|
|
for i, item in enumerate(it.strip() for it in items):
|
|
key = (id_, i)
|
|
old_amount = tax_dict[key][1] if key in tax_dict else Decimal(0)
|
|
match = re.match(r"(^.*)\s+\((.*?)/(.*?)\)[^(]*$", item)
|
|
if not match or len(match.groups()) != 3:
|
|
raise Exception("Error in tax as it has multiple items, but the format is wrong.")
|
|
sub_amount: Decimal = round(amount * Decimal(match.group(2)) / Decimal(match.group(3)), 2)
|
|
tax_dict[(id_, i)] = match.group(1), round(sub_amount + old_amount, 2)
|
|
|
|
|
|
def design_bill(
|
|
voucher: Voucher,
|
|
items: list[Inventory],
|
|
tax: list[tuple[str, Decimal]],
|
|
db: Session,
|
|
):
|
|
# Header
|
|
s = "\n\r" + db.execute(select(DbSetting).where(DbSetting.name == "Header")).scalar_one().data["Text"]
|
|
if voucher.voucher_type == VoucherType.REGULAR_BILL:
|
|
s += "\n\r" + "Retail Invoice".center(42)
|
|
s += "\n\r"
|
|
elif voucher.voucher_type == VoucherType.NO_CHARGE:
|
|
s += "\n\r" + "NO CHARGE - THIS IS NOT A BILL - DON'T PAY".center(42)
|
|
s += "\n\r"
|
|
elif voucher.voucher_type == VoucherType.STAFF:
|
|
s += "\n\r" + "STAFF CONSUMPTION -- THIS IS NOT A BILL".center(42)
|
|
s += "\n\r"
|
|
|
|
# Products
|
|
product_date = (
|
|
voucher.date + timedelta(minutes=settings.TIMEZONE_OFFSET_MINUTES - settings.NEW_DAY_OFFSET_MINUTES)
|
|
).date()
|
|
voucher_date = voucher.date + timedelta(minutes=settings.TIMEZONE_OFFSET_MINUTES)
|
|
s += "\n\r" + f"Bill No: {voucher.full_bill_id:>12} {voucher_date:%d-%b-%Y %H:%M}"
|
|
s += "\n\r" + "Table No.: " + voucher.food_table.name
|
|
s += "\n\r" + "-" * 42
|
|
s += "\n\r" + "Qty. Particulars Price Amount"
|
|
s += "\n\r" + "-" * 42
|
|
for item in [i for i in items if i.quantity != 0]:
|
|
product: ProductVersion = db.execute(
|
|
select(ProductVersion).where(
|
|
and_(
|
|
ProductVersion.product_id == item.product_id,
|
|
or_(
|
|
ProductVersion.valid_from == None, # noqa: E711
|
|
ProductVersion.valid_from <= product_date,
|
|
),
|
|
or_(
|
|
ProductVersion.valid_till == None, # noqa: E711
|
|
ProductVersion.valid_till >= product_date,
|
|
),
|
|
)
|
|
)
|
|
).scalar_one()
|
|
name = "H H " + product.full_name if item.is_happy_hour else product.full_name
|
|
s += (
|
|
f"\n\r"
|
|
f"{item.quantity: >5.2f} {name:<22.22} {format_no_decimals(item.price): >6}"
|
|
f" {format_no_decimals(item.price * item.quantity): >6}"
|
|
)
|
|
for m in [m for m in item.modifiers if m.modifier.show_in_bill]:
|
|
s += f"\n\r -- {m.modifier.name: <38.38}"
|
|
s += "\n\r" + "------------------------------------------"
|
|
|
|
# Totals
|
|
amount = sum(round(i.quantity * i.price, 2) for i in items)
|
|
s += f"\n\r{'Subtotal :': >32} {currency_format(amount): >9}"
|
|
|
|
amount = sum(round(i.quantity * i.price, 2) for i in items if i.is_happy_hour)
|
|
if amount != 0:
|
|
s += f"\n\r{'Happy Hour Discount :': >32} {currency_format(amount): >9}"
|
|
|
|
amount = sum(round(i.quantity * i.effective_price * i.discount, 2) for i in items)
|
|
if amount != 0:
|
|
s += f"\n\r{'Discount :': >32} {currency_format(amount): >9}"
|
|
|
|
for t in tax:
|
|
if t[1] != 0:
|
|
s += f"\n\r{t[0]: >30} : {currency_format(t[1]): >9}"
|
|
|
|
s += f"\n\r{'Total Amount :': >32} {currency_format(round(voucher.amount)): >9}"
|
|
s += "\n\r" + "-" * 42
|
|
|
|
if voucher.voucher_type != VoucherType.REGULAR_BILL:
|
|
s += "\n\r" + "THIS IS NOT A BILL - DON'T PAY".center(42)
|
|
s += "\n\r" + "-" * 42
|
|
|
|
if voucher.narration:
|
|
s += f"\n\r{voucher.narration: ^42}"
|
|
s += "\n\r" + "-" * 42
|
|
|
|
if voucher.customer and voucher.customer.print_in_bill:
|
|
s += f"\n\r{voucher.customer.name or ''}\n\r{voucher.customer.phone or ''}\n\r{voucher.customer.address or ''}"
|
|
s += "\n\r" + "-" * 42
|
|
|
|
s += "\n\r" + "Cashier : " + voucher.user.name
|
|
s += "\n\r" + db.execute(select(DbSetting).where(DbSetting.name == "Footer")).scalar_one().data["Text"]
|
|
return s
|