Accrue and allocate rounding errors when there are multiple taxes
Closes #11317
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
* Accrue and allocate rounding errors when there are multiple taxes
|
||||
* Use match pattern to find journal
|
||||
* Display general ledger information on account form
|
||||
|
||||
|
||||
+20
-3
@@ -3,7 +3,7 @@
|
||||
import datetime
|
||||
from collections import namedtuple
|
||||
from decimal import Decimal
|
||||
from itertools import groupby
|
||||
from itertools import cycle, groupby
|
||||
|
||||
from sql import Literal
|
||||
from sql.aggregate import Sum
|
||||
@@ -1248,8 +1248,23 @@ class TaxableMixin(object):
|
||||
def _round_taxes(self, taxes):
|
||||
if not self.currency:
|
||||
return
|
||||
|
||||
remainder = 0
|
||||
for taxline in taxes.values():
|
||||
taxline['amount'] = self.currency.round(taxline['amount'])
|
||||
rounded_amount = self.currency.round(taxline['amount'])
|
||||
remainder += rounded_amount - taxline['amount']
|
||||
taxline['amount'] = rounded_amount
|
||||
|
||||
# We need to compensate the rounding we did
|
||||
remainder = self.currency.round(remainder, opposite=True)
|
||||
if abs(remainder) >= self.currency.rounding:
|
||||
offset_amount = self.currency.rounding.copy_sign(remainder)
|
||||
|
||||
for tax in cycle(taxes.values()):
|
||||
tax['amount'] -= offset_amount
|
||||
remainder -= offset_amount
|
||||
if abs(remainder) < self.currency.rounding:
|
||||
break
|
||||
|
||||
@fields.depends('company', methods=['_get_tax_context', '_round_taxes'])
|
||||
def _get_taxes(self):
|
||||
@@ -1267,6 +1282,7 @@ class TaxableMixin(object):
|
||||
assert all(t.company == self.company for t in line.taxes)
|
||||
l_taxes = Tax.compute(Tax.browse(line.taxes), line.unit_price,
|
||||
line.quantity, line.tax_date or self.tax_date)
|
||||
current_taxes = {}
|
||||
for tax in l_taxes:
|
||||
taxline = self._compute_tax_line(**tax)
|
||||
# Base must always be rounded per line as there will be one
|
||||
@@ -1278,8 +1294,9 @@ class TaxableMixin(object):
|
||||
else:
|
||||
taxes[taxline]['base'] += taxline['base']
|
||||
taxes[taxline]['amount'] += taxline['amount']
|
||||
current_taxes[taxline] = taxes[taxline]
|
||||
if tax_rounding == 'line':
|
||||
self._round_taxes(taxes)
|
||||
self._round_taxes(current_taxes)
|
||||
if tax_rounding == 'document':
|
||||
self._round_taxes(taxes)
|
||||
return taxes
|
||||
|
||||
@@ -1153,6 +1153,108 @@ class AccountTestCase(
|
||||
self.assertEqual(tax['base'], Decimal('100.00'))
|
||||
self.assertEqual(tax['amount'], Decimal('20.02'))
|
||||
|
||||
@with_transaction()
|
||||
def test_taxable_mixin_tax_residual_rounding(self):
|
||||
"Test TaxableMixin for rounding with residual amount"
|
||||
pool = Pool()
|
||||
Account = pool.get('account.account')
|
||||
Tax = pool.get('account.tax')
|
||||
Configuration = pool.get('account.configuration')
|
||||
currency = create_currency('cur')
|
||||
|
||||
company = create_company()
|
||||
with set_company(company):
|
||||
create_chart(company)
|
||||
|
||||
config = Configuration(1)
|
||||
config.tax_rounding = 'line'
|
||||
config.save()
|
||||
|
||||
tax_account, = Account.search([
|
||||
('name', '=', 'Main Tax'),
|
||||
])
|
||||
|
||||
tax1 = Tax()
|
||||
tax1.name = tax1.description = "Tax 1"
|
||||
tax1.type = 'percentage'
|
||||
tax1.rate = Decimal('.1')
|
||||
tax1.invoice_account = tax_account
|
||||
tax1.credit_note_account = tax_account
|
||||
tax1.save()
|
||||
tax2 = Tax()
|
||||
tax2.name = tax2.description = "Tax 2"
|
||||
tax2.type = 'percentage'
|
||||
tax2.rate = Decimal('.1')
|
||||
tax2.invoice_account = tax_account
|
||||
tax2.credit_note_account = tax_account
|
||||
tax2.save()
|
||||
|
||||
taxable = self.Taxable(
|
||||
currency=currency,
|
||||
taxable_lines=[
|
||||
([tax1, tax2], Decimal('1.0417'), 1, None),
|
||||
],
|
||||
company=company)
|
||||
|
||||
taxline_1, taxline_2 = taxable._get_taxes()
|
||||
self.assertEqual(taxline_1['base'], Decimal('1.04'))
|
||||
self.assertEqual(taxline_1['amount'], Decimal('.11'))
|
||||
self.assertEqual(taxline_2['base'], Decimal('1.04'))
|
||||
self.assertEqual(taxline_2['amount'], Decimal('.10'))
|
||||
|
||||
@with_transaction()
|
||||
def test_taxable_mixin_tax_residual_rounding_negative_residual(self):
|
||||
"Test TaxableMixin for rounding with negative residual amount"
|
||||
pool = Pool()
|
||||
Account = pool.get('account.account')
|
||||
Tax = pool.get('account.tax')
|
||||
Configuration = pool.get('account.configuration')
|
||||
currency = create_currency('cur')
|
||||
|
||||
company = create_company()
|
||||
with set_company(company):
|
||||
create_chart(company)
|
||||
|
||||
config = Configuration(1)
|
||||
config.tax_rounding = 'line'
|
||||
config.save()
|
||||
|
||||
tax_account, = Account.search([
|
||||
('name', '=', 'Main Tax'),
|
||||
])
|
||||
|
||||
tax1 = Tax()
|
||||
tax1.name = tax1.description = "Tax 1"
|
||||
tax1.type = 'percentage'
|
||||
tax1.rate = Decimal('.1')
|
||||
tax1.invoice_account = tax_account
|
||||
tax1.credit_note_account = tax_account
|
||||
tax1.save()
|
||||
tax2 = Tax()
|
||||
tax2.name = tax2.description = "Tax 2"
|
||||
tax2.type = 'percentage'
|
||||
tax2.rate = Decimal('.1')
|
||||
tax2.invoice_account = tax_account
|
||||
tax2.credit_note_account = tax_account
|
||||
tax2.save()
|
||||
|
||||
# -2.95 is the unit price of -3.54 with 20% tax included
|
||||
taxable = self.Taxable(
|
||||
currency=currency,
|
||||
taxable_lines=[
|
||||
([tax1], Decimal('30.00'), 1, None),
|
||||
([tax1, tax2], Decimal('-2.95'), 1, None),
|
||||
],
|
||||
company=company)
|
||||
|
||||
taxline_1, taxline_2, taxline_3 = taxable._get_taxes()
|
||||
self.assertEqual(taxline_1['base'], Decimal('30.00'))
|
||||
self.assertEqual(taxline_1['amount'], Decimal('3.00'))
|
||||
self.assertEqual(taxline_2['base'], Decimal('-2.95'))
|
||||
self.assertEqual(taxline_2['amount'], Decimal('-0.29'))
|
||||
self.assertEqual(taxline_3['base'], Decimal('-2.95'))
|
||||
self.assertEqual(taxline_3['amount'], Decimal('-0.30'))
|
||||
|
||||
@with_transaction()
|
||||
def test_tax_compute_with_children_update_unit_price(self):
|
||||
"Test tax compute with children taxes modifying unit_price"
|
||||
|
||||
Reference in New Issue
Block a user