From 3f2a06c2cf683bb2a262bf3cfb0e223fce1e55af Mon Sep 17 00:00:00 2001 From: tanshu Date: Thu, 1 Oct 2020 11:43:43 +0530 Subject: [PATCH] Feature: Print kot and print bill are working now using celery Now also need to install RabbitMQ for clery to work --- .../versions/8c06ac60d125_initial_commit.py | 2 +- barker/printing/__init__.py | 79 +-------------- barker/printing/bill.py | 97 +++++++++++++++++++ barker/printing/kot.py | 61 ++++++++++++ barker/routers/section_printer.py | 1 + barker/routers/voucher/save.py | 7 +- barker/routers/voucher/update.py | 7 ++ barker/worker.py | 28 ++++++ bookie/package.json | 3 +- 9 files changed, 203 insertions(+), 82 deletions(-) create mode 100644 barker/printing/bill.py create mode 100644 barker/printing/kot.py create mode 100644 barker/worker.py diff --git a/alembic/versions/8c06ac60d125_initial_commit.py b/alembic/versions/8c06ac60d125_initial_commit.py index 68e2208..b4b2185 100644 --- a/alembic/versions/8c06ac60d125_initial_commit.py +++ b/alembic/versions/8c06ac60d125_initial_commit.py @@ -205,7 +205,7 @@ def upgrade(): sa.Column("menu_category_id", postgresql.UUID(), nullable=True), sa.Column("section_id", postgresql.UUID(), nullable=False), sa.Column("printer_id", postgresql.UUID(), nullable=False), - sa.Column("copies", sa.Numeric(), nullable=False), + sa.Column("copies", sa.Integer(), nullable=False), sa.ForeignKeyConstraint( ["menu_category_id"], ["menu_categories.id"], diff --git a/barker/printing/__init__.py b/barker/printing/__init__.py index cab0429..5e30ffb 100644 --- a/barker/printing/__init__.py +++ b/barker/printing/__init__.py @@ -1,78 +1,3 @@ -import re -import socket -import sys -import uuid +from barker.printing.bill import print_bill # noqa: F401 +from barker.printing.kot import print_kot # noqa: F401 -from sqlalchemy import or_ -from sqlalchemy.orm import Session - -from barker.models import SectionPrinter, Printer, Voucher - - -def sent_to_printer(data, address, cut_code): - print(data, address, cut_code) - try: - regex = re.compile(r'pdl://(?P[\w\d.-]+):(?P[\d]+)') - match = regex.match(address) - s = socket.socket() - s.connect((match.group("host"), int(match.group("port")))) - s.send(bytearray(data + cut_code, "ascii")) - except LookupError as e: - print("Lookup error:", e) - except: - print("Unexpected error:", sys.exc_info()[0]) - finally: - s.close() - - -def design_kot(voucher, kot, items, copy_number): - s = ( - f"KOT / BOT".center(42) - + "\n\r" + f"Copy No. {copy_number}:".center(42) - + "\n\r" + "".ljust(42, "-") - + "\n\r" + f"KOT ID : K-{voucher.kot_id:>5}/S-{kot.code:>5} {kot.date:%d-%b-%Y %H:%M}" - + "\n\r" + f"Table No.: {voucher.food_table.name}" - + "\n\r" + "".ljust(42, "-") - + "\n\r" + " Qty. x Name " - + "\n\r" + "".ljust(42, "-") - ) - for item in items: - name = ( - "H H " + item.product.full_name - if item.is_happy_hour - else item.product.full_name - ) - s += "\n\r" + f"{item.quantity:6.2} x {name:>33}" - for m in item.modifiers: - s += "\n\r" + f" --- {m.name:>32}" - s += "\n\r" + "".ljust(42, "-") - return s - - -def print_kot(voucher_id: uuid.UUID, db: Session): - voucher: Voucher = db.query(Voucher).filter(Voucher.id == voucher_id).first() - my_hash = {} - kot = voucher.kots[-1] - for item in kot.inventories: - printer, copies = ( - db.query(Printer, SectionPrinter.copies) - .join(SectionPrinter.printer) - .filter(SectionPrinter.section_id == voucher.food_table.section_id) - .filter( - or_( - SectionPrinter.menu_category_id == item.product.menu_category_id, - SectionPrinter.menu_category_id == None, - ) - ).order_by(SectionPrinter.menu_category_id) - .first() - ) - key = (printer.id, copies) - if key not in my_hash: - my_hash[key] = (printer, []) - my_hash[key][1].append(item) - for key, value in my_hash.items(): - printer_id, copies = key - printer, items = value - for c in range(int(copies)): - data = design_kot(voucher, kot, items, c) - sent_to_printer(data, printer.address, printer.cut_code) diff --git a/barker/printing/bill.py b/barker/printing/bill.py new file mode 100644 index 0000000..37c2767 --- /dev/null +++ b/barker/printing/bill.py @@ -0,0 +1,97 @@ +import uuid +from decimal import Decimal +from typing import List, Tuple + +from sqlalchemy.orm import Session + +from barker.models import SectionPrinter, Printer, Voucher, Inventory, DbSetting, VoucherType +from barker.worker import sent_to_printer + + +def print_bill(voucher_id: uuid.UUID, db: Session): + 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) + .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 + if i.tax_id in tax: + tax[i.tax_id] = (i.tax.name, tax[i.tax_id][1] + i.tax_amount) + else: + tax[i.tax_id] = (i.tax.name, i.tax_amount) + data = design_bill(voucher, items_dict.values(), tax.values(), db) + sent_to_printer.delay(data, printer.address, printer.cut_code) + + +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 + 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]: + name = "H H " + item.product.full_name if item.is_happy_hour else item.product.full_name + s += "\n\r" + f"{item.quantity: >5.2f} {name:<22.22} {item.price: >6.2f} {item.price * item.quantity: >6.2f}" + 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} {amount: >9.2f}" + + 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} {amount: >9.2f}" + + amount = sum(i.quantity * i.effective_price * i.discount for i in items) + if amount != 0: + s += f"\n\r{'Discount :': >32} {amount: >9.2f}" + + for t in tax: + if t[1] != 0: + s += f"\n\r{t[0]: >30} : {t[1]: >9.2f}" + + s += f"\n\r{'Total Amount :',32} {round(voucher.amount): >9.2f}" + 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 diff --git a/barker/printing/kot.py b/barker/printing/kot.py new file mode 100644 index 0000000..754f6ef --- /dev/null +++ b/barker/printing/kot.py @@ -0,0 +1,61 @@ +import uuid +from typing import List + +from sqlalchemy import or_ +from sqlalchemy.orm import Session + +from barker.models import SectionPrinter, Printer, Voucher, Kot, Inventory +from barker.worker import sent_to_printer + + +def design_kot(voucher: Voucher, kot: Kot, items: List[Inventory], copy_number: int) -> str: + s = ( + f"KOT / BOT".center(42) + + "\n\r" + f"Copy No. {copy_number}:".center(42) + + "\n\r" + "".ljust(42, "-") + + "\n\r" + f"KOT ID : K-{voucher.kot_id:>5}/S-{kot.code:>5} {kot.date:%d-%b-%Y %H:%M}" + + "\n\r" + f"Table No.: {voucher.food_table.name}" + + "\n\r" + "".ljust(42, "-") + + "\n\r" + " Qty. x Name " + + "\n\r" + "".ljust(42, "-") + ) + for item in items: + name = ( + "H H " + item.product.full_name + if item.is_happy_hour + else item.product.full_name + ) + s += "\n\r" + f"{item.quantity:6.2} x {name:>33}" + for m in item.modifiers: + s += "\n\r" + f" --- {m.name:>32}" + s += "\n\r" + "".ljust(42, "-") + return s + + +def print_kot(voucher_id: uuid.UUID, db: Session): + voucher: Voucher = db.query(Voucher).filter(Voucher.id == voucher_id).first() + my_hash = {} + kot: Kot = voucher.kots[-1] + for item in kot.inventories: + printer, copies = ( + db.query(Printer, SectionPrinter.copies) + .join(SectionPrinter.printer) + .filter(SectionPrinter.section_id == voucher.food_table.section_id) + .filter( + or_( + SectionPrinter.menu_category_id == item.product.menu_category_id, + SectionPrinter.menu_category_id == None, + ) + ).order_by(SectionPrinter.menu_category_id) + .first() + ) + key = (printer.id, copies) + if key not in my_hash: + my_hash[key] = (printer, []) + my_hash[key][1].append(item) + for key, value in my_hash.items(): + printer_id, copies = key + printer, items = value + for c in range(int(copies)): + data = design_kot(voucher, kot, items, c) + sent_to_printer.delay(data, printer.address, printer.cut_code) diff --git a/barker/routers/section_printer.py b/barker/routers/section_printer.py index 01d45cf..f47d719 100644 --- a/barker/routers/section_printer.py +++ b/barker/routers/section_printer.py @@ -59,6 +59,7 @@ def save( SectionPrinter.__table__.delete( and_( SectionPrinter.section_id == id_, + SectionPrinter.menu_category_id != None, ~SectionPrinter.menu_category_id.in_([x for x in current if x is not None]), ) ) diff --git a/barker/routers/voucher/save.py b/barker/routers/voucher/save.py index 25ac245..b86dbbe 100644 --- a/barker/routers/voucher/save.py +++ b/barker/routers/voucher/save.py @@ -20,7 +20,7 @@ from ...routers.voucher import ( check_permissions, get_guest_book, ) -from barker.printing import print_kot +from barker.printing import print_kot, print_bill router = APIRouter() @@ -51,8 +51,9 @@ def save( if update_table: do_update_table(item, guest_book, db) db.commit() - # print_kot(item.id, db) - + print_kot(item.id, db) + if item.voucher_type != VoucherType.KOT: + print_bill(item.id, db) except SQLAlchemyError as e: db.rollback() raise HTTPException( diff --git a/barker/routers/voucher/update.py b/barker/routers/voucher/update.py index 5ef7873..e80f352 100644 --- a/barker/routers/voucher/update.py +++ b/barker/routers/voucher/update.py @@ -7,6 +7,7 @@ from sqlalchemy import func from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.orm import Session +from ...printing import print_kot, print_bill from ...schemas.auth import UserToken import barker.schemas.voucher as schemas from ...core.security import get_current_active_user as get_user @@ -48,6 +49,7 @@ def update( update_table = u voucher_type = VoucherType[p] guest_book = get_guest_book(g, db) + need_to_print_kot = False item: Voucher = db.query(Voucher).filter(Voucher.id == id_).first() @@ -76,6 +78,7 @@ def update( round(inv.discount, 5) for ko in data.kots for inv in ko.inventories if inv.id_ == i.id ) for k in (k for k in data.kots if k.id_ is None and len(k.inventories) > 0): + need_to_print_kot = True code = db.query(func.coalesce(func.max(Kot.code), 0) + 1).scalar() kot = Kot(item.id, code, item.food_table_id, now, item.user_id) item.kots.append(kot) @@ -104,6 +107,10 @@ def update( if update_table: do_update_table(item, guest_book, db) db.commit() + if need_to_print_kot: + print_kot(item.id, db) + if item.voucher_type != VoucherType.KOT: + print_bill(item.id, db) except SQLAlchemyError as e: db.rollback() raise HTTPException( diff --git a/barker/worker.py b/barker/worker.py new file mode 100644 index 0000000..82285a1 --- /dev/null +++ b/barker/worker.py @@ -0,0 +1,28 @@ +import re +import socket +import sys +from celery import Celery +from celery.utils.log import get_task_logger + +# Create the celery app and get the logger +celery_app = Celery('tasks', broker='pyamqp://guest@localhost//') +logger = get_task_logger(__name__) + + +@celery_app.task +def sent_to_printer(data: str, address: str, cut_code: str): + print(data, address, cut_code) + # try: + # regex = re.compile(r'pdl://(?P[\w\d.-]+):(?P[\d]+)') + # match = regex.match(address) + # s = socket.socket() + # s.connect((match.group("host"), int(match.group("port")))) + # s.send(bytearray(data + cut_code, "ascii")) + # except LookupError as e: + # print("Lookup error:", e) + # except: + # print("Unexpected error:", sys.exc_info()[0]) + # finally: + # s.close() + +# celery --app=barker.worker.celery_app worker -c 2 --loglevel=INFO \ No newline at end of file diff --git a/bookie/package.json b/bookie/package.json index 3835ecf..058102b 100644 --- a/bookie/package.json +++ b/bookie/package.json @@ -7,7 +7,8 @@ "build": "ng build", "test": "ng test", "lint": "ng lint", - "e2e": "ng e2e" + "e2e": "ng e2e", + "postinstall": "ngcc" }, "private": true, "dependencies": {