Restore shared Dominican tax classification
This commit is contained in:
@@ -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
|
Version 8.0.2 - 2026-08-10
|
||||||
--------------------------
|
--------------------------
|
||||||
* Structure income-statement types under current-period profit or loss, with
|
* Structure income-statement types under current-period profit or loss, with
|
||||||
|
|||||||
@@ -1,2 +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
|
||||||
|
|
||||||
|
__all__ = ['tax']
|
||||||
|
|||||||
@@ -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))
|
||||||
@@ -229,6 +229,7 @@ class AccountDoTestCase(ModuleTestCase):
|
|||||||
('company', '=', company.id),
|
('company', '=', company.id),
|
||||||
('description', '=', 'ITBIS 18% Ventas'),
|
('description', '=', 'ITBIS 18% Ventas'),
|
||||||
('rate', '=', Decimal('0.18')),
|
('rate', '=', Decimal('0.18')),
|
||||||
|
('tax_kind', '=', 'itbis'),
|
||||||
], limit=1))
|
], limit=1))
|
||||||
self.assertTrue(TaxCode.search([
|
self.assertTrue(TaxCode.search([
|
||||||
('company', '=', company.id),
|
('company', '=', company.id),
|
||||||
@@ -362,12 +363,14 @@ class AccountDoTestCase(ModuleTestCase):
|
|||||||
('company', '=', company.id),
|
('company', '=', company.id),
|
||||||
('description', '=', 'ITBIS 18% Sales'),
|
('description', '=', 'ITBIS 18% Sales'),
|
||||||
('rate', '=', Decimal('0.18')),
|
('rate', '=', Decimal('0.18')),
|
||||||
|
('tax_kind', '=', 'itbis'),
|
||||||
], limit=1))
|
], limit=1))
|
||||||
self.assertTrue(Tax.search([
|
self.assertTrue(Tax.search([
|
||||||
('company', '=', company.id),
|
('company', '=', company.id),
|
||||||
('description', '=',
|
('description', '=',
|
||||||
'ITBIS Withholding Large Taxpayer 30%'),
|
'ITBIS Withholding Large Taxpayer 30%'),
|
||||||
('rate', '=', Decimal('-0.054')),
|
('rate', '=', Decimal('-0.054')),
|
||||||
|
('tax_kind', '=', 'itbis_withholding'),
|
||||||
], limit=1))
|
], limit=1))
|
||||||
self.assertTrue(Tax.search([
|
self.assertTrue(Tax.search([
|
||||||
('company', '=', company.id),
|
('company', '=', company.id),
|
||||||
@@ -565,7 +568,7 @@ class AccountDoUnitTestCase(unittest.TestCase):
|
|||||||
if line.strip()
|
if line.strip()
|
||||||
]
|
]
|
||||||
|
|
||||||
self.assertEqual(config.get('tryton', 'version'), '8.0.2')
|
self.assertEqual(config.get('tryton', 'version'), '8.0.3')
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
set(lines('tryton', 'depends')),
|
set(lines('tryton', 'depends')),
|
||||||
{'account', 'ir'})
|
{'account', 'ir'})
|
||||||
@@ -581,11 +584,14 @@ class AccountDoUnitTestCase(unittest.TestCase):
|
|||||||
'tax_code_do_es_419.xml',
|
'tax_code_do_es_419.xml',
|
||||||
'tax_rule_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'])
|
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())
|
||||||
self.assertTrue((MODULE_DIR / 'account.py').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')
|
pyproject = (MODULE_DIR / 'pyproject.toml').read_text(encoding='utf-8')
|
||||||
self.assertIn("name = 'trytond_account_do'", pyproject)
|
self.assertIn("name = 'trytond_account_do'", pyproject)
|
||||||
self.assertIn(
|
self.assertIn(
|
||||||
|
|||||||
+3
-1
@@ -1,5 +1,5 @@
|
|||||||
[tryton]
|
[tryton]
|
||||||
version=8.0.2
|
version=8.0.3
|
||||||
depends:
|
depends:
|
||||||
account
|
account
|
||||||
ir
|
ir
|
||||||
@@ -16,5 +16,7 @@ xml:
|
|||||||
[register]
|
[register]
|
||||||
model:
|
model:
|
||||||
ir.ModelData
|
ir.ModelData
|
||||||
|
tax.TaxTemplate
|
||||||
|
tax.Tax
|
||||||
wizard:
|
wizard:
|
||||||
account.CreateChart
|
account.CreateChart
|
||||||
|
|||||||
Reference in New Issue
Block a user