39 Commits

Author SHA1 Message Date
2c9bcc82ce Merge branch '6.4' of https://gitea.onecluster.org/OneTeam/trytondo-sale_fast_food into 6.4 2023-09-03 17:15:07 -05:00
b8c9db77f4 total_discount 2023-09-03 17:14:11 -05:00
cosmos
2bf689ef87 Add field Waiter in res.user and transate 2023-09-03 16:53:37 -05:00
cosmos
94279af9b8 Final changes have the total discount field and translate 2023-09-03 13:51:17 -05:00
cosmos
f6a968535d Add fields total_discuont and total_discount_cache 2023-08-31 13:27:25 -05:00
03d8711ab3 Se limpia la casa 2023-08-28 13:38:23 -05:00
d60c932b45 Se limpia la casa 2023-08-28 13:34:37 -05:00
0e7694b347 feat: Se agregan traducciones a botones 2023-08-15 11:26:34 -05:00
7556fd3789 fix se corrige vistas de botones del restaurante 2023-08-14 14:10:20 -05:00
b737a14857 fix: clean home 2023-08-13 15:14:22 -05:00
cosmos
4e0c39fd78 Add fields kitchen and bar in form product 2023-08-11 18:05:25 -05:00
root
e18d857ed6 change impreso buttons 2023-08-02 21:17:53 +00:00
cosmos
3b058e0e4a Add button Impreso 2023-08-02 15:45:55 -05:00
a4a4481312 fix: se corrige error en on_change_product linea 230 2023-08-02 02:12:27 +00:00
a244f18bf9 chore: Se agrega Total de la venta a la vista tree de la venta 2023-07-31 10:43:37 -05:00
aca4b9e1b4 chore: Se agrega zona y mesa a la vista tree de la venta 2023-07-31 10:27:53 -05:00
693f2d2e04 Error Bug 2023-07-27 07:33:36 -05:00
2ab5993a90 finish bill ticket 2023-07-26 13:09:21 -05:00
7b244fa0da add information for print bill ticket 2023-07-26 00:36:00 -05:00
1c65c9da81 add button bill 2023-07-25 23:06:21 -05:00
5bb58622c3 field shop to sale 2023-07-25 22:20:50 -05:00
4f378cbae9 add printer by zone 2023-07-22 11:27:17 -05:00
a118d64c71 Add field Tip 2023-07-19 02:04:57 +00:00
root
d0bde61593 update print buttons 2023-07-11 19:37:12 +00:00
root
8f079d4f65 add bar button 2023-07-11 19:26:23 +00:00
62144b60ca add decode method to customer_order 2023-07-11 12:10:30 -05:00
bfdffca9e3 Merge branch '6.4' of https://gitea.onecluster.org/OneTeam/trytondo-sale_fast_food into 6.4
add report customer_order
2023-07-10 14:38:08 -05:00
69966b529a add report 2023-07-10 14:37:53 -05:00
root
55cefcb8ca add button kitchen 2023-07-10 19:32:48 +00:00
b38ac4cb3e Merge branch '6.4' of https://gitea.onecluster.org/OneTeam/trytondo-sale_fast_food into 6.4 2023-07-09 10:02:04 -05:00
2494b0b906 add print button kitchen 2023-07-09 10:01:00 -05:00
5054666e40 No requirido lineas de typo = Titulo 2023-06-28 20:01:20 +00:00
d9df054288 No requirido lineas de typo = Titulo 2023-06-28 20:00:31 +00:00
a723842c36 No requirido lineas de typo = Titulo 2023-06-28 19:58:33 +00:00
24da82aa41 add line of title to the sale 2023-06-28 10:18:35 -05:00
0bfc36b7c2 update production 2023-06-25 01:50:26 -05:00
8049d555f2 add production 2023-06-24 23:02:37 -05:00
e170c152ab delete element expand in label product template 2023-06-25 02:26:56 +00:00
975b64f44f add boms to template product 2023-06-24 10:29:11 -05:00
16 changed files with 1725 additions and 17 deletions

View File

