Restore shared Dominican tax classification

This commit is contained in:
José Arturo García
2026-08-10 23:04:26 -04:00
parent f3d942a6e4
commit 03d30589cb
5 changed files with 184 additions and 3 deletions
+7
View File
@@ -1,3 +1,10 @@
Version 8.0.3 - 2026-08-10
--------------------------
* Restore the shared Dominican tax classification required by invoicing, NCF
management and electronic fiscal documents.
* Populate tax classifications for existing charts and language-scoped tax
templates.
Version 8.0.2 - 2026-08-10
--------------------------
* Structure income-statement types under current-period profit or loss, with
+4
View File
@@ -1,2 +1,6 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from . import tax
__all__ = ['tax']
+162
View File
@@ -0,0 +1,162 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from sql import Null, Table
from trytond.model import fields
from trytond.pool import Pool, PoolMeta
from trytond.transaction import Transaction
TAX_KIND = [
('', ''),
('itbis', 'ITBIS'),
('itbis_withholding', 'ITBIS Withholding'),
('itbis_withholding_acquirer', 'ITBIS Acquirer Withholding'),
('isr_withholding', 'ISR Withholding'),
('isc', 'Selective Consumption Tax (ISC)'),
('cdt', 'INDOTEL Contribution (CDT)'),
('tip', 'Statutory Tip'),
('others', 'Other Taxes'),
]
TAX_TEMPLATE_KINDS = {
'itbis': {
'do_tax_itbis_18_venta', 'do_tax_itbis_16_venta',
'do_tax_itbis_18_compra', 'do_tax_itbis_16_compra',
'do_tax_itbis_exento', 'do_tax_itbis_tasa_cero',
},
'itbis_withholding': {
'do_tax_ret_itbis_30', 'do_tax_ret_itbis_100_inf',
'do_tax_ret_itbis_75_inf', 'do_tax_ret_itbis_75_inf_16',
'do_tax_ret_itbis_100_goods_18',
'do_tax_ret_itbis_100_goods_16', 'do_tax_ret_itbis_rst_18',
'do_tax_ret_itbis_rst_16', 'do_tax_ret_itbis_insurance_100',
'do_tax_ret_itbis_airline_100',
'do_tax_ret_itbis_society_30_suf', 'do_tax_ret_itbis_hotel_100',
'do_tax_ret_itbis_state_100',
},
'isr_withholding': {
'do_tax_ret_isr_hon_5', 'do_tax_ret_isr_serv_10',
'do_tax_ret_isr_serv_15', 'do_tax_ret_isr_div_10',
'do_tax_ret_isr_int_10', 'do_tax_ret_isr_alq_10',
'do_tax_ret_isr_alq_15', 'do_tax_ret_isr_est_15',
'do_tax_ret_isr_est_5', 'do_tax_ret_isr_bovine_1',
'do_tax_ret_isr_exporter_25', 'do_tax_ret_isr_int_pj_1',
'do_tax_ret_isr_premios_25', 'do_tax_ret_isr_premios_10',
'do_tax_ret_isr_premios_15', 'do_tax_ret_isr_betting_15',
'do_tax_ret_isr_tragamonedas_10',
'do_tax_ret_isr_tragamonedas_15',
'do_tax_ret_isr_other_income_10',
'do_tax_ret_isr_other_income_15', 'do_tax_ret_isr_ext_27',
'do_tax_ret_isr_ext_15', 'do_tax_ret_isr_ext_10',
},
'isc': {
'do_tax_isc_bebidas_alc', 'do_tax_isc_tabaco',
'do_tax_isc_telecom', 'do_tax_isc_combustibles_16',
'do_tax_isc_avtur_65', 'do_tax_isc_fuel_rd2_gallon',
'do_tax_ret_isc_insurance_100', 'do_tax_isc_vehiculos',
},
'cdt': {'do_tax_cdt_indotel'},
'tip': {'do_tax_propina_10'},
'others': {
'do_tax_cheques_015', 'do_tax_cheques_020',
'do_tax_activos_1', 'do_tax_iti_3',
},
}
def _set_template_kinds(table):
cursor = Transaction().connection.cursor()
model_data = Table('ir_model_data')
kind_by_identifier = {
identifier + suffix: tax_kind
for tax_kind, identifiers in TAX_TEMPLATE_KINDS.items()
for identifier in identifiers
for suffix in ('', '_en', '_es_419')
}
cursor.execute(*model_data.select(
model_data.db_id, model_data.fs_id,
where=model_data.module == 'account_do'))
for template_id, identifier in cursor.fetchall():
tax_kind = kind_by_identifier.get(identifier)
if not tax_kind:
continue
cursor.execute(*table.update(
[table.tax_kind], [tax_kind],
where=(table.id == template_id)
& ((table.tax_kind == Null) | (table.tax_kind == ''))))
def _kind_from_values(values):
pool = Pool()
TaxGroup = pool.get('account.tax.group')
group = values.get('group')
if isinstance(group, int):
group = TaxGroup(group)
code = getattr(group, 'code', None)
if code == 'ITBIS':
amount = values.get('rate') or values.get('amount') or 0
return 'itbis_withholding' if amount < 0 else 'itbis'
if code in {'ISR', 'ISREXT'}:
return 'isr_withholding'
if code == 'ISC':
return 'isc'
if code == 'CDT':
return 'cdt'
if code == 'OTROS':
name = (values.get('name') or '').casefold()
if 'propina' in name or 'tip' in name:
return 'tip'
if 'vehículo' in name or 'vehicle' in name:
return 'isc'
return 'others'
return ''
class TaxTemplate(metaclass=PoolMeta):
__name__ = 'account.tax.template'
tax_kind = fields.Selection(TAX_KIND, 'Tax Kind', sort=False)
@classmethod
def __register__(cls, module_name):
super().__register__(module_name)
_set_template_kinds(cls.__table__())
@classmethod
def create(cls, vlist):
vlist = [values.copy() for values in vlist]
for values in vlist:
values.setdefault('tax_kind', _kind_from_values(values))
return super().create(vlist)
def _get_tax_value(self, tax=None):
values = super()._get_tax_value(tax=tax)
if not tax or tax.tax_kind != self.tax_kind:
values['tax_kind'] = self.tax_kind
return values
class Tax(metaclass=PoolMeta):
__name__ = 'account.tax'
tax_kind = fields.Selection(TAX_KIND, 'Tax Kind', sort=False)
@classmethod
def __register__(cls, module_name):
super().__register__(module_name)
table = cls.__table__()
template = Table('account_tax_template')
cursor = Transaction().connection.cursor()
cursor.execute(*table.join(
template, condition=template.id == table.template).select(
table.id, template.tax_kind,
where=(table.template != Null)
& (template.tax_kind != Null)
& (template.tax_kind != '')
& ((table.tax_kind == Null) | (table.tax_kind == ''))))
for tax_id, tax_kind in cursor.fetchall():
cursor.execute(*table.update(
[table.tax_kind], [tax_kind],
where=table.id == tax_id))
+8 -2
View File
@@ -229,6 +229,7 @@ class AccountDoTestCase(ModuleTestCase):
('company', '=', company.id),
('description', '=', 'ITBIS 18% Ventas'),
('rate', '=', Decimal('0.18')),
('tax_kind', '=', 'itbis'),
], limit=1))
self.assertTrue(TaxCode.search([
('company', '=', company.id),
@@ -362,12 +363,14 @@ class AccountDoTestCase(ModuleTestCase):
('company', '=', company.id),
('description', '=', 'ITBIS 18% Sales'),
('rate', '=', Decimal('0.18')),
('tax_kind', '=', 'itbis'),
], limit=1))
self.assertTrue(Tax.search([
('company', '=', company.id),
('description', '=',
'ITBIS Withholding Large Taxpayer 30%'),
('rate', '=', Decimal('-0.054')),
('tax_kind', '=', 'itbis_withholding'),
], limit=1))
self.assertTrue(Tax.search([
('company', '=', company.id),
@@ -565,7 +568,7 @@ class AccountDoUnitTestCase(unittest.TestCase):
if line.strip()
]
self.assertEqual(config.get('tryton', 'version'), '8.0.2')
self.assertEqual(config.get('tryton', 'version'), '8.0.3')
self.assertEqual(
set(lines('tryton', 'depends')),
{'account', 'ir'})
@@ -581,11 +584,14 @@ class AccountDoUnitTestCase(unittest.TestCase):
'tax_code_do_es_419.xml',
'tax_rule_do_es_419.xml',
])
self.assertEqual(lines('register', 'model'), ['ir.ModelData'])
self.assertEqual(
lines('register', 'model'),
['ir.ModelData', 'tax.TaxTemplate', 'tax.Tax'])
self.assertEqual(lines('register', 'wizard'), ['account.CreateChart'])
for filename in lines('tryton', 'xml'):
self.assertTrue((MODULE_DIR / filename).is_file())
self.assertTrue((MODULE_DIR / 'account.py').is_file())
self.assertTrue((MODULE_DIR / 'tax.py').is_file())
pyproject = (MODULE_DIR / 'pyproject.toml').read_text(encoding='utf-8')
self.assertIn("name = 'trytond_account_do'", pyproject)
self.assertIn(
+3 -1
View File
@@ -1,5 +1,5 @@
[tryton]
version=8.0.2
version=8.0.3
depends:
account
ir
@@ -16,5 +16,7 @@ xml:
[register]
model:
ir.ModelData
tax.TaxTemplate
tax.Tax
wizard:
account.CreateChart