353 lines
13 KiB
Python
353 lines
13 KiB
Python
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")
|
|
total_discount = fields.Function(
|
|
Monetary(
|
|
"Total Discount", digits='currency', currency='currency'),
|
|
'get_amount')
|
|
total_discount_cache = fields.Numeric("Total Discount 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')
|
|
|
|
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_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 = {}
|
|
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
|
|
and sale.total_discount_cache is not None):
|
|
untaxed_amount[sale.id] = sale.untaxed_amount_cache
|
|
total_discount[sale.id] = sale.total_discount_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 for line in sale.lines
|
|
if 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,
|
|
}
|
|
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]
|
|
|
|
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"] = 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")
|
|
|
|
@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, product_quantities ):
|
|
"Return production for the sale line"
|
|
Production = super(Line, self).get_production(product_quantities)
|
|
|
|
return Production
|