From 5c33dbf8df0795630bccf9048ecef150dd117239 Mon Sep 17 00:00:00 2001 From: Antonio Espinosa Date: Fri, 24 Jul 2015 16:36:45 +0200 Subject: [PATCH] [IMP] Show aggregated taxes detail in pos.order and when printing ticket --- pos_pricelist/README.rst | 80 ++++++++++++++- pos_pricelist/__openerp__.py | 4 +- pos_pricelist/data/pos_order.yml | 25 +++++ pos_pricelist/models/point_of_sale.py | 110 ++++++++++++++++++++- pos_pricelist/report/report_receipt.xml | 36 +++++++ pos_pricelist/views/point_of_sale_view.xml | 11 +++ 6 files changed, 258 insertions(+), 8 deletions(-) create mode 100644 pos_pricelist/data/pos_order.yml create mode 100644 pos_pricelist/report/report_receipt.xml diff --git a/pos_pricelist/README.rst b/pos_pricelist/README.rst index 8f353a3b..16ef83b8 100644 --- a/pos_pricelist/README.rst +++ b/pos_pricelist/README.rst @@ -1,3 +1,6 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :alt: License: AGPL-3 + Dynamic Price for Odoo Point of Sale ==================================== @@ -20,13 +23,31 @@ Goal of the module The goal of this module is to bring the pricelist computation engine to the POS. This module loads all the necessary data into the POS in order to have a coherent behaviour (offline/online/backend). -Implemented features --------------------- + +Installation +============ + +Nothing special is needed to install this module. + + +Configuration +============= + +You'll have new configuration parameters at Point of Sale > Configuration > Point of Sales + +* Price with Taxes: Show prices with taxes in POS session or not + + +Usage +===== + +Implemented features at POS Session +----------------------------------- 1. Attached pricelist on partner will take effect on the POS, which means that if we attach a pricelist to a partner. The POS will recognize it and will compute the price according to the rule defined. -2. Fiscal Position of each partner will also be present so taxes will be correctly computed +2. Fiscal Position of each partner will also be present so taxes will be correctly computed (conforming to the fiscal position). - Implemented Rules are : @@ -44,7 +65,60 @@ The computation take in account the pricelist and the fiscal position of the cus 3x -> 70 € 5x -> 50 € + +Implemented features at backend +------------------------------- + +1. Tax details + +- Tax details per order line +- Tax details aggregated by tax at order level + +2. Ticket + +- Tax details table added at end of printed ticket + + +Known issues / Roadmap +====================== + Missing features ---------------- * As you may know, product template is not fully implemented in the POS, so I decided to drop it from this module. + + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed feedback +`here `_. + + +Credits +======= + +Contributors +------------ + +* Adil Houmadi +* Pablo Cayuela +* Antonio Espinosa + + +Maintainer +---------- + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +This module is maintained by the OCA. + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +To contribute to this module, please visit http://odoo-community.org. diff --git a/pos_pricelist/__openerp__.py b/pos_pricelist/__openerp__.py index c5106d42..d6965e41 100644 --- a/pos_pricelist/__openerp__.py +++ b/pos_pricelist/__openerp__.py @@ -34,7 +34,9 @@ New feature for the Point Of Sale: 'data': [ "views/pos_pricelist_template.xml", "views/pos_pricelist_views.xml", - "views/point_of_sale_view.xml" + "views/point_of_sale_view.xml", + "report/report_receipt.xml", + "data/pos_order.yml", ], 'demo': [ 'demo/pos_pricelist_demo.yml', diff --git a/pos_pricelist/data/pos_order.yml b/pos_pricelist/data/pos_order.yml new file mode 100644 index 00000000..5f510e48 --- /dev/null +++ b/pos_pricelist/data/pos_order.yml @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Python source code encoding : https://www.python.org/dev/peps/pep-0263/ +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright : +# (c) 2015 Antiun Ingenieria, SL (Madrid, Spain, http://www.antiun.com) +# Antonio Espinosa +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +- !function {model: pos.order, name: _install_tax_detail} diff --git a/pos_pricelist/models/point_of_sale.py b/pos_pricelist/models/point_of_sale.py index ab9b765b..92c3bed8 100644 --- a/pos_pricelist/models/point_of_sale.py +++ b/pos_pricelist/models/point_of_sale.py @@ -20,19 +20,47 @@ from openerp import models, fields, api +from openerp.addons import decimal_precision as dp +import logging +_logger = logging.getLogger(__name__) + + +class PosOrderTax(models.Model): + _name = 'pos.order.tax' + + pos_order = fields.Many2one('pos.order', string='POS Order', + ondelete='cascade', index=True) + tax = fields.Many2one('account.tax', string='Tax') + name = fields.Char(string='Tax Description', required=True) + base = fields.Float(string='Base', digits=dp.get_precision('Account')) + amount = fields.Float(string='Amount', digits=dp.get_precision('Account')) class PosOrderLine(models.Model): _inherit = "pos.order.line" + @api.multi + def _compute_taxes(self): + res = { + 'total': 0, + 'total_included': 0, + 'taxes': [], + } + for line in self: + price = line.price_unit * (1 - (line.discount or 0.0) / 100.0) + taxes = line.tax_ids.compute_all( + price, line.qty, product=line.product_id, + partner=line.order_id.partner_id) + res['total'] += taxes['total'] + res['total_included'] += taxes['total_included'] + res['taxes'] += taxes['taxes'] + return res + @api.one @api.depends('tax_ids', 'qty', 'price_unit', 'product_id', 'discount', 'order_id.partner_id') def _amount_line_all(self): - price = self.price_unit * (1 - (self.discount or 0.0) / 100.0) - taxes = self.tax_ids.compute_all( - price, self.qty, product=self.product_id, - partner=self.order_id.partner_id) + taxes = self._compute_taxes() self.price_subtotal = taxes['total'] self.price_subtotal_incl = taxes['total_included'] @@ -46,6 +74,9 @@ class PosOrderLine(models.Model): class PosOrder(models.Model): _inherit = "pos.order" + taxes = fields.One2many(comodel_name='pos.order.tax', + inverse_name='pos_order') + @api.model def _amount_line_tax(self, line): price = line.price_unit * (1 - (line.discount or 0.0) / 100.0) @@ -56,3 +87,74 @@ class PosOrder(models.Model): for c in taxes: val += c.get('amount', 0.0) return val + + @api.multi + def _tax_list_get(self): + agg_taxes = {} + tax_lines = [] + for order in self: + for line in order.lines: + tax_lines.append({ + 'base': line.price_subtotal, + 'taxes': line._compute_taxes()['taxes'] + }) + + for tax_line in tax_lines: + base = tax_line['base'] + for tax in tax_line['taxes']: + tax_id = str(tax['id']) + if tax_id in agg_taxes: + agg_taxes[tax_id]['base'] += base + agg_taxes[tax_id]['amount'] += tax['amount'] + else: + agg_taxes[tax_id] = { + 'tax_id': tax_id, + 'name': tax['name'], + 'base': base, + 'amount': tax['amount'], + } + return agg_taxes + + @api.multi + def compute_tax_detail(self): + taxes_to_delete = False + for order in self: + taxes_to_delete = self.env['pos.order.tax'].search( + [('pos_order', '=', order.id)]) + # Update order taxes list + for key, tax in self._tax_list_get().iteritems(): + current = taxes_to_delete.filtered( + lambda r: r.tax.id == tax['tax_id']) + if current: + current.write({ + 'base': tax['base'], + 'amount': tax['amount'], + }) + taxes_to_delete -= current + else: + current = self.env['pos.order.tax'].create({ + 'pos_order': order.id, + 'tax': tax['tax_id'], + 'name': tax['name'], + 'base': tax['base'], + 'amount': tax['amount'], + }) + if taxes_to_delete: + taxes_to_delete.unlink() + + @api.multi + def action_paid(self): + result = super(PosOrder, self).action_paid() + self.compute_tax_detail() + return result + + @api.model + def _install_tax_detail(self): + """Create tax details to pos.order's already paid, done or invoiced. + """ + # Find orders with state : paid, done or invoiced + orders = self.search([('state', 'in', ('paid', 'done', 'invoiced')), + ('taxes', '=', False)]) + # Compute tax detail + orders.compute_tax_detail() + _logger.info("%d orders computed installing module.", len(orders)) diff --git a/pos_pricelist/report/report_receipt.xml b/pos_pricelist/report/report_receipt.xml new file mode 100644 index 00000000..80572662 --- /dev/null +++ b/pos_pricelist/report/report_receipt.xml @@ -0,0 +1,36 @@ + + + + + + + + + \ No newline at end of file diff --git a/pos_pricelist/views/point_of_sale_view.xml b/pos_pricelist/views/point_of_sale_view.xml index 79e99e3e..5a1cc1be 100644 --- a/pos_pricelist/views/point_of_sale_view.xml +++ b/pos_pricelist/views/point_of_sale_view.xml @@ -10,6 +10,17 @@ + + + + + + + + + + +