@@ -1,5 +1,5 @@
from trytond.pool import Pool from trytond.pool import Pool
from . import product, sale from . import product, sale, production, invoice, user
__all__ = ['register'] __all__ = ['register']
@@ -7,8 +7,11 @@ __all__ = ['register']
def register(): def register():
Pool.register( Pool.register(
product.Product, product.Product,
invoice.InvoiceLine,
sale.Sale, sale.Sale,
sale.Line, sale.Line,
user.User,
production.Production,
module='sale_fast_food', type_='model') module='sale_fast_food', type_='model')
Pool.register( Pool.register(
module='sale_fast_food', type_='wizard') module='sale_fast_food', type_='wizard')

16
invoice.py Normal file
View File

@@ -0,0 +1,16 @@
from trytond.pool import Pool, PoolMeta
from trytond.model import ModelView, fields
from trytond.exceptions import UserError
from trytond.pyson import Eval
class InvoiceLine(metaclass=PoolMeta):
__name__ = 'account.invoice.line'
@classmethod
def __setup__(cls):
super(InvoiceLine, cls).__setup__()
cls.product.states['required'] = (Eval('type') == 'line')
cls.unit_price.domain = []

View File

@@ -6,6 +6,42 @@ msgctxt "field:product.template,pizza:"
msgid "Pizza" msgid "Pizza"
msgstr "Pizza" msgstr "Pizza"
msgctxt "field:product.template,pizza:" msgctxt "field:product.template,pizza_topping:"
msgid "Topping Pizza" msgid "Topping Pizza"
msgstr "Adiciónes" msgstr "Adiciónes"
msgctxt "field:product.template,boms:"
msgid "Boms"
msgstr "Lista de Materiales"
msgctxt "field:product.template,tip:"
msgid "Tip"
msgstr "Propina"
msgctxt "field:product.template,kitchen:"
msgid "Kitchen"
msgstr "Cocina"
msgctxt "model:ir.model.button,string:sale_add_pizza_button"
msgid "Add Pizza"
msgstr "Agregar Pizza"
msgctxt "model:ir.model.button,string:sale_order_kitchen_button"
msgid "Kitchen"
msgstr "Cocina"
msgctxt "model:ir.model.button,string:sale_order_bar_button"
msgid "Bar"
msgstr "Bar"
msgctxt "model:ir.model.button,string:sale_print_bill_button"
msgid "Bill"
msgstr "Subtotal"
msgctxt "field:sale.sale,total_discount:"
msgid "Total Discount"
msgstr "Total Descuento"
msgctxt "field:res.user,waiter:"
msgid "Waiter"
msgstr "Mesero"

View File

@@ -1,5 +1,6 @@
from trytond.pool import Pool, PoolMeta from trytond.pool import Pool, PoolMeta
from trytond.model import fields from trytond.model import fields
from trytond.pyson import Eval, Bool
class Product(metaclass=PoolMeta): class Product(metaclass=PoolMeta):
@@ -7,4 +8,9 @@ class Product(metaclass=PoolMeta):
__name__ = 'product.template' __name__ = 'product.template'
pizza = fields.Boolean("Pizza") pizza = fields.Boolean("Pizza")
Pizza_topping = fields.Boolean("Topping Pizza") pizza_topping = fields.Boolean("Topping Pizza")
tip = fields.Boolean("Tip")
kitchen = fields.Boolean("Kitchen")
bar = fields.Boolean("Bar")
boms = fields.Many2One('production.bom', "Boms",
states={'required': Eval('producible', True)})

62
production.py Normal file
View File

@@ -0,0 +1,62 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from decimal import Decimal
from trytond.pool import Pool, PoolMeta
from trytond.model import fields
from trytond.modules.product import price_digits, round_price
from trytond.exceptions import UserError
BOM_CHANGES = ['bom', 'product', 'quantity', 'uom', 'warehouse', 'location',
'company', 'inputs', 'outputs']
class Production(metaclass=PoolMeta):
"Production"
__name__ = 'production'
@fields.depends(*BOM_CHANGES)
def explode_bom(self):
pool = Pool()
Uom = pool.get('product.uom')
if not (self.bom and self.product and self.uom):
return
factor = self.bom.compute_factor(self.product, self.quantity or 0,
self.uom)
inputs = []
for input_ in self.bom.inputs:
if input_.product.producible:
for input_ in input_.product.template.boms.inputs:
quantity = input_.compute_quantity(factor)
move = self._explode_move_values(
self.picking_location, self.location, self.company,
input_, quantity)
if move:
inputs.append(move)
quantity = Uom.compute_qty(input_.uom, quantity,
input_.product.default_uom, round=False)
else:
quantity = input_.compute_quantity(factor)
move = self._explode_move_values(
self.picking_location, self.location, self.company,
input_, quantity)
if move:
inputs.append(move)
quantity = Uom.compute_qty(input_.uom, quantity,
input_.product.default_uom, round=False)
self.inputs = inputs
outputs = []
for output in self.bom.outputs:
quantity = output.compute_quantity(factor)
move = self._explode_move_values(
self.location, self.output_location, self.company, output,
quantity)
if move:
move.unit_price = Decimal(0)
outputs.append(move)
self.outputs = outputs

