[FIX] account: wrong computation with currency rounding 0.05

- Create a tax:
  5%
  Included in price
- Set the currency rounding of the company to 0.05 (e.g. CHF)
- Create the following invoice
  Prod 1, 1 unit,  5.0 CHF, tax 5%
  Prod 2, 1 unit, 10.0 CHF, tax 5%
  Prod 3, 1 unit, 50.0 CHF, tax 5%

The total amount of the invoice is 64.95 CHF instead of 65.00 CHF, even
in a 'Round per line' configuration.

It happens because `compute_all` rounds the amounts following the number
of digits of the currency instead of using the `rounding` field.

Note that there is a discrepancy between the POS computation and the
Account computation: the POS already uses the `rounding` field instead
of the number of digits:

https://github.com/odoo/odoo/blob/80073c5430f0ab562dfc27572c108114161ebb25/addons/point_of_sale/static/src/js/models.js#L1855-L1991

opw-2341566

X-original-commit: 94353ebd6b47a286157f0559b194ff03ea49ff68
This commit is contained in:
Nicolas Martinelli
2020-10-14 17:24:31 +00:00
parent 60572d6597
commit fa60131f9c
2 changed files with 108 additions and 8 deletions
+8 -8
View File
@@ -385,7 +385,7 @@ class AccountTax(models.Model):
# precision when we round the tax amount for each line (we use
# the 'Account' decimal precision + 5), and that way it's like
# rounding after the sum of the tax amounts of each line
prec = currency.decimal_places
prec = currency.rounding
# In some cases, it is necessary to force/prevent the rounding of the tax and the total
# amounts. For example, in SO/PO line, we don't want to round the price unit at the
@@ -396,7 +396,7 @@ class AccountTax(models.Model):
round_tax = bool(self.env.context['round'])
if not round_tax:
prec += 5
prec *= 1e-5
# 4) Iterate the taxes in the reversed sequence order to retrieve the initial base of the computation.
# tax | base | amount |
@@ -525,8 +525,8 @@ class AccountTax(models.Model):
base, sign * price_unit, quantity, product, partner)
# Round the tax_amount multiplied by the computed repartition lines factor.
tax_amount = round(tax_amount, prec)
factorized_tax_amount = round(tax_amount * sum_repartition_factor, prec)
tax_amount = round(tax_amount, precision_rounding=prec)
factorized_tax_amount = round(tax_amount * sum_repartition_factor, precision_rounding=prec)
if price_include and not total_included_checkpoints.get(i):
cumulated_tax_included_amount += factorized_tax_amount
@@ -548,10 +548,10 @@ class AccountTax(models.Model):
# The factorized_tax_amount will be 0.06 (200% x 0.03). However, each line taken independently will compute
# 50% * 0.03 = 0.01 with rounding. It means there is 0.06 - 0.04 = 0.02 as total_rounding_error to dispatch
# in lines as 2 x 0.01.
repartition_line_amounts = [round(tax_amount * line.factor, prec) for line in tax_repartition_lines]
total_rounding_error = round(factorized_tax_amount - sum(repartition_line_amounts), prec)
repartition_line_amounts = [round(tax_amount * line.factor, precision_rounding=prec) for line in tax_repartition_lines]
total_rounding_error = round(factorized_tax_amount - sum(repartition_line_amounts), precision_rounding=prec)
nber_rounding_steps = int(abs(total_rounding_error / currency.rounding))
rounding_error = round(nber_rounding_steps and total_rounding_error / nber_rounding_steps or 0.0, prec)
rounding_error = round(nber_rounding_steps and total_rounding_error / nber_rounding_steps or 0.0, precision_rounding=prec)
for repartition_line, line_amount in zip(tax_repartition_lines, repartition_line_amounts):
@@ -563,7 +563,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, prec),
'base': round(sign * base, 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,
+100
View File
@@ -130,6 +130,12 @@ class TestTaxCommon(AccountTestInvoicingCommon):
'amount': 0,
})
cls.tax_5_percent = cls.env['account.tax'].with_company(cls.company_data['company']).create({
'name': "test_5_percent",
'amount_type': 'percent',
'amount': 5,
})
cls.tax_8_percent = cls.env['account.tax'].with_company(cls.company_data['company']).create({
'name': "test_8_percent",
'amount_type': 'percent',
@@ -878,6 +884,53 @@ class TestTax(TestTaxCommon):
res2
)
def test_rounding_tax_included_round_per_line_04(self):
''' Test the rounding of a 5% price included tax.
The decimal precision is set to 0.05.
'''
self.tax_5_percent.price_include = True
self.tax_5_percent.company_id.currency_id.rounding = 0.05
self.tax_5_percent.company_id.tax_calculation_rounding_method = 'round_per_line'
res1 = self.tax_5_percent.compute_all(5)
self._check_compute_all_results(
5, # 'total_included'
4.75, # 'total_excluded'
[
# base , amount
# ---------------
(4.75, 0.25),
# ---------------
],
res1
)
res2 = self.tax_5_percent.compute_all(10)
self._check_compute_all_results(
10, # 'total_included'
9.5, # 'total_excluded'
[
# base , amount
# ---------------
(9.5, 0.5),
# ---------------
],
res2
)
res3 = self.tax_5_percent.compute_all(50)
self._check_compute_all_results(
50, # 'total_included'
47.6, # 'total_excluded'
[
# base , amount
# ---------------
(47.6, 2.4),
# ---------------
],
res3
)
def test_rounding_tax_included_round_globally_01(self):
''' Test the rounding of a 19% price included tax in an invoice having 27000 and 10920 as lines.
The decimal precision is set to zero.
@@ -945,3 +998,50 @@ class TestTax(TestTaxCommon):
],
res2
)
def test_rounding_tax_included_round_globally_03(self):
''' Test the rounding of a 5% price included tax.
The decimal precision is set to 0.05.
'''
self.tax_5_percent.price_include = True
self.tax_5_percent.company_id.currency_id.rounding = 0.05
self.tax_5_percent.company_id.tax_calculation_rounding_method = 'round_globally'
res1 = self.tax_5_percent.compute_all(5)
self._check_compute_all_results(
5, # 'total_included'
4.75, # 'total_excluded'
[
# base , amount
# ---------------
(4.75, 0.25),
# ---------------
],
res1
)
res2 = self.tax_5_percent.compute_all(10)
self._check_compute_all_results(
10, # 'total_included'
9.5, # 'total_excluded'
[
# base , amount
# ---------------
(9.50, 0.50),
# ---------------
],
res2
)
res3 = self.tax_5_percent.compute_all(50)
self._check_compute_all_results(
50, # 'total_included'
47.6, # 'total_excluded'
[
# base , amount
# ---------------
(47.60, 2.40),
# ---------------
],
res3
)