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 trytond.pyson import Eval from decimal import Decimal import requests import json class Sale(metaclass=PoolMeta): "Sale Fast Food" __name__ = 'sale.sale' pizza_number = fields.Integer("Number pizza") 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 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): pool = Pool() ctx = Transaction().context Shop = pool.get('sale.shop') shop = Shop.search([('id', '=', ctx["shop"])])[0] 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_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 if line.type == 'line' and not line.product.tip] data["total_discount"] = str(round(record.total_discount,2)) data["untaxed_amount"] = str(record.untaxed_amount) data["total_tip"] = str(record.total_tip) data["tax_amount"] = str(record.tax_amount) 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, # "quantity": line.quantity if line.type != 'title' else None, # "uom": line.unit.name if line.type != 'title' else None} # for line in report.lines] 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] 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() 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)), "ip_printer": str(printer.ip_address), "user_name": user_name} headers = {"accept": 'application/json', 'Content-Type': 'application/json'} response = requests.post(url, data=json.dumps(content), headers=headers) @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)), "ip_printer": str(printer.ip_address), "user_name": user_name} headers = {"accept": 'application/json', 'Content-Type': 'application/json'} cls.impreso([record]) response = requests.post(url, data=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)), "ip_printer": str(printer.ip_address), "user_name": user_name} headers = {"accept": 'application/json', 'Content-Type': 'application/json'} response = 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") rate = fields.Numeric( "Rate", digits=(16, 4), states={ 'invisible': Eval('type') != 'line', 'readonly': Eval('sale_state') != 'draft', }, depends=['type', 'sale_state']) @classmethod def default_rate(cls): return 0 @fields.depends('product', 'unit', 'sale', '_parent_sale.party', '_parent_sale.invoice_party', '_parent_sale.pizza_number', 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.pizza: self.pizza = self.sale.pizza_number self.bought_pizza = True else: self.bought_pizza = False def get_production(self): "Return production for the sale line" Production = super(Line, self).get_production() return Production @fields.depends('discount_rate') def on_change_discount_rate(self): if self.discount_rate: self.rate = self.discount_rate