From 89c9396cc336f9e8a27882d452ca64c735b9837f Mon Sep 17 00:00:00 2001 From: Laurent Smet Date: Fri, 15 Mar 2024 16:09:21 +0000 Subject: [PATCH] [FIX] account,point_of_sale: Fix computation of GST group of taxes in india Configure the group of taxes 5% GST to be price_included and apply it on 295. Both children taxes must have the exact same amount. However, this is not the case. In this example, we get 7.02 for one tax (correct) but 7.20 for the other. This is because both taxes are "include_base_amount". opw-3758458 closes odoo/odoo#160541 X-original-commit: ce28edbaae5a9af0a8c6e1f2addf4285ec56e9e1 Signed-off-by: Claire Bretton (clbr) Signed-off-by: Laurent Smet (las) --- addons/account/models/account_tax.py | 25 +++++++++---- addons/account/tests/test_tax.py | 26 +++++++++++-- addons/account_tax_python/tests/test_tax.py | 10 ++--- .../static/src/app/store/pos_store.js | 37 ++++++++++--------- 4 files changed, 64 insertions(+), 34 deletions(-) diff --git a/addons/account/models/account_tax.py b/addons/account/models/account_tax.py index 4962daa027e..c1a1d024c2c 100644 --- a/addons/account/models/account_tax.py +++ b/addons/account/models/account_tax.py @@ -757,11 +757,14 @@ class AccountTax(models.Model): # For the indian case, when facing two percent price-included taxes having the same percentage, # both need to produce the same tax amounts. To do that, the tax amount of those taxes are computed # directly during the first traveling in reversed order. - total_percentage = sum(tax_factor for _i, tax_factor in incl_tax_amounts['percent_taxes']) + total_tax_amount = 0.0 for i, tax_factor in incl_tax_amounts['percent_taxes']: - tax_amount = float_round(base * tax_factor / (100 + total_percentage), precision_rounding=prec) + tax_amount = float_round(base * tax_factor / (100 + percent_amount), precision_rounding=prec) + total_tax_amount += tax_amount cached_tax_amounts[i] = tax_amount fixed_amount += tax_amount + for i, tax_factor in incl_tax_amounts['percent_taxes']: + cached_base_amounts[i] = base - total_tax_amount percent_amount = 0.0 incl_tax_amounts.update({ @@ -822,7 +825,9 @@ class AccountTax(models.Model): 'fixed_amount': 0.0, } # Store the tax amounts we compute while searching for the total_excluded + cached_base_amounts = {} cached_tax_amounts = {} + is_base_affected = True if handle_price_include: for tax in reversed(taxes): tax_repartition_lines = ( @@ -832,7 +837,7 @@ class AccountTax(models.Model): ).filtered(lambda x: x.repartition_type == "tax") sum_repartition_factor = sum(tax_repartition_lines.mapped("factor")) - if tax.include_base_amount: + if tax.include_base_amount and is_base_affected: base = recompute_base(base, incl_tax_amounts) store_included_tax_total = True if self._context.get('force_price_include', tax.price_include): @@ -844,7 +849,8 @@ class AccountTax(models.Model): incl_tax_amounts['fixed_amount'] = abs(quantity) * tax.amount * sum_repartition_factor * abs(fixed_multiplicator) else: # tax.amount_type == other (python) - tax_amount = tax._compute_amount(base, sign * price_unit, quantity, product, partner, fixed_multiplicator) * sum_repartition_factor + tax_amount = tax._compute_amount(base, sign * price_unit, quantity, product, partner, fixed_multiplicator) + tax_amount = float_round(tax_amount, precision_rounding=prec) incl_tax_amounts['fixed_amount'] += tax_amount # Avoid unecessary re-computation cached_tax_amounts[i] = tax_amount @@ -857,6 +863,7 @@ class AccountTax(models.Model): total_included_checkpoints[i] = base store_included_tax_total = False i -= 1 + is_base_affected = tax.is_base_affected total_excluded = recompute_base(base, incl_tax_amounts) if self._context.get('round_base', True): @@ -881,7 +888,9 @@ class AccountTax(models.Model): for tax in taxes: price_include = self._context.get('force_price_include', tax.price_include) - if price_include or tax.is_base_affected: + if price_include and i in cached_base_amounts: + tax_base_amount = cached_base_amounts[i] + elif price_include or tax.is_base_affected: tax_base_amount = base else: tax_base_amount = total_excluded @@ -890,12 +899,12 @@ class AccountTax(models.Model): sum_repartition_factor = sum(tax_repartition_lines.mapped('factor')) #compute the tax_amount - if not skip_checkpoint and price_include and total_included_checkpoints.get(i) is not None and sum_repartition_factor != 0: + if price_include and i in cached_tax_amounts: + tax_amount = cached_tax_amounts[i] + elif not skip_checkpoint and price_include and total_included_checkpoints.get(i) is not None and sum_repartition_factor != 0: # We know the total to reach for that tax, so we make a substraction to avoid any rounding issues tax_amount = total_included_checkpoints[i] - (base + cumulated_tax_included_amount) cumulated_tax_included_amount = 0 - elif price_include and i in cached_tax_amounts: - tax_amount = cached_tax_amounts[i] else: tax_amount = tax.with_context(force_price_include=False)._compute_amount( tax_base_amount, sign * price_unit, quantity, product, partner, fixed_multiplicator) diff --git a/addons/account/tests/test_tax.py b/addons/account/tests/test_tax.py index 44406de8734..63c11259766 100644 --- a/addons/account/tests/test_tax.py +++ b/addons/account/tests/test_tax.py @@ -217,7 +217,7 @@ class TestTax(TestTaxCommon): ) def test_tax_group_percent(self): - res = self.group_tax_percent.with_context({'force_price_include':True}).compute_all(100.0) + res = self.group_tax_percent.with_context({'force_price_include': True}).compute_all(100.0) self._check_compute_all_results( 100, # 'total_included' 83.33, # 'total_excluded' @@ -232,10 +232,11 @@ class TestTax(TestTaxCommon): ) self.env.company.country_id = self.env.ref('base.in') - res = self.group_tax_percent.with_context({'force_price_include':True}).compute_all(100.0) + self.group_tax_percent.children_tax_ids.price_include = True + res = self.group_tax_percent.compute_all(100.0) self._check_compute_all_results( 100, # 'total_included' - 83.34, # 'total_excluded' + 83.34, # 'total_excluded' [ # base , amount | seq | amount | incl | incl_base # --------------------------------------------------- @@ -246,6 +247,25 @@ class TestTax(TestTaxCommon): res ) + self.group_tax_percent.children_tax_ids.write({ + 'amount': 2.5, + 'include_base_amount': True, + 'is_base_affected': False, + }) + res = self.group_tax_percent.compute_all(295.0) + self._check_compute_all_results( + 295.0, # 'total_included' + 280.96, # 'total_excluded' + [ + # base , amount | seq | amount | incl | incl_base + # --------------------------------------------------- + (280.96, 7.02), # | 1 | 10% | | + (280.96, 7.02), # | 2 | 10% | | + # --------------------------------------------------- + ], + res + ) + def test_tax_percent_division(self): self.division_tax.price_include = True self.division_tax.include_base_amount = True diff --git a/addons/account_tax_python/tests/test_tax.py b/addons/account_tax_python/tests/test_tax.py index 947e8503cdb..5341eef9ed5 100644 --- a/addons/account_tax_python/tests/test_tax.py +++ b/addons/account_tax_python/tests/test_tax.py @@ -20,7 +20,7 @@ class TestTaxPython(TestTaxCommon): def test_tax_python_basic(self): res = self.python_tax.compute_all(130.0) self._check_compute_all_results( - 136.96, # 'total_included' + 136.96, # 'total_included' 130.0, # 'total_excluded' [ # base , amount | seq | amount | incl | incl_base @@ -49,13 +49,13 @@ class TestTaxPython(TestTaxCommon): python_tax_2 = self.python_tax.copy() res = (self.python_tax + python_tax_2).compute_all(130.0) self._check_compute_all_results( - 130, # 'total_included' - 116.07, # 'total_excluded' + 130, # 'total_included' + 116.08, # 'total_excluded' [ # base , amount | seq | amount | incl | incl_base # --------------------------------------------------- - (116.07, 6.96), # | 1 | 6% | t | - (116.07, 6.97), # | 1 | 6% | t | + (116.08, 6.96), # | 1 | 6% | t | + (116.08, 6.96), # | 1 | 6% | t | # --------------------------------------------------- ], res diff --git a/addons/point_of_sale/static/src/app/store/pos_store.js b/addons/point_of_sale/static/src/app/store/pos_store.js index 645cfbe4193..2ec36f39a30 100644 --- a/addons/point_of_sale/static/src/app/store/pos_store.js +++ b/addons/point_of_sale/static/src/app/store/pos_store.js @@ -1531,14 +1531,16 @@ export class PosStore extends Reactive { } if (company.country && company.country.code === "IN") { - for (const [i, tax_factor] of incl_tax_amounts.percent_taxes) { - const tax_amount = round_pr( - (base_amount * tax_factor) / (100 + percent_amount), - currency_rounding - ); + let total_tax_amount = 0.0; + for(const [i, tax_factor] of incl_tax_amounts.percent_taxes){ + const tax_amount = round_pr(base_amount * tax_factor / (100 + percent_amount), currency_rounding); + total_tax_amount += tax_amount; cached_tax_amounts[i] = tax_amount; fixed_amount += tax_amount; } + for (const [i,] of incl_tax_amounts.percent_taxes) { + cached_base_amounts[i] = base - total_tax_amount; + } percent_amount = 0.0; } @@ -1574,9 +1576,11 @@ export class PosStore extends Reactive { }; var cached_tax_amounts = {}; + var cached_base_amounts = {}; + let is_base_affected = true; if (handle_price_include) { taxes.reverse().forEach(function (tax) { - if (tax.include_base_amount) { + if (tax.include_base_amount && is_base_affected) { base = recompute_base(base, incl_tax_amounts); store_included_tax_total = true; } @@ -1605,6 +1609,7 @@ export class PosStore extends Reactive { } } i -= 1; + is_base_affected = tax.is_base_affected; }); } @@ -1624,24 +1629,20 @@ export class PosStore extends Reactive { i = 0; var cumulated_tax_included_amount = 0; taxes.reverse().forEach(function (tax) { - if (tax.price_include || tax.is_base_affected) { + if (tax.price_include && i in cached_base_amounts) { + var tax_base_amount = cached_base_amounts[i]; + } else if (tax.price_include || tax.is_base_affected) { var tax_base_amount = base; } else { tax_base_amount = total_excluded; } - if ( - !skip_checkpoint && - tax.price_include && - total_included_checkpoints[i] !== undefined && - tax.sum_repartition_factor != 0 - ) { - var tax_amount = - total_included_checkpoints[i] - (base + cumulated_tax_included_amount); - cumulated_tax_included_amount = 0; - } else if (tax.price_include && cached_tax_amounts.hasOwnProperty(i)) { + if (tax.price_include && cached_tax_amounts.hasOwnProperty(i)) { var tax_amount = cached_tax_amounts[i]; - } else { + } else if (!skip_checkpoint && 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, tax_base_amount, quantity, true); }