[FIX] account: Fix computation of taxes with round_globally.
-github issue: 14673 closes odoo/odoo#44312 -task: 2007300 X-original-commit: 6d3f3c749dbccc875f67c17ee95086df494044f6 Signed-off-by: Quentin De Paoli (qdp) <qdp@openerp.com>
This commit is contained in:
@@ -1819,10 +1819,8 @@ class AccountTax(models.Model):
|
||||
# precision of the currency.
|
||||
# The context key 'round' allows to force the standard behavior.
|
||||
round_tax = False if company.tax_calculation_rounding_method == 'round_globally' else True
|
||||
round_total = True
|
||||
if 'round' in self.env.context:
|
||||
round_tax = bool(self.env.context['round'])
|
||||
round_total = bool(self.env.context['round'])
|
||||
|
||||
if not round_tax:
|
||||
prec += 5
|
||||
@@ -1850,7 +1848,33 @@ class AccountTax(models.Model):
|
||||
# (145 - 15) / (1.0 + 30%) * 90% = 130 / 1.3 * 90% = 90
|
||||
return (base_amount - fixed_amount) / (1.0 + percent_amount / 100.0) * (100 - division_amount) / 100
|
||||
|
||||
base = round(price_unit * quantity, prec)
|
||||
# The first/last base must absolutely be rounded to work in round globally.
|
||||
# Indeed, the sum of all taxes ('taxes' key in the result dictionary) must be strictly equals to
|
||||
# 'price_included' - 'price_excluded' whatever the rounding method.
|
||||
#
|
||||
# Example using the global rounding without any decimals:
|
||||
# Suppose two invoice lines: 27000 and 10920, both having a 19% price included tax.
|
||||
#
|
||||
# Line 1 Line 2
|
||||
# -----------------------------------------------------------------------
|
||||
# total_included: 27000 10920
|
||||
# tax: 27000 / 1.19 = 4310.924 10920 / 1.19 = 1743.529
|
||||
# total_excluded: 22689.076 9176.471
|
||||
#
|
||||
# If the rounding of the total_excluded isn't made at the end, it could lead to some rounding issues
|
||||
# when summing the tax amounts, e.g. on invoices.
|
||||
# In that case:
|
||||
# - amount_untaxed will be 22689 + 9176 = 31865
|
||||
# - amount_tax will be 4310.924 + 1743.529 = 6054.453 ~ 6054
|
||||
# - amount_total will be 31865 + 6054 = 37919 != 37920 = 27000 + 10920
|
||||
#
|
||||
# By performing a rounding at the end to compute the price_excluded amount, the amount_tax will be strictly
|
||||
# equals to 'price_included' - 'price_excluded' after rounding and then:
|
||||
# Line 1: sum(taxes) = 27000 - 22689 = 4311
|
||||
# Line 2: sum(taxes) = 10920 - 2176 = 8744
|
||||
# amount_tax = 4311 + 8744 = 13055
|
||||
# amount_total = 31865 + 13055 = 37920
|
||||
base = currency.round(price_unit * quantity)
|
||||
|
||||
# For the computation of move lines, we could have a negative base value.
|
||||
# In this case, compute all with positive values and negate them at the end.
|
||||
@@ -1898,7 +1922,7 @@ class AccountTax(models.Model):
|
||||
store_included_tax_total = False
|
||||
i -= 1
|
||||
|
||||
total_excluded = recompute_base(base, incl_fixed_amount, incl_percent_amount, incl_division_amount)
|
||||
total_excluded = currency.round(recompute_base(base, incl_fixed_amount, incl_percent_amount, incl_division_amount))
|
||||
|
||||
# 5) Iterate the taxes in the sequence order to compute missing tax amounts.
|
||||
# Start the computation of accumulated amounts at the total_excluded value.
|
||||
@@ -1983,9 +2007,9 @@ class AccountTax(models.Model):
|
||||
return {
|
||||
'base_tags': taxes.mapped(is_refund and 'refund_repartition_line_ids' or 'invoice_repartition_line_ids').filtered(lambda x: x.repartition_type == 'base').mapped('tag_ids').ids,
|
||||
'taxes': taxes_vals,
|
||||
'total_excluded': sign * (currency.round(total_excluded) if round_total else total_excluded),
|
||||
'total_included': sign * (currency.round(total_included) if round_total else total_included),
|
||||
'total_void': sign * (currency.round(total_void) if round_total else total_void),
|
||||
'total_excluded': sign * total_excluded,
|
||||
'total_included': sign * currency.round(total_included),
|
||||
'total_void': sign * currency.round(total_void),
|
||||
}
|
||||
|
||||
@api.model
|
||||
|
||||
@@ -107,6 +107,13 @@ class TestTaxCommon(AccountTestUsersCommon):
|
||||
}),
|
||||
],
|
||||
})
|
||||
|
||||
cls.tax_19_percent = cls.tax_model.create({
|
||||
'name': "test_rounding_methods_1",
|
||||
'amount_type': 'percent',
|
||||
'amount': 19,
|
||||
})
|
||||
|
||||
cls.bank_journal = cls.env['account.journal'].search([('type', '=', 'bank'), ('company_id', '=', cls.account_manager.company_id.id)])[0]
|
||||
cls.bank_account = cls.bank_journal.default_debit_account_id
|
||||
cls.expense_account = cls.env['account.account'].search([('user_type_id.type', '=', 'payable')], limit=1) #Should be done by onchange later
|
||||
@@ -649,3 +656,141 @@ class TestTax(TestTaxCommon):
|
||||
],
|
||||
tax.compute_all(-1.0)
|
||||
)
|
||||
|
||||
def test_rounding_tax_excluded_round_per_line(self):
|
||||
''' Test the rounding of a 19% price excluded tax in an invoice having 22689 and 9176 as lines.
|
||||
The decimal precision is set to zero.
|
||||
The computation must be similar to round(22689 * 0.19) + round(9176 * 0.19).
|
||||
'''
|
||||
self.tax_19_percent.company_id.currency_id.rounding = 1.0
|
||||
self.tax_19_percent.company_id.tax_calculation_rounding_method = 'round_per_line'
|
||||
|
||||
res1 = self.tax_19_percent.compute_all(22689)
|
||||
self._check_compute_all_results(
|
||||
27000, # 'total_included'
|
||||
22689, # 'total_excluded'
|
||||
[
|
||||
# base, amount
|
||||
# ---------------
|
||||
(22689, 4311),
|
||||
# ---------------
|
||||
],
|
||||
res1
|
||||
)
|
||||
|
||||
res2 = self.tax_19_percent.compute_all(9176)
|
||||
self._check_compute_all_results(
|
||||
10919, # 'total_included'
|
||||
9176, # 'total_excluded'
|
||||
[
|
||||
# base , amount
|
||||
# ---------------
|
||||
(9176, 1743),
|
||||
# ---------------
|
||||
],
|
||||
res2
|
||||
)
|
||||
|
||||
def test_rounding_tax_excluded_round_globally(self):
|
||||
''' Test the rounding of a 19% price excluded tax in an invoice having 22689 and 9176 as lines.
|
||||
The decimal precision is set to zero.
|
||||
The computation must be similar to round((22689 + 9176) * 0.19).
|
||||
'''
|
||||
self.tax_19_percent.company_id.currency_id.rounding = 1.0
|
||||
self.tax_19_percent.company_id.tax_calculation_rounding_method = 'round_globally'
|
||||
|
||||
res1 = self.tax_19_percent.compute_all(22689)
|
||||
self._check_compute_all_results(
|
||||
27000, # 'total_included'
|
||||
22689, # 'total_excluded'
|
||||
[
|
||||
# base, amount
|
||||
# ---------------
|
||||
(22689, 4310.91),
|
||||
# ---------------
|
||||
],
|
||||
res1
|
||||
)
|
||||
|
||||
res2 = self.tax_19_percent.compute_all(9176)
|
||||
self._check_compute_all_results(
|
||||
10919, # 'total_included'
|
||||
9176, # 'total_excluded'
|
||||
[
|
||||
# base , amount
|
||||
# ---------------
|
||||
(9176, 1743.44),
|
||||
# ---------------
|
||||
],
|
||||
res2
|
||||
)
|
||||
|
||||
def test_rounding_tax_included_round_per_line(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.
|
||||
The computation must be similar to round(27000 / 1.19) + round(10920 / 1.19).
|
||||
'''
|
||||
self.tax_19_percent.price_include = True
|
||||
self.tax_19_percent.company_id.currency_id.rounding = 1.0
|
||||
self.tax_19_percent.company_id.tax_calculation_rounding_method = 'round_per_line'
|
||||
|
||||
res1 = self.tax_19_percent.compute_all(27000)
|
||||
self._check_compute_all_results(
|
||||
27000, # 'total_included'
|
||||
22689, # 'total_excluded'
|
||||
[
|
||||
# base , amount
|
||||
# ---------------
|
||||
(22689, 4311),
|
||||
# ---------------
|
||||
],
|
||||
res1
|
||||
)
|
||||
|
||||
res2 = self.tax_19_percent.compute_all(10920)
|
||||
self._check_compute_all_results(
|
||||
10920, # 'total_included'
|
||||
9176, # 'total_excluded'
|
||||
[
|
||||
# base , amount
|
||||
# ---------------
|
||||
(9176, 1744),
|
||||
# ---------------
|
||||
],
|
||||
res2
|
||||
)
|
||||
|
||||
def test_rounding_tax_included_round_globally(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.
|
||||
The computation must be similar to round((27000 + 10920) / 1.19).
|
||||
'''
|
||||
self.tax_19_percent.price_include = True
|
||||
self.tax_19_percent.company_id.currency_id.rounding = 1.0
|
||||
self.tax_19_percent.company_id.tax_calculation_rounding_method = 'round_globally'
|
||||
|
||||
res1 = self.tax_19_percent.compute_all(27000)
|
||||
self._check_compute_all_results(
|
||||
27000, # 'total_included'
|
||||
22689, # 'total_excluded'
|
||||
[
|
||||
# base , amount
|
||||
# ---------------
|
||||
(22689, 4311),
|
||||
# ---------------
|
||||
],
|
||||
res1
|
||||
)
|
||||
|
||||
res2 = self.tax_19_percent.compute_all(10920)
|
||||
self._check_compute_all_results(
|
||||
10920, # 'total_included'
|
||||
9176, # 'total_excluded'
|
||||
[
|
||||
# base , amount
|
||||
# ---------------
|
||||
(9176, 1744),
|
||||
# ---------------
|
||||
],
|
||||
res2
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user