21 Commits

Author SHA1 Message Date
4738104006 fix: Se añade total_tip 2024-03-05 14:42:20 -05:00
1ad532c033 fix: UPDATE on_change_lines 2024-03-04 13:17:35 -05:00
57f95f0b7d fix: Se elimina producto Tip de Bill report 2024-03-03 10:18:38 -05:00
cd199fbaa0 Fix: UPDATE Total descuento 2024-03-03 10:03:00 -05:00
c2b590b052 feat: Se añade campo rate in SaleLine 2024-02-25 14:05:35 -05:00
0bc83265e1 feat: Se añade tip in totals 2024-02-20 02:17:05 +00:00
07390de74a Se añade base gravada 2023-09-20 12:11:50 -05:00
808c94cf47 Se modifica plantilla reporte 2023-09-19 15:53:38 -05:00
0382a15ab7 Se Añade excepción si no se encuentran ventas 2023-09-19 15:39:42 -05:00
2d15aa389f Se añade rango de resolución, formato de moneda 2023-09-19 15:34:53 -05:00
39351c2640 Se añade ventas por pizzas 2023-09-17 23:53:24 -05:00
4b837318e3 Se adiciona lógica de extractos 2023-09-17 20:31:47 -05:00
50c40c0a1c chore: Se agregan traducciones y se organizan vistas 2023-09-17 19:02:00 -05:00
f45043af7e feat: Se crea reporte de extracto de ventas 2023-09-17 13:12:32 -05:00
cb12fe5e0c Se añade descripcion en la linea 2023-09-05 17:45:45 +00:00
ee13006322 Merge branch '6.4' of https://gitea.onecluster.org/OneTeam/trytondo-sale_fast_food into 6.4 2023-09-03 19:57:53 -05:00
c9e50bb844 se agrega subtotal 2023-09-03 19:57:00 -05:00
cosmos
de95b5c996 Infinite search bug fix 2023-09-03 19:38:13 -05:00
3b46ae9c78 Se añade redondeo al descuento total 2023-09-03 19:15:20 -05:00
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
10 changed files with 2437 additions and 22 deletions

View File

@@ -1,5 +1,5 @@
from trytond.pool import Pool
from . import product, sale, production, invoice, user
from . import product, sale, production, invoice, user, report_close_statement
__all__ = ['register']
@@ -12,8 +12,11 @@ def register():
sale.Line,
user.User,
production.Production,
report_close_statement.ReportCloseStatementStart,
module='sale_fast_food', type_='model')
Pool.register(
report_close_statement.PrintReportCloseStatement,
module='sale_fast_food', type_='wizard')
Pool.register(
report_close_statement.CashRegister,
module='sale_fast_food', type_='report')

View File

@@ -45,3 +45,27 @@ msgstr "Total Descuento"
msgctxt "field:res.user,waiter:"
msgid "Waiter"
msgstr "Mesero"
msgctxt "model:ir.ui.menu,name:menu_print_cash_register"
msgid "Print cash register"
msgstr "Imprimir arqueo de caja"
msgctxt "field:sale.print_cash_register.start,date:"
msgid "Date"
msgstr "Fecha"
msgctxt "field:sale.print_cash_register.start,shop:"
msgid "Shop"
msgstr "Tienda"
msgctxt "wizard_button:sale.print_cash_register,start,end:"
msgid "Cancel"
msgstr "Cancelar"
msgctxt "wizard_button:sale.print_cash_register,start,print_cash_register:"
msgid "Print Cash Register"
msgstr "Imprimir"
msgctxt "model:ir.action,name:wizard_print_cash_register"
msgid "Print cash register"
msgstr "Imprimir arqueo de caja"

2061
report/close_statement.fodt Normal file

File diff suppressed because it is too large Load Diff

212
report_close_statement.py Normal file
View File

