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 @@
+
+
+