1206
report/customer_order.fodt Normal file

File diff suppressed because it is too large Load Diff

286
sale.py
View File

@@ -1,6 +1,11 @@
from trytond.pool import Pool, PoolMeta from trytond.pool import Pool, PoolMeta
from trytond.model import ModelView, fields from trytond.model import ModelView, fields
from trytond.exceptions import UserError from trytond.transaction import Transaction
from trytond.modules.currency.fields import Monetary
from decimal import Decimal
import requests
import json
class Sale(metaclass=PoolMeta): class Sale(metaclass=PoolMeta):
@@ -8,32 +13,301 @@ class Sale(metaclass=PoolMeta):
__name__ = 'sale.sale' __name__ = 'sale.sale'
pizza_number = fields.Integer("Number pizza") pizza_number = fields.Integer("Number pizza")
total_discount = fields.Function(
Monetary("Total Discount", digits='currency', currency='currency'), 'get_amount')
total_discount_cache = Monetary(
"Total Discount cache", digits='currency', currency='currency')
@classmethod @classmethod
def __setup__(cls): def __setup__(cls):
super(Sale, cls).__setup__() super(Sale, cls).__setup__()
cls._buttons.update({ cls._buttons.update({
'add_pizza': {}, 'add_pizza': {},
'kitchen': {},
'bar': {},
'print_bill': {},
'impreso': {},
}) })
@classmethod @classmethod
def default_pizza_number(cls): def default_pizza_number(cls):
return 0 return 0
@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 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] = sum(
(line.amount for line in sale.lines
if line.type == 'line'), Decimal(0))
total_discount[sale.id] = sum(
(line.discount_amount for line in sale.lines
if line.type == 'line'), Decimal(0))
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)
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,
"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 @classmethod
@ModelView.button @ModelView.button
def add_pizza(cls, records): def add_pizza(cls, records):
pool = Pool()
saleLine = pool.get('sale.line')
for record in records: for record in records:
record.pizza_number += 1 record.pizza_number += 1
record.lines += (saleLine(type="title",
description="Pizza Combinada"),)
record.save() 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): class Line(metaclass=PoolMeta):
"Sale Line Fast Food" "Sale Line Fast Food"
__name__ = 'sale.line' __name__ = 'sale.line'
pizza = fields.Integer("Pizza") pizza = fields.Integer("Pizza")
impreso = fields.Boolean("Impreso")
@fields.depends('product', 'unit', 'sale', @fields.depends('product', 'unit', 'sale',
'_parent_sale.party', '_parent_sale.invoice_party', '_parent_sale.party', '_parent_sale.invoice_party',
@@ -42,5 +316,11 @@ class Line(metaclass=PoolMeta):
'on_change_with_amount']) 'on_change_with_amount'])
def on_change_product(self): def on_change_product(self):
super(Line, self).on_change_product() super(Line, self).on_change_product()
if self.product.pizza: if self.product and self.product.pizza:
self.pizza = self.sale.pizza_number self.pizza = self.sale.pizza_number
def get_production(self):
"Return production for the sale line"
Production = super(Line, self).get_production()
return Production

View File