@@ -0,0 +1,212 @@
# 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 trytond.model import ModelView, fields
from trytond.wizard import Wizard, StateView, Button, StateReport
from trytond.transaction import Transaction
from trytond.pool import Pool
from trytond.report import Report
from trytond.exceptions import UserError
from decimal import Decimal
from datetime import date
__all__ = ['ReportCloseStatementStart', 'PrintReportCloseStatement',
'CashRegister']
class ReportCloseStatementStart(ModelView):
"Vista inicial de reporte de extracto"
__name__ = 'sale.print_cash_register.start'
company = fields.Many2One(
'company.company', "Company", readonly=True, required=True)
currency = fields.Many2One('currency.currency', 'Currency', required=True)
date = fields.Date('Date', required=True)
shop = fields.Many2One('sale.shop', 'Shop', required=True)
@staticmethod
def default_company():
return Transaction().context.get('company')
@staticmethod
def default_currency():
Company = Pool().get('company.company')
if Transaction().context.get('company'):
company = Company(Transaction().context['company'])
return company.currency.id
@classmethod
def default_date(cls):
return date.today()
@classmethod
def default_shop(cls):
context = Transaction().context
shop = context['shop']
if shop:
return shop
class PrintReportCloseStatement(Wizard):
"Asistente para imprimir extracto"
__name__ = 'sale.print_cash_register'
start = StateView(
'sale.print_cash_register.start',
'sale_fast_food.print_cash_register_start_view_form', [
Button("Cancel", 'end', 'tryton-cancel'),
Button("Print Cash Register",
'print_cash_register', 'tryton-ok', default=True)])
print_cash_register = StateReport('sale.cash_register')
def do_print_cash_register(self, action):
data = {
'lang': self.start.company.party.lang,
'date': self.start.date,
'shop': self.start.shop.id,
'currency': self.start.currency.id,
}
return action, data
def transition_print_cash_register(self):
return 'end'
class CashRegister(Report):
"Extracto"
__name__ = 'sale.cash_register'
def _get_info_pizzas_sold(lines):
pizzas = 0
amount_w_tax = 0
amount_tip = 0
for line in lines:
if line.bought_pizza:
pizzas += line.quantity
amount_w_tax += line.amount_w_tax
if line.product and line.product.template.tip:
amount_tip += line.amount
return {
'amount_tip': amount_tip,
'amount_w_tax': amount_w_tax,
'pizzas': pizzas,
}
def _get_statements_by_date(date):
pool = Pool()
User = pool.get('res.user')
Statement = pool.get('account.statement')
user = Transaction().user
user = User(user)
device = user.sale_device
if not device:
return
journals = [j.id for j in device.journals]
statements = Statement.search([
('journal', 'in', journals),
('state', 'in', ['validated', 'posted']),
('date', '=', date)
])
return statements
def _get_invoices_by_subtype(date, shop):
pool = Pool()
Invoice = pool.get('account.invoice')
Shop = pool.get('sale.shop')
fe_pos_subtype = Shop(shop).fe_pos_subtype.id
pos_subtype = Shop(shop).pos_subtype.id
ranges = {}
invoices_fe = Invoice.search([('invoice_date', '=', date),
('subtype', '=', fe_pos_subtype)])
if invoices_fe:
ranges['range_fe'] = \
[invoices_fe[0].number, invoices_fe[-1].number]
invoices_pos = Invoice.search([('invoice_date', '=', date),
('subtype', '=', pos_subtype)])
if invoices_pos:
ranges['range_pos'] = \
[invoices_pos[0].number, invoices_pos[-1].number]
return ranges
@classmethod
def get_context(cls, records, header, data):
report_context = super(
CashRegister, cls).get_context(records, header, data)
pool = Pool()
Shop = pool.get('sale.shop')
Sale = pool.get('sale.sale')
Currency = pool.get('currency.currency')
currency = Currency(data['currency'])
date = data['date']
shop = data['shop']
today_sales_domain = [('state', 'in', ['processing', 'done']),
('sale_date', '=', date),
('shop', '=', shop)]
today_sales = Sale.search(today_sales_domain,
order=[('id', 'ASC')])
if not today_sales:
raise UserError(str("No se han encontrado ventas para este día."))
return
untaxed_amount = 0
tax_amount = 0
total_amount = 0
total_pizzas = 0
amount_w_tax_pizzas = []
amount_tips = []
for sale in today_sales:
untaxed_amount += sale.untaxed_amount
tax_amount += sale.tax_amount
info_pizzas_sold = cls._get_info_pizzas_sold(sale.lines)
total_pizzas += info_pizzas_sold['pizzas']
amount_w_tax_pizzas.append(info_pizzas_sold['amount_w_tax'])
amount_tips.append(info_pizzas_sold['amount_tip'])
total_amount += sale.total_amount
untaxed_amount_for_tax = round((tax_amount * 100)/Decimal('8.0'), 2)
statements = cls._get_statements_by_date(data['date'])
if statements:
report_context['statements'] = [{
'name': statement.journal.name,
'start_balance': statement.start_balance,
'end_balance': statement.end_balance,
'total_amount': sum(
(line.amount for line in statement.lines
if line.description), Decimal(0))
} for statement in statements]
report_context['total_statements'] = sum(
[s['total_amount'] for s in report_context['statements']])
ranges = cls._get_invoices_by_subtype(date, shop)
ranges_keys = ranges.keys()
if 'range_fe' in ranges_keys:
report_context['range_fe'] = ranges['range_fe']
else:
report_context['range_fe'] = []
if 'range_pos' in ranges:
report_context['range_pos'] = ranges['range_pos']
else:
report_context['range_pos'] = []
report_context['shop'] = Shop(shop).name
report_context['date'] = date
report_context['currency'] = currency
report_context['amount_w_tax_pizzas'] = sum(amount_w_tax_pizzas)
report_context['tips'] = sum(amount_tips)
report_context['untaxed_amount'] = untaxed_amount
report_context['untaxed_amount_for_tax'] = untaxed_amount_for_tax
report_context['tax_amount'] = tax_amount
report_context['total_amount'] = total_amount
report_context['number_pizzas_sold'] = total_pizzas
return report_context

