from trytond.pool import Pool, PoolMeta from trytond.model import ModelView, fields from trytond.transaction import Transaction from trytond.modules.currency.fields import Monetary from decimal import Decimal import requests import json class Sale(metaclass=PoolMeta): "Sale Fast Food" __name__ = 'sale.sale' pizza_number = fields.Integer("Number pizza") credito = fields.Boolean("Credito") total_discount = fields.Function( Monetary( "Total Discount", digits='currency', currency='currency'), 'get_amount') total_discount_cache = fields.Numeric( "Total Discount cache", digits='currency') total_tip = fields.Function( Monetary( "Total Tip", digits='currency', currency='currency'), 'get_amount') total_tip_cache = fields.Numeric( "Total Tip cache", digits="currency") @classmethod def __setup__(cls): super(Sale, cls).__setup__() cls._buttons.update({ 'add_pizza': {}, 'kitchen': {}, 'bar': {}, 'print_bill': {}, 'impreso': {}, }) @classmethod def default_pizza_number(cls): return 0 @fields.depends('lines', 'currency', methods=['get_tax_amount']) def on_change_lines(self): self.untaxed_amount = Decimal('0.0') self.tax_amount = Decimal('0.0') self.total_amount = Decimal('0.0') self.total_discount = Decimal('0.0') self.total_tip = Decimal('0.0') if self.lines: for line in self.lines: self.untaxed_amount += getattr(line, 'amount', None) or 0 self.tax_amount = self.get_tax_amount() if self.currency: self.untaxed_amount = self.currency.round(self.untaxed_amount) self.tax_amount = self.currency.round(self.tax_amount) self.total_amount = self.untaxed_amount + self.tax_amount if self.currency: self.total_tip = self.currency.round(self.total_tip) self.total_discount = self.currency.round(self.total_discount) self.total_amount = self.currency.round(self.total_amount) @classmethod def store_cache(cls, sales): for sale in sales: sale.untaxed_amount_cache = sale.untaxed_amount sale.tax_amount_cache = sale.tax_amount sale.total_amount_cache = sale.total_amount sale.total_discount_cache = sale.total_discount sale.total_tip_cache = sale.total_tip cls.save(sales) @classmethod def get_amount(cls, sales, names): untaxed_amount = {} tax_amount = {} total_amount = {} total_discount = {} total_tip = {} if {'tax_amount', 'total_amount'} & set(names): compute_taxes = True else: compute_taxes = False # Sort cached first and re-instanciate to optimize cache management sales = sorted(sales, key=lambda s: s.state in cls._states_cached, reverse=True) sales = cls.browse(sales) for sale in sales: if (sale.state in cls._states_cached and sale.untaxed_amount_cache is not None and sale.tax_amount_cache is not None and sale.total_amount_cache is not None): untaxed_amount[sale.id] = sale.untaxed_amount_cache total_discount[sale.id] = sale.total_discount_cache total_tip[sale.id] = sale.total_tip_cache if compute_taxes: tax_amount[sale.id] = sale.tax_amount_cache total_amount[sale.id] = sale.total_amount_cache else: untaxed_amount[sale.id] = round(sum( (line.amount for line in sale.lines if line.type == 'line'), Decimal(0)), 2) total_discount[sale.id] = round(sum( (line.discount_amount * Decimal( line.quantity) for line in sale.lines if line.discount_amount and line.type == 'line' ), Decimal(0)), 2) total_tip[sale.id] = round( sum(( line.amount for line in sale.lines if line.product and line.product.tip and ( line.type == 'line') ), Decimal(0)), 2) if compute_taxes: tax_amount[sale.id] = sale.get_tax_amount() total_amount[sale.id] = ( untaxed_amount[sale.id] + tax_amount[sale.id]) result = { 'untaxed_amount': untaxed_amount, 'tax_amount': tax_amount, 'total_amount': total_amount, 'total_discount': total_discount, 'total_tip': total_tip } for key in list(result.keys()): if key not in names: del result[key] return result def get_invoice_resolution(subtype): if subtype: resolution = subtype.sequence.invoice_resolution if resolution: return dict([ ('resolution_number', resolution.resolution_number), ('resolution_prefix', resolution.prefix), ('valid_date_time_from', str( resolution.valid_date_time_from)), ('valid_date_time_to', str(resolution.valid_date_time_to)), ('from_number', resolution.from_number), ('to_number', resolution.to_number)]) @classmethod def get_invoice(cls, record): if record.state != 'draft' and record.invoices: invoice = record.invoices[0] data = {} data['invoice_number'] = invoice.number subtype = invoice.subtype data['resolution'] = cls.get_invoice_resolution(subtype) return data @classmethod def report_bill(cls, records): if not records: return pool = Pool() ctx = Transaction().context record = records[0] Shop = pool.get('sale.shop') data = {} shop = Shop.search([('id', '=', ctx["shop"])])[0] data["shop_name"] = shop.name data["shop_nit"] = shop.company.party.tax_identifier.code data["shop_address"] = shop.address.street data['invoice'] = cls.get_invoice(record) data["party"] = record.party.name data["tax_identifier_type"] = record.party.tax_identifier.type_string data["tax_identifier_code"] = record.party.tax_identifier.code data["address"] = record.invoice_address.street data["city"] = record.invoice_address.subdivision_municipality.name data["zone"] = record.zone.name if record.zone else "" data["table"] = record.table.name if record.table else "" data["lines"] = [{ 'type': line.type, "product": line.product.name if line.type != 'title' else None, "quantity": line.quantity if line.type != 'title' else None, "uom": line.unit.symbol if line.type != 'title' else None, "unit_price": str(line.amount_w_tax) if line.type != 'title' else None, "taxes": str(round(line.taxes[0].rate * 100, 2)) + '%' if line.type != 'title' and line.taxes else None } for line in record.lines] data["total_discount"] = str(round(record.total_discount, 2)) data["untaxed_amount"] = str(record.untaxed_amount) data["tax_amount"] = str(record.tax_amount) data["total_tip"] = str(record.total_tip) data["total"] = str(record.total_amount) data["state"] = \ "SUBTOTAL" if record.state == "draft" else "CUENTA FINAL" if record.payments: data['payments'] = [{ "statement": payment.statement.journal.name, "amount": str(payment.amount)} for payment in record.payments] return data def report_customer_order(records): if not records: return report = records[0] data = {} data["party"] = report.party.name data["tax_identifier_type"] = report.party.tax_identifier.type_string data["tax_identifier_code"] = report.party.tax_identifier.code data["address"] = report.invoice_address.street data["city"] = report.invoice_address.subdivision_municipality.name data["zone"] = report.zone.name if report.zone else "" data["table"] = report.table.name if report.table else "" data["lines"] = [{ "type": line.type, "product": line.product.name if line.type != "title" else None, "description": line.description if line.type != "title" else None, "quantity": line.quantity if line.type != "title" else None, "uom": line.unit.name if line.type != "title" else None} for line in report.lines if not line.impreso] data["deleted_lines"] = [{ "product": line.product.name, "quantity": str(-1 * line.quantity), "unit": line.unit.symbol } for line in report.delete_lines if not line.printed] return data @classmethod @ModelView.button def add_pizza(cls, records): pool = Pool() saleLine = pool.get('sale.line') for record in records: record.pizza_number += 1 record.lines += (saleLine(type="title", description="Pizza Combinada"),) record.save() @classmethod @ModelView.button def impreso(cls, records): record = records[0] for line in record.lines: line.analytic_accounts = tuple() line.impreso = True line.save() for line in record.delete_lines: line.printed = True line.save() record.save() @classmethod @ModelView.button def print_bill(cls, records): pool = Pool() context = Transaction().context shop = context['shop'] Printer = pool.get('sale.printer') printers = Printer.search([ ('zone', '=', 'reception'), ('shop', '=', shop)]) if not printers: return printer = printers[0] url = f"http://{printer.api.ip_address}/print_bill" bill = cls.report_bill(records) if 'employee.rec_name' in context.keys(): user_name = context['employee.rec_name'] else: user_name = "" content = { "content": str(json.dumps(bill)), "printer_type": "", "ip_printer": "", "user_name": user_name, "idVendor": "", "idProduct": ""} if printer.type_ == 'network': content["printer_type"] = "Network" content["ip_printer"] = str(printer.ip_address) else: if printer.type_ == 'usb': content["printer_type"] = "USB" content["idVendor"] = str(printer.idVendor) content["idProduct"] = str(printer.idProduct) headers = {"accept": 'application/json', 'Content-Type': 'application/json'} requests.post( url, data=json.dumps(content), headers=headers, timeout=5) @classmethod @ModelView.button def kitchen(cls, records): pool = Pool() context = Transaction().context shop = context['shop'] Printer = pool.get('sale.printer') printers = Printer.search([('zone', '=', 'kitchen'), ('shop', '=', shop)]) record = records[0] if not printers: return printer = printers[0] url = f"http://{printer.api.ip_address}/order_kitchen" customer_order = cls.report_customer_order(records) if 'employee.rec_name' in context.keys(): user_name = context['employee.rec_name'] else: user_name = "" content = {"content": str(json.dumps(customer_order)), "printer_type": "", "ip_printer": "", "user_name": user_name, "idVendor": "", "idProduct": ""} if printer.type_ == 'network': content["printer_type"] = "Network" content["ip_printer"] = str(printer.ip_address) else: if printer.type_ == 'usb': content["printer_type"] = "USB" content["idVendor"] = str(printer.idVendor) content["idProduct"] = str(printer.idProduct) headers = {"accept": 'application/json', 'Content-Type': 'application/json'} cls.impreso([record]) requests.post( url, data=str(json.dumps(content)), headers=headers) @classmethod @ModelView.button def bar(cls, records): pool = Pool() context = Transaction().context shop = context['shop'] Printer = pool.get('sale.printer') printers = Printer.search([('zone', '=', 'bar'), ('shop', '=', shop)]) record = records[0] if not printers: return printer = printers[0] url = f"http://{printer.api.ip_address}/order_bar" customer_order = cls.report_customer_order(records) cls.impreso([record]) if 'employee.rec_name' in context.keys(): user_name = context['employee.rec_name'] else: user_name = "" content = { "content": str(json.dumps(customer_order)), "printer_type": "", "ip_printer": "", "user_name": user_name, "idVendor": "", "idProduct": ""} if printer.type_ == 'network': content["printer_type"] = "Network" content["ip_printer"] = str(printer.ip_address) else: if printer.type_ == 'usb': content["printer_type"] = "USB" content["idVendor"] = str(printer.idVendor) content["idProduct"] = str(printer.idProduct) headers = { "accept": 'application/json', 'Content-Type': 'application/json'} requests.post(url, data=json.dumps(content), headers=headers) class Line(metaclass=PoolMeta): "Sale Line Fast Food" __name__ = 'sale.line' pizza = fields.Integer("Pizza") impreso = fields.Boolean("Impreso") bought_pizza = fields.Boolean("Sold pizza") @classmethod def delete(cls, lines): for line in lines: if line.impreso: cls._create_sale_line_deleted_log(line) super(Line, cls).delete(lines) @fields.depends('product', 'unit', 'sale', '_parent_sale.party', '_parent_sale.invoice_party', '_parent_sale.pizza_number', '_parent_product.template', '_parent_product._parent_template.pizza', methods=['compute_taxes', 'compute_unit_price', 'on_change_with_amount']) def on_change_product(self): super(Line, self).on_change_product() if self.product and self.product.template.pizza: self.pizza = self.sale.pizza_number self.bought_pizza = True else: self.bought_pizza = False def get_production(self, product_quantities): "Return production for the sale line" Production = super(Line, self).get_production(product_quantities) return Production class SaleLineDeletedLog(metaclass=PoolMeta): """Sale Line Deleted Log""" __name__ = 'sale.line_deleted' printed = fields.Boolean("Printed")