diff --git a/__init__.py b/__init__.py index d5a1920..41d39e3 100644 --- a/__init__.py +++ b/__init__.py @@ -1,5 +1,6 @@ from trytond.pool import Pool -from . import product, sale, production, invoice, user, report_close_statement +from . import ( + product, sale, production, invoice, user, report_close_statement, report) __all__ = ['register'] @@ -14,6 +15,8 @@ def register(): user.User, production.Production, report_close_statement.ReportCloseStatementStart, + report.ReportSaleProduct, + report.ReportSaleContext, module='sale_fast_food', type_='model') Pool.register( report_close_statement.PrintReportCloseStatement, diff --git a/report.py b/report.py new file mode 100644 index 0000000..c28c8fe --- /dev/null +++ b/report.py @@ -0,0 +1,151 @@ +from trytond.model import ModelSQL, ModelView, fields +from trytond.pool import Pool +from trytond.pyson import If, Eval +from sql import Literal, Null +from sql.aggregate import Min, Sum +from sql.functions import CurrentTimestamp +from trytond.transaction import Transaction +import datetime + + +class ReportSaleContext(ModelView): + """Contexto de reportes de miembros de familia""" + __name__ = 'sale_fast_food.report.context' + + from_date = fields.Date( + "From Date", + domain=[ + If(Eval('to_date') & Eval('from_date'), + ('from_date', '<=', Eval('to_date')), + ()), + If(Eval('from_date'), + ('from_date', '<=', datetime.date.today())) + ]) + + to_date = fields.Date( + "To Date", + domain=[ + If(Eval('from_date') & Eval('to_date'), + ('to_date', '>=', Eval('from_date')), + ()), + If(Eval('to_date'), + ('to_date', '<=', datetime.date.today())) + ]) + + +class ReportSaleAbstract(ModelSQL): + + @classmethod + def table_query(cls): + from_item, tables = cls._joins() + + final_query = from_item.select( + *cls._columns(tables), + where=cls._where(tables), + group_by=cls._group_by(tables) + ) + + return final_query + + @classmethod + def _joins(cls): + pool = Pool() + tables = {} + + Sale = pool.get('sale.sale') + tables['sale.sale'] = sale = Sale.__table__() + + SaleLine = pool.get('sale.line') + tables['sale.line'] = sale_line = SaleLine.__table__() + + ProductTemplate = pool.get('product.template') + tables[ + 'product.template' + ] = product_template = ProductTemplate.__table__() + + ProductProduct = pool.get('product.product') + tables['product.product'] = product = ProductProduct.__table__() + + from_item = sale_line.left_join( + product, + condition=product.id == sale_line.product + ).left_join( + product_template, + condition=product.template == product_template.id + ).left_join( + sale, + condition=sale.id == sale_line.sale + ) + + return from_item, tables + + @classmethod + def _columns(cls, tables): + columns = [ + cls._column_id(tables).as_('id'), + Literal(0).as_('create_uid'), + CurrentTimestamp().as_('create_date'), + cls.write_uid.sql_cast(Literal(Null)).as_('write_uid'), + cls.write_date.sql_cast(Literal(Null)).as_('write_date'), + ] + + return columns + + @classmethod + def _where(cls, tables): + context = Transaction().context + where = Literal(True) + + from_date = context.get('from_date') + to_date = context.get('to_date') + + sale = tables['sale.sale'] + if from_date: + where &= sale.sale_date >= from_date + + if to_date: + where &= sale.sale_date <= to_date + + return where + + @classmethod + def _group_by(cls, tables): + raise NotImplementedError() + + @classmethod + def _column_id(cls, tables): + sale_line = tables['sale.line'] + return Min(sale_line.id) + + +class ReportSaleProduct(ReportSaleAbstract, ModelView): + """Report Sale Group by Product""" + __name__ = 'sale_fast_food.reporting.product' + + product_pizza = fields.Many2One('product.product', "Product") + quantity = fields.Float("Quantity") + + @classmethod + def _columns(cls, tables): + sale_line = tables['sale.line'] + + return super(ReportSaleProduct, cls)._columns(tables) + [ + sale_line.product.as_('product_pizza'), + Sum(sale_line.quantity).as_('quantity') + ] + + @classmethod + def _group_by(cls, tables): + sale_line = tables['sale.line'] + + return [ + sale_line.product + ] + + @classmethod + def _where(cls, tables): + where = super(ReportSaleProduct, cls)._where(tables) + product_template = tables['product.template'] + where &= product_template.pizza == Literal(True) + + return where diff --git a/report.xml b/report.xml new file mode 100644 index 0000000..0233db2 --- /dev/null +++ b/report.xml @@ -0,0 +1,48 @@ + + + + + + sale_fast_food.report.context + form + sale_fast_food_report_context_form + + + + + sale_fast_food.reporting.product + tree + pizzas_sold_by_product_list + + + + Pizza Sold by Product + sale_fast_food.reporting.product + sale_fast_food.report.context + + + + + + + + + + + + tree_open + + + + + diff --git a/tests/scenario_report_pizza.rst b/tests/scenario_report_pizza.rst new file mode 100644 index 0000000..713ff42 --- /dev/null +++ b/tests/scenario_report_pizza.rst @@ -0,0 +1,206 @@ +============================= +Sale Line Delete Log Scenario +============================= + +Imports:: + >>> from decimal import Decimal + >>> from proteus import Model, Wizard + >>> from trytond.tests.tools import activate_modules + >>> from trytond.modules.company.tests.tools import create_company, get_company + >>> from trytond.modules.account.tests.tools import ( + ... create_chart, create_fiscalyear, create_tax, get_accounts) + >>> from trytond.modules.account_invoice.tests.tools import ( + ... create_payment_term, set_fiscalyear_invoice_sequences) + >>> import datetime as dt + >>> today = dt.date.today() + >>> from trytond.tests.tools import set_user + >>> from trytond.modules.sale_shop.tests.tools import create_shop + >>> from trytond.modules.sale_line_delete_log.sale import SaleLineDeleted + + +Activate modules:: + + >>> config = activate_modules('sale_fast_food') + + + >>> User = Model.get('res.user') + >>> Party = Model.get('party.party') + >>> Employee = Model.get('company.employee') + >>> Journal = Model.get('account.journal') + >>> PaymentMethod = Model.get('account.invoice.payment.method') + >>> Party = Model.get('party.party') + >>> ProductUom = Model.get('product.uom') + >>> ProductTemplate = Model.get('product.template') + >>> Sale = Model.get('sale.sale') + >>> SaleLine = Model.get('sale.line') + +Create company:: + + >>> _ = create_company() + >>> company = get_company() + +Set employee:: + + >>> employee_party = Party(name="Employee") + >>> employee_party.save() + >>> employee = Employee(party=employee_party) + >>> employee.save() + >>> user = User(config.user) + >>> user.employees.append(employee) + >>> user.employee = employee + >>> user.save() + >>> set_user(user.id) + +Create fiscal year:: + + >>> fiscalyear = set_fiscalyear_invoice_sequences(create_fiscalyear(company, today)) + >>> fiscalyear.click('create_period') + +Create chart of accounts:: + + >>> _ = create_chart(company) + >>> accounts = get_accounts(company) + >>> revenue = accounts['revenue'] + >>> expense = accounts['expense'] + >>> cash = accounts['cash'] + + >>> cash_journal, = Journal.find([('type', '=', 'cash')]) + >>> cash_journal.save() + >>> payment_method = PaymentMethod() + >>> payment_method.name = 'Cash' + >>> payment_method.journal = cash_journal + >>> payment_method.credit_account = cash + >>> payment_method.debit_account = cash + >>> payment_method.save() + +Create tax:: + + >>> tax = create_tax(Decimal('.10')) + >>> tax.save() + +Create parties:: + + >>> supplier = Party(name='Supplier') + >>> supplier.save() + >>> customer = Party(name='Customer') + >>> customer.save() + +Create account categories:: + + >>> ProductCategory = Model.get('product.category') + >>> account_category = ProductCategory(name="Account Category") + >>> account_category.accounting = True + >>> account_category.account_expense = expense + >>> account_category.account_revenue = revenue + >>> account_category.save() + + >>> account_category_tax, = account_category.duplicate() + >>> account_category_tax.customer_taxes.append(tax) + >>> account_category_tax.save() + +Create product:: + + >>> unit, = ProductUom.find([('name', '=', 'Unit')]) + + >>> template = ProductTemplate() + >>> template.name = 'product' + >>> template.default_uom = unit + >>> template.type = 'goods' + >>> template.salable = True + >>> template.pizza = True + >>> template.list_price = Decimal('10') + >>> template.account_category = account_category_tax + >>> template.save() + >>> product_pizza, = template.products + + >>> template = ProductTemplate() + >>> template.name = 'service' + >>> template.default_uom = unit + >>> template.type = 'service' + >>> template.salable = True + >>> template.list_price = Decimal('30') + >>> template.account_category = account_category + >>> template.save() + >>> service, = template.products + +Create payment term:: + + >>> payment_term = create_payment_term() + >>> payment_term.save() + +Create product price list:: + + >>> ProductPriceList = Model.get('product.price_list') + >>> product_price_list = ProductPriceList() + >>> product_price_list.name = 'Price List' + >>> product_price_list.company = company + >>> product_price_list.save() + +Create an Inventory:: + + >>> Inventory = Model.get('stock.inventory') + >>> Location = Model.get('stock.location') + >>> storage, = Location.find([ + ... ('code', '=', 'STO'), + ... ]) + >>> inventory = Inventory() + >>> inventory.location = storage + >>> inventory_line = inventory.lines.new(product=product_pizza) + >>> inventory_line.quantity = 100.0 + >>> inventory_line.expected_quantity = 0.0 + >>> inventory.click('confirm') + >>> inventory.state + 'done' + +Create Sale Shop:: + + >>> shop = create_shop(payment_term, product_price_list) + >>> shop.save() + +Save Sale Shop User:: + + >>> User = Model.get('res.user') + >>> user, = User.find([]) + >>> user.shops.append(shop) + >>> user.shop = shop + >>> user.save() + >>> set_user(user) + +Sale 5 products:: + + >>> sale = Sale() + >>> sale.party = customer + >>> sale.payment_term = payment_term + >>> sale.invoice_method = 'order' + + >>> sale_line = SaleLine() + >>> sale.lines.append(sale_line) + + >>> sale_line.product = product_pizza + >>> sale_line.quantity = 2.0 + >>> sale_line.impreso = True + + >>> sale_line = SaleLine() + >>> sale.lines.append(sale_line) + >>> sale_line.product = product_pizza + >>> sale_line.quantity = 3.0 + + >>> sale_line = SaleLine() + >>> sale.lines.append(sale_line) + >>> sale_line.product = service + >>> sale_line.quantity = 5.0 + + >>> sale.save() + >>> len(sale.lines) + 3 + +Sale Fast Food Pizzas Sell by Product:: + >>> ReportSellPizzas = Model.get('sale_fast_food.reporting.product') + >>> reports = ReportSellPizzas.find([]) + + >>> expected_report = { + ... (product_pizza, 2) + ... } + + >>> actual_report = {(r.product_pizza, r.quantity) for r in reports} + >>> assert expected_report == actual_report, f"\n Expect: {expected_report} \n Actual: {actual_report}" diff --git a/tests/scenario_sale_fast_food.rst b/tests/scenario_sale_fast_food.rst index baac5cb..1e5f1f1 100644 --- a/tests/scenario_sale_fast_food.rst +++ b/tests/scenario_sale_fast_food.rst @@ -107,6 +107,7 @@ Create product:: >>> template.default_uom = unit >>> template.type = 'goods' >>> template.salable = True + >>> template.pizza = True >>> template.list_price = Decimal('10') >>> template.account_category = account_category_tax >>> template.save() @@ -206,4 +207,4 @@ Create a sale line delete log it's was printed:: >>> sale.delete_lines[0] proteus.Model.get('sale.line_deleted')(1) - >>> assert isinstance(sale.delete_lines[0], Model.get('sale.line_deleted')), "it's not instance SaleLineDeleted" + >>> assert isinstance(sale.delete_lines[0], Model.get('sale.line_deleted')), "it's not instance SaleLineDeleted" diff --git a/tryton.cfg b/tryton.cfg index f8c4209..5a7c753 100644 --- a/tryton.cfg +++ b/tryton.cfg @@ -18,3 +18,4 @@ xml: sale.xml user.xml report_close_statement.xml + report.xml diff --git a/view/pizzas_sold_by_product_list.xml b/view/pizzas_sold_by_product_list.xml new file mode 100644 index 0000000..25527ef --- /dev/null +++ b/view/pizzas_sold_by_product_list.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/view/sale_fast_food_report_context_form.xml b/view/sale_fast_food_report_context_form.xml new file mode 100644 index 0000000..25d22f0 --- /dev/null +++ b/view/sale_fast_food_report_context_form.xml @@ -0,0 +1,11 @@ + + +
+