import asyncio import locale import re import uuid from datetime import timedelta from decimal import Decimal from typing import List, Tuple from arq import ArqRedis, create_pool from barker.core.config import settings from sqlalchemy import and_, or_ 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.query(Voucher).filter(Voucher.id == voucher_id).first() printer = ( db.query(Printer) .join(SectionPrinter.printer) .filter(SectionPrinter.section_id == voucher.food_table.section_id) .filter(SectionPrinter.menu_category_id == None) # noqa: E711 .first() ) items_dict = {} tax = {} for i in [i for k in voucher.kots for i in k.inventories]: key = ( i.product_id, i.is_happy_hour, tuple(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] = i get_tax_item(i.tax.id, i.tax.name, i.tax_amount, tax) data = design_bill(voucher, items_dict.values(), tax.values(), db) redis: ArqRedis = asyncio.run(create_pool(redis_settings)) asyncio.run( redis.enqueue_job( "sent_to_printer", data, printer.address, "\x1dV\x41\x03", _queue_name=f"barker:print:{printer.name}" ) ) def get_tax_item(id_: uuid.UUID, tax_name: str, amount: Decimal, tax_dict: {}) -> None: items = tax_name.split(";") if len(items) == 1: key = (id_, 0) old_amount = tax_dict[key][1] if key in tax_dict else 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 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 = 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[Tuple[Inventory, Decimal]], tax: List[Tuple[str, Decimal]], db: Session, ): # Header s = "\n\r" + db.query(DbSetting).filter(DbSetting.name == "Header").first().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 date = voucher.date + timedelta(minutes=settings.NEW_DAY_OFFSET_MINUTES) s += "\n\r" + f"Bill No: {voucher.full_bill_id:>12} {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.query(ProductVersion) .filter( and_( ProductVersion.product_id == item.product_id, or_( ProductVersion.valid_from == None, # noqa: E711 ProductVersion.valid_from <= (voucher.date - timedelta(minutes=settings.NEW_DAY_OFFSET_MINUTES)).date(), ), or_( ProductVersion.valid_till == None, # noqa: E711 ProductVersion.valid_till >= (voucher.date - timedelta(minutes=settings.NEW_DAY_OFFSET_MINUTES)).date(), ), ) ) .first() ) 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(i.quantity * i.price for i in items) s += f"\n\r{'Subtotal :': >32} {currency_format(amount): >9}" amount = sum(i.quantity * i.price 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(i.quantity * i.effective_price * i.discount 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: s += f"\n\r{voucher.customer.name}\n\r{voucher.customer.phone}\n\r{voucher.customer.address}" s += "\n\r" + "-" * 42 s += "\n\r" + "Cashier : " + voucher.user.name s += "\n\r" + db.query(DbSetting).filter(DbSetting.name == "Footer").first().data["Text"] return s