diff --git a/addons/account/models/account_tax.py b/addons/account/models/account_tax.py index c85143e863b..a2982d8f7fc 100644 --- a/addons/account/models/account_tax.py +++ b/addons/account/models/account_tax.py @@ -73,7 +73,11 @@ class AccountTax(models.Model): price_include = fields.Boolean(string='Included in Price', default=False, help="Check this if the price you use on the product and invoices includes this tax.") include_base_amount = fields.Boolean(string='Affect Base of Subsequent Taxes', default=False, - help="If set, taxes which are computed after this one will be computed based on the price tax included.") + help="If set, taxes with a higher sequence than this one will be affected by it, provided they accept it.") + is_base_affected = fields.Boolean( + string="Base Affected by Previous Taxes", + default=True, + help="If set, taxes with a lower sequence might affect this one, provided they try to do it.") analytic = fields.Boolean(string="Include in Analytic Cost", help="If set, the amount computed by this tax will be assigned to the same analytic account as the invoice line (if any)") tax_group_id = fields.Many2one('account.tax.group', string="Tax Group", default=_default_tax_group, required=True) # Technical field to make the 'tax_exigibility' field invisible if the same named field is set to false in 'res.company' model @@ -373,6 +377,7 @@ class AccountTax(models.Model): # 3) Deal with the rounding methods if not currency: currency = company.currency_id + # By default, for each tax, tax amount will first be computed # and rounded at the 'Account' decimal precision for each # PO/SO/invoice line and then these rounded amounts will be @@ -510,11 +515,16 @@ class AccountTax(models.Model): i = 0 cumulated_tax_included_amount = 0 for tax in taxes: + price_include = self._context.get('force_price_include', tax.price_include) + + if price_include or tax.is_base_affected: + tax_base_amount = base + else: + tax_base_amount = total_excluded + tax_repartition_lines = (is_refund and tax.refund_repartition_line_ids or tax.invoice_repartition_line_ids).filtered(lambda x: x.repartition_type == 'tax') sum_repartition_factor = sum(tax_repartition_lines.mapped('factor')) - price_include = self._context.get('force_price_include', tax.price_include) - #compute the tax_amount if price_include and total_included_checkpoints.get(i): # We know the total to reach for that tax, so we make a substraction to avoid any rounding issues @@ -522,7 +532,7 @@ class AccountTax(models.Model): cumulated_tax_included_amount = 0 else: tax_amount = tax.with_context(force_price_include=False)._compute_amount( - base, sign * price_unit, quantity, product, partner) + tax_base_amount, sign * price_unit, quantity, product, partner) # Round the tax_amount multiplied by the computed repartition lines factor. tax_amount = round(tax_amount, precision_rounding=prec) @@ -563,7 +573,7 @@ class AccountTax(models.Model): 'id': tax.id, 'name': partner and tax.with_context(lang=partner.lang).name or tax.name, 'amount': sign * line_amount, - 'base': round(sign * base, precision_rounding=prec), + 'base': round(sign * tax_base_amount, precision_rounding=prec), 'sequence': tax.sequence, 'account_id': tax.cash_basis_transition_account_id.id if tax.tax_exigibility == 'on_payment' else repartition_line.account_id.id, 'analytic': tax.analytic, diff --git a/addons/account/models/chart_template.py b/addons/account/models/chart_template.py index 4364223f4ed..c2072ca7f33 100644 --- a/addons/account/models/chart_template.py +++ b/addons/account/models/chart_template.py @@ -914,7 +914,11 @@ class AccountTaxTemplate(models.Model): price_include = fields.Boolean(string='Included in Price', default=False, help="Check this if the price you use on the product and invoices includes this tax.") include_base_amount = fields.Boolean(string='Affect Subsequent Taxes', default=False, - help="If set, taxes which are computed after this one will be computed based on the price tax included.") + help="If set, taxes with a higher sequence than this one will be affected by it, provided they accept it.") + is_base_affected = fields.Boolean( + string="Base Affected by Previous Taxes", + default=True, + help="If set, taxes with a lower sequence might affect this one, provided they try to do it.") analytic = fields.Boolean(string="Analytic Cost", help="If set, the amount computed by this tax will be assigned to the same analytic account as the invoice line (if any)") invoice_repartition_line_ids = fields.One2many(string="Repartition for Invoices", comodel_name="account.tax.repartition.line.template", inverse_name="invoice_tax_id", copy=True, help="Repartition when the tax is used on an invoice") refund_repartition_line_ids = fields.One2many(string="Repartition for Refund Invoices", comodel_name="account.tax.repartition.line.template", inverse_name="refund_tax_id", copy=True, help="Repartition when the tax is used on a refund") @@ -964,6 +968,7 @@ class AccountTaxTemplate(models.Model): 'description': self.description, 'price_include': self.price_include, 'include_base_amount': self.include_base_amount, + 'is_base_affected': self.is_base_affected, 'analytic': self.analytic, 'children_tax_ids': [(6, 0, children_ids)], 'tax_exigibility': self.tax_exigibility, diff --git a/addons/account/tests/test_tax.py b/addons/account/tests/test_tax.py index 532ca7eb0c3..0e937444c95 100644 --- a/addons/account/tests/test_tax.py +++ b/addons/account/tests/test_tax.py @@ -1045,3 +1045,31 @@ class TestTax(TestTaxCommon): ], res3 ) + + def test_is_base_affected(self): + taxes = self.env['account.tax'].create([{ + 'name': 'test_is_base_affected%s' % i, + 'amount_type': 'percent', + 'amount': amount, + 'include_base_amount': include_base_amount, + 'is_base_affected': is_base_affected, + 'sequence': i, + } for i, amount, include_base_amount, is_base_affected in [ + (0, 6, True, True), + (1, 6, True, False), + (2, 10, False, True), + ]]) + + self._check_compute_all_results( + 123.2, # 'total_included' + 100.0, # 'total_excluded' + [ + # base, amount + # ------------------------- + (100.0, 6.0), + (100.0, 6.0), + (112.0, 11.2), + # ------------------------- + ], + taxes.compute_all(100.0), + ) diff --git a/addons/account/views/account_chart_template_views.xml b/addons/account/views/account_chart_template_views.xml index f42f53ecdc7..6aa16900233 100644 --- a/addons/account/views/account_chart_template_views.xml +++ b/addons/account/views/account_chart_template_views.xml @@ -172,6 +172,8 @@ + diff --git a/addons/account/views/account_tax_views.xml b/addons/account/views/account_tax_views.xml index 26862d825bd..d70f6932459 100644 --- a/addons/account/views/account_tax_views.xml +++ b/addons/account/views/account_tax_views.xml @@ -164,6 +164,8 @@ + diff --git a/addons/l10n_in/views/account_tax_views.xml b/addons/l10n_in/views/account_tax_views.xml index a108bda381e..116fc2ee976 100644 --- a/addons/l10n_in/views/account_tax_views.xml +++ b/addons/l10n_in/views/account_tax_views.xml @@ -5,7 +5,7 @@ account.tax - + diff --git a/addons/point_of_sale/models/account_tax.py b/addons/point_of_sale/models/account_tax.py index a8ad419a0e5..6e8dfae8b5e 100644 --- a/addons/point_of_sale/models/account_tax.py +++ b/addons/point_of_sale/models/account_tax.py @@ -10,7 +10,7 @@ class AccountTax(models.Model): def write(self, vals): forbidden_fields = set([ 'amount_type', 'amount', 'type_tax_use', 'tax_group_id', 'price_include', - 'include_base_amount' + 'include_base_amount', 'is_base_affected', ]) if forbidden_fields & set(vals.keys()): tax_ids = self.env['pos.order.line'].sudo().search([ diff --git a/addons/point_of_sale/static/src/js/models.js b/addons/point_of_sale/static/src/js/models.js index fd37a782d87..52ae9e50981 100644 --- a/addons/point_of_sale/static/src/js/models.js +++ b/addons/point_of_sale/static/src/js/models.js @@ -245,7 +245,7 @@ exports.PosModel = Backbone.Model.extend({ }, },{ model: 'account.tax', - fields: ['name','amount', 'price_include', 'include_base_amount', 'amount_type', 'children_tax_ids'], + fields: ['name','amount', 'price_include', 'include_base_amount', 'is_base_affected', 'amount_type', 'children_tax_ids'], domain: function(self) {return [['company_id', '=', self.company && self.company.id || false]]}, loaded: function(self, taxes){ self.taxes = taxes; @@ -2218,11 +2218,16 @@ exports.Orderline = Backbone.Model.extend({ i = 0; var cumulated_tax_included_amount = 0; _(taxes.reverse()).each(function(tax){ + if(tax.price_include || tax.is_base_affected) + var tax_base_amount = base; + else + var tax_base_amount = total_excluded; + if(tax.price_include && total_included_checkpoints[i] !== undefined){ var tax_amount = total_included_checkpoints[i] - (base + cumulated_tax_included_amount); cumulated_tax_included_amount = 0; }else - var tax_amount = self._compute_all(tax, base, quantity, true); + var tax_amount = self._compute_all(tax, tax_base_amount, quantity, true); tax_amount = round_pr(tax_amount, currency_rounding); @@ -2233,7 +2238,7 @@ exports.Orderline = Backbone.Model.extend({ 'id': tax.id, 'name': tax.name, 'amount': sign * tax_amount, - 'base': sign * round_pr(base, currency_rounding), + 'base': sign * round_pr(tax_base_amount, currency_rounding), }); if(tax.include_base_amount)