This commit is contained in:
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||||
# this repository contains the full copyright notices and license terms.
|
# this repository contains the full copyright notices and license terms.
|
||||||
|
|
||||||
from . import tax
|
from . import invoice, tax
|
||||||
|
|
||||||
__all__ = ['tax']
|
__all__ = ['invoice', 'tax']
|
||||||
|
|||||||
+39
@@ -0,0 +1,39 @@
|
|||||||
|
"""Dominican invoice tax rounding policy.
|
||||||
|
|
||||||
|
Tryton's generic taxable mixin compensates the aggregate currency-rounding
|
||||||
|
remainder on the first non-zero tax. That keeps the rounded aggregate equal
|
||||||
|
to the aggregate of the unrounded taxes, but it can change the reported value
|
||||||
|
of a specific Dominican tax (ITBIS, ISC or CDT).
|
||||||
|
|
||||||
|
Dominican fiscal documents state every tax in currency precision. Each tax
|
||||||
|
must therefore retain its own rounded result; no residual is redistributed
|
||||||
|
between different tax liabilities or tax credits.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from trytond.model import fields
|
||||||
|
from trytond.pool import PoolMeta
|
||||||
|
|
||||||
|
|
||||||
|
def round_taxes_independently(currency, taxes):
|
||||||
|
"""Round every tax amount without reallocating the aggregate residual."""
|
||||||
|
if not currency:
|
||||||
|
return
|
||||||
|
for taxline in taxes.values():
|
||||||
|
taxline.amount = currency.round(taxline.amount)
|
||||||
|
|
||||||
|
|
||||||
|
class IndependentTaxRoundingMixin:
|
||||||
|
|
||||||
|
__slots__ = ()
|
||||||
|
|
||||||
|
@fields.depends('currency')
|
||||||
|
def _round_taxes(self, taxes):
|
||||||
|
round_taxes_independently(self.currency, taxes)
|
||||||
|
|
||||||
|
|
||||||
|
class Invoice(IndependentTaxRoundingMixin, metaclass=PoolMeta):
|
||||||
|
__name__ = 'account.invoice'
|
||||||
|
|
||||||
|
|
||||||
|
class InvoiceLine(IndependentTaxRoundingMixin, metaclass=PoolMeta):
|
||||||
|
__name__ = 'account.invoice.line'
|
||||||
+61
-2
@@ -4,12 +4,15 @@ from collections import Counter
|
|||||||
from configparser import ConfigParser
|
from configparser import ConfigParser
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from types import SimpleNamespace
|
||||||
from xml.etree import ElementTree as ET
|
from xml.etree import ElementTree as ET
|
||||||
|
|
||||||
from trytond.pool import Pool
|
from trytond.pool import Pool
|
||||||
from trytond.tests.test_tryton import ModuleTestCase, with_transaction
|
from trytond.tests.test_tryton import ModuleTestCase, with_transaction
|
||||||
from trytond.transaction import Transaction
|
from trytond.transaction import Transaction
|
||||||
|
|
||||||
|
from trytond.modules.account_do.invoice import round_taxes_independently
|
||||||
|
|
||||||
MODULE_DIR = Path(__file__).resolve().parent.parent
|
MODULE_DIR = Path(__file__).resolve().parent.parent
|
||||||
|
|
||||||
TAX_CODE_LINE_AUDIT = {
|
TAX_CODE_LINE_AUDIT = {
|
||||||
@@ -125,6 +128,56 @@ class AccountDoTestCase(ModuleTestCase):
|
|||||||
'account_stock_continental',
|
'account_stock_continental',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@with_transaction()
|
||||||
|
def test_invoice_taxes_round_independently(self):
|
||||||
|
"""A rounding residual must not change ITBIS, ISC or CDT."""
|
||||||
|
pool = Pool()
|
||||||
|
Currency = pool.get('currency.currency')
|
||||||
|
currency, = Currency.create([{
|
||||||
|
'name': 'Peso dominicano',
|
||||||
|
'code': 'DOP',
|
||||||
|
'symbol': 'RD$',
|
||||||
|
'digits': 2,
|
||||||
|
'rounding': Decimal('0.01'),
|
||||||
|
}])
|
||||||
|
taxes = {
|
||||||
|
'itbis': SimpleNamespace(amount=Decimal('91.8036')),
|
||||||
|
'isc': SimpleNamespace(amount=Decimal('51.0020')),
|
||||||
|
'cdt': SimpleNamespace(amount=Decimal('9.9000')),
|
||||||
|
}
|
||||||
|
|
||||||
|
round_taxes_independently(currency, taxes)
|
||||||
|
|
||||||
|
self.assertEqual(taxes['itbis'].amount, Decimal('91.80'))
|
||||||
|
self.assertEqual(taxes['isc'].amount, Decimal('51.00'))
|
||||||
|
self.assertEqual(taxes['cdt'].amount, Decimal('9.90'))
|
||||||
|
self.assertEqual(
|
||||||
|
sum(tax.amount for tax in taxes.values()), Decimal('152.70'))
|
||||||
|
|
||||||
|
@with_transaction()
|
||||||
|
def test_invoice_credit_note_taxes_round_independently(self):
|
||||||
|
"""The same policy applies symmetrically to credit notes."""
|
||||||
|
pool = Pool()
|
||||||
|
Currency = pool.get('currency.currency')
|
||||||
|
currency, = Currency.create([{
|
||||||
|
'name': 'Peso dominicano',
|
||||||
|
'code': 'DOP',
|
||||||
|
'symbol': 'RD$',
|
||||||
|
'digits': 2,
|
||||||
|
'rounding': Decimal('0.01'),
|
||||||
|
}])
|
||||||
|
taxes = {
|
||||||
|
'itbis': SimpleNamespace(amount=Decimal('-91.8036')),
|
||||||
|
'isc': SimpleNamespace(amount=Decimal('-51.0020')),
|
||||||
|
'cdt': SimpleNamespace(amount=Decimal('-9.9000')),
|
||||||
|
}
|
||||||
|
|
||||||
|
round_taxes_independently(currency, taxes)
|
||||||
|
|
||||||
|
self.assertEqual(taxes['itbis'].amount, Decimal('-91.80'))
|
||||||
|
self.assertEqual(taxes['isc'].amount, Decimal('-51.00'))
|
||||||
|
self.assertEqual(taxes['cdt'].amount, Decimal('-9.90'))
|
||||||
|
|
||||||
@with_transaction()
|
@with_transaction()
|
||||||
def test_chart_template_is_loaded_by_default(self):
|
def test_chart_template_is_loaded_by_default(self):
|
||||||
'Test English Dominican chart is loaded in an English database'
|
'Test English Dominican chart is loaded in an English database'
|
||||||
@@ -571,7 +624,7 @@ class AccountDoUnitTestCase(unittest.TestCase):
|
|||||||
self.assertEqual(config.get('tryton', 'version'), '8.0.5')
|
self.assertEqual(config.get('tryton', 'version'), '8.0.5')
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
set(lines('tryton', 'depends')),
|
set(lines('tryton', 'depends')),
|
||||||
{'account', 'ir'})
|
{'account', 'account_invoice', 'ir'})
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
lines('tryton', 'xml'),
|
lines('tryton', 'xml'),
|
||||||
[
|
[
|
||||||
@@ -587,7 +640,13 @@ class AccountDoUnitTestCase(unittest.TestCase):
|
|||||||
])
|
])
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
lines('register', 'model'),
|
lines('register', 'model'),
|
||||||
['ir.ModelData', 'tax.TaxTemplate', 'tax.Tax'])
|
[
|
||||||
|
'ir.ModelData',
|
||||||
|
'invoice.Invoice',
|
||||||
|
'invoice.InvoiceLine',
|
||||||
|
'tax.TaxTemplate',
|
||||||
|
'tax.Tax',
|
||||||
|
])
|
||||||
self.assertEqual(lines('register', 'wizard'), ['account.CreateChart'])
|
self.assertEqual(lines('register', 'wizard'), ['account.CreateChart'])
|
||||||
for filename in lines('tryton', 'xml'):
|
for filename in lines('tryton', 'xml'):
|
||||||
self.assertTrue((MODULE_DIR / filename).is_file())
|
self.assertTrue((MODULE_DIR / filename).is_file())
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
version=8.0.5
|
version=8.0.5
|
||||||
depends:
|
depends:
|
||||||
account
|
account
|
||||||
|
account_invoice
|
||||||
ir
|
ir
|
||||||
xml:
|
xml:
|
||||||
account_chart_do_en.xml
|
account_chart_do_en.xml
|
||||||
@@ -17,6 +18,8 @@ xml:
|
|||||||
[register]
|
[register]
|
||||||
model:
|
model:
|
||||||
ir.ModelData
|
ir.ModelData
|
||||||
|
invoice.Invoice
|
||||||
|
invoice.InvoiceLine
|
||||||
tax.TaxTemplate
|
tax.TaxTemplate
|
||||||
tax.Tax
|
tax.Tax
|
||||||
wizard:
|
wizard:
|
||||||
|
|||||||
Reference in New Issue
Block a user