[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:
@@ -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,
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user