@@ -22,4 +22,35 @@
<field name="string">Add Pizza</field> <field name="string">Add Pizza</field>
<field name="model" search="[('model', '=', 'sale.sale')]"/> <field name="model" search="[('model', '=', 'sale.sale')]"/>
</record> </record>
<record model="ir.model.button" id="sale_order_kitchen_button">
<field name="name">kitchen</field>
<field name="string">Kitchen</field>
<field name="model" search="[('model', '=', 'sale.sale')]"/>
</record>
<record model="ir.model.button" id="sale_order_bar_button">
<field name="name">bar</field>
<field name="string">Bar</field>
<field name="model" search="[('model', '=', 'sale.sale')]"/>
</record>
<record model="ir.model.button" id="sale_print_bill_button">
<field name="name">print_bill</field>
<field name="string">Bill</field>
<field name="model" search="[('model', '=', 'sale.sale')]"/>
</record>
<record model="ir.model.button" id="sale_impreso_button">
<field name="name">impreso</field>
<field name="string">Impreso</field>
<field name="model" search="[('model', '=', 'sale.sale')]"/>
</record>
<record model="ir.action.report" id="report_customer_order">
<field name="name">Customer Order</field>
<field name="model">sale.sale</field>
<field name="report_name">sale.customer_order</field>
<field name="report">sale_fast_food/report/customer_order.fodt</field>
</record>
<record model="ir.action.keyword" id="report_customer_order_keyword">
<field name="keyword">form_print</field>
<field name="model">sale.sale,-1</field>
<field name="action" ref="report_customer_order"/>
</record>
</tryton> </tryton>

View File

@@ -2,8 +2,14 @@
version=6.4 version=6.4
depends: depends:
ir ir
res
product product
sale sale
sale_supply_production
#sale_printer
production
account_invoice
xml: xml:
product.xml product.xml
sale.xml sale.xml
user.xml

8
user.py Normal file
View File

@@ -0,0 +1,8 @@
from trytond.model import fields
from trytond.pool import Pool, PoolMeta
class User(metaclass=PoolMeta):
"User"
__name__ = 'res.user'
waiter = fields.Boolean('Waiter')

13
user.xml Normal file
View File

@@ -0,0 +1,13 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<tryton>
<data>
<record model="ir.ui.view" id="user_view_form" >
<field name="name">user_form</field>
<field name="inherit" ref="res.user_view_form"/>
<field name="model">res.user</field>
</record>
</data>
</tryton>

View File

@@ -7,5 +7,14 @@
<field name="pizza"/> <field name="pizza"/>
<label name="pizza_topping"/> <label name="pizza_topping"/>
<field name="pizza_topping"/> <field name="pizza_topping"/>
<label name="tip"/>
<field name="tip"/>
<label name="kitchen"/>
<field name="kitchen"/>
<label name="bar"/>
<field name="bar"/>
<newline/>
<label name="boms"/>
<field name="boms" xexpand="1"/>
</xpath> </xpath>
</data> </data>

View File

@@ -2,9 +2,18 @@
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of <!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. --> this repository contains the full copyright notices and license terms. -->
<data> <data>
<xpath expr="//field[@name='reference']" position="after"> <xpath expr="//field[@name='lines']" position="after">
<label name="pizza_number"/> <group col="-1" colspan="5" id="restaturant_buttons">
<field name="pizza_number"/> <button name="kitchen" icon="tryton-print"/>
<button name="bar" icon="tryton-print"/>
<button name="print_bill" icon="tryton-print"/>
<button name="add_pizza"/> <button name="add_pizza"/>
<button name="impreso"/>
</group>
<newline/>
</xpath>
<xpath expr="/form/notebook/page[@id='sale']/group[@id='amount']/field[@name='tax_amount']" position="after">
<label name="total_discount"/>
<field name="total_discount"/>
</xpath> </xpath>
</data> </data>

View File

@@ -5,5 +5,7 @@
<xpath expr="//field[@name='product']" position="after"> <xpath expr="//field[@name='product']" position="after">
<label name="pizza"/> <label name="pizza"/>
<field name="pizza"/> <field name="pizza"/>
<label name="impreso"/>
<field name="impreso"/>
</xpath> </xpath>
</data> </data>

12
view/sale_tree.xml Normal file
View File

@@ -0,0 +1,12 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<data>
<xpath expr="//field[@name='shop']" position="after">
<field name="zone" string="Zona"/>
<field name="table" string="Mesa"/>
</xpath>
<xpath expr="//field[@name='untaxed_amount']" position="after">
<field name="total_amount"/>
</xpath>
</data>

9
view/user_form.xml Normal file
View File

@@ -0,0 +1,9 @@
<?xml version="1.0"?>
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<data>
<xpath expr="//field[@name='name']" position="after">
<label name="waiter"/>
<field name="waiter"/>
</xpath>
</data>