View File

@@ -0,0 +1,28 @@
<?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.action.report" id="report_close_statement">
<field name="name">Close statement</field>
<field name="model"></field>
<field name="report_name">sale.cash_register</field>
<field name="report">sale_fast_food/report/close_statement.fodt</field>
<field name="template_extension">odt</field>
</record>
<record model="ir.ui.view" id="print_cash_register_start_view_form">
<field name="model">sale.print_cash_register.start</field>
<field name="type">form</field>
<field name="name">print_cash_register_start_form</field>
</record>
<record model="ir.action.wizard" id="wizard_print_cash_register">
<field name="name">Print cash register</field>
<field name="wiz_name">sale.print_cash_register</field>
</record>
<menuitem
parent="sale.menu_sale"
action="wizard_print_cash_register"
id="menu_print_cash_register"
icon="tryton-print"/>
</data>
</tryton>

99
sale.py
View File

@@ -2,6 +2,7 @@ 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
@@ -13,9 +14,17 @@ class Sale(metaclass=PoolMeta):
__name__ = 'sale.sale'
pizza_number = fields.Integer("Number pizza")
total_discount = fields.Function(Monetary("Total Discount", currency='currency', digits='currency'),
'get_amount')
total_discount_cache = Monetary("Total Discount cache", currency='currency', digits='currency')
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):
@@ -32,6 +41,27 @@ class Sale(metaclass=PoolMeta):
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:
@@ -39,6 +69,7 @@ class Sale(metaclass=PoolMeta):
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
@@ -47,32 +78,36 @@ class Sale(metaclass=PoolMeta):
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)
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):
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] = sum(
untaxed_amount[sale.id] = round(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 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] = (
@@ -83,6 +118,7 @@ class Sale(metaclass=PoolMeta):
'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:
@@ -97,7 +133,8 @@ class Sale(metaclass=PoolMeta):
return dict([
('resolution_number', resolution.resolution_number),
('resolution_prefix', resolution.prefix),
('valid_date_time_from', str(resolution.valid_date_time_from)),
('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)])
@@ -146,11 +183,14 @@ class Sale(metaclass=PoolMeta):
"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]
} 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'] = [{
@@ -172,16 +212,17 @@ class Sale(metaclass=PoolMeta):
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]
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]
@@ -305,6 +346,18 @@ class Line(metaclass=PoolMeta):
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',
@@ -315,9 +368,17 @@ class Line(metaclass=PoolMeta):
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

View File

@@ -9,7 +9,9 @@ depends:
#sale_printer
production
account_invoice
sale_shop
xml:
product.xml
sale.xml
user.xml
report_close_statement.xml

View File

@@ -0,0 +1,15 @@
<?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. -->
<form col="6">
<label name="company"/>
<field name="company"/>
<label name="currency"/>
<field name="currency"/>
<newline/>
<label name="date"/>
<field name="date"/>
<label name="shop"/>
<field name="shop"/>
</form>

View File

@@ -15,5 +15,8 @@ this repository contains the full copyright notices and license terms. -->
<xpath expr="/form/notebook/page[@id='sale']/group[@id='amount']/field[@name='tax_amount']" position="after">
<label name="total_discount"/>
<field name="total_discount"/>
<label name="total_tip"/>
<field name="total_tip"/>
</xpath>
</data>

View File

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