Update Dominican withholding catalog
Apply the Article 309 rates introduced by Law 30-26 with dated historical templates and rules. Complete tax-code reversals, improve English terminology, harden migrations, and expand structural and functional tests.
This commit is contained in:
+120
-13
@@ -310,8 +310,10 @@ class AccountDoTestCase(ModuleTestCase):
|
||||
'21020801',
|
||||
'21020802', '21020803', '21020804', '21020805',
|
||||
'21020901', '21021001', '21021101', '21021201',
|
||||
'21021301', '21021302', '21021401', '21021501',
|
||||
'21021502', '21021701', '21021801', '21021901',
|
||||
'21020302', '21021301', '21021302', '21021303',
|
||||
'21021401', '21021402', '21021501',
|
||||
'21021502', '21021701', '21021705', '21021706',
|
||||
'21021801', '21021901',
|
||||
'21021902', '6208']:
|
||||
with self.subTest(code=code):
|
||||
self.assertTrue(Account.search([
|
||||
@@ -426,6 +428,28 @@ class AccountDoTestCase(ModuleTestCase):
|
||||
'date': datetime.date(2026, 7, 1),
|
||||
}),
|
||||
[foreign_15.id])
|
||||
service_10, = Tax.search([
|
||||
('company', '=', company.id),
|
||||
('description', '=', 'ISR Withholding Individual 10%'),
|
||||
], limit=1)
|
||||
service_15, = Tax.search([
|
||||
('company', '=', company.id),
|
||||
('description', '=', 'ISR Withholding Individual 15%'),
|
||||
], limit=1)
|
||||
service_rule, = TaxRule.search([
|
||||
('company', '=', company.id),
|
||||
('name', '=', 'Individual Services ISR Rule (RD)'),
|
||||
], limit=1)
|
||||
self.assertEqual(
|
||||
service_rule.apply(service_10, {
|
||||
'date': datetime.date(2026, 6, 30),
|
||||
}),
|
||||
[service_10.id])
|
||||
self.assertEqual(
|
||||
service_rule.apply(service_10, {
|
||||
'date': datetime.date(2026, 7, 1),
|
||||
}),
|
||||
[service_15.id])
|
||||
itbis_sale, = Tax.search([
|
||||
('company', '=', company.id),
|
||||
('description', '=', 'ITBIS 18% Sales'),
|
||||
@@ -452,6 +476,8 @@ class AccountDoTestCase(ModuleTestCase):
|
||||
('ITBIS Withholding Large Taxpayer 30%', 'credit'): '+',
|
||||
('ISR Withholding Individual 10%', 'invoice'): '-',
|
||||
('ISR Withholding Individual 10%', 'credit'): '+',
|
||||
('ISR Withholding Individual 15%', 'invoice'): '-',
|
||||
('ISR Withholding Individual 15%', 'credit'): '+',
|
||||
('ISR Withholding State 1.5%', 'invoice'): '+',
|
||||
('ISR Withholding State 1.5%', 'credit'): '-',
|
||||
('Checks and Transfers Tax 0.20%', 'invoice'): '+',
|
||||
@@ -514,6 +540,8 @@ class SpanishAccountDoTestCase(ModuleTestCase):
|
||||
}])
|
||||
|
||||
ModelData._migrate_localization_identifiers()
|
||||
# A retried module update must be harmless.
|
||||
ModelData._migrate_localization_identifiers()
|
||||
|
||||
self.assertEqual(
|
||||
ModelData.get_id('account_do', 'do_account_root_es_419'),
|
||||
@@ -597,40 +625,51 @@ class AccountDoUnitTestCase(unittest.TestCase):
|
||||
'doc/releases.rst',
|
||||
]:
|
||||
self.assertTrue((MODULE_DIR / filename).is_file())
|
||||
self.assertNotIn(
|
||||
'Solu' + 'tema',
|
||||
'\n'.join(
|
||||
path.read_text(encoding='utf-8')
|
||||
for path in MODULE_DIR.rglob('*')
|
||||
if path.is_file()
|
||||
and '.git' not in path.parts
|
||||
and 'dist' not in path.parts
|
||||
and (path.suffix in {'.py', '.rst', '.toml', '.xml'}
|
||||
or path.name in {
|
||||
'CHANGELOG', 'COPYRIGHT', 'tryton.cfg'})))
|
||||
|
||||
def test_xml_record_inventory_is_explicit(self):
|
||||
expected = {
|
||||
'account_chart_do_en.xml': {
|
||||
'account.account.type.template': 36,
|
||||
'account.account.template': 282,
|
||||
'account.account.template': 287,
|
||||
},
|
||||
'tax_do_en.xml': {
|
||||
'account.tax.group': 6,
|
||||
'account.tax.template': 51,
|
||||
'account.tax.template': 56,
|
||||
},
|
||||
'tax_code_do_en.xml': {
|
||||
'account.tax.code.template': 43,
|
||||
'account.tax.code.line.template': 95,
|
||||
'account.tax.code.template': 44,
|
||||
'account.tax.code.line.template': 112,
|
||||
},
|
||||
'tax_rule_do_en.xml': {
|
||||
'account.tax.rule.template': 28,
|
||||
'account.tax.rule.line.template': 31,
|
||||
'account.tax.rule.line.template': 34,
|
||||
},
|
||||
'account_chart_do_es_419.xml': {
|
||||
'account.account.type.template': 36,
|
||||
'account.account.template': 282,
|
||||
'account.account.template': 287,
|
||||
},
|
||||
'tax_do_es_419.xml': {
|
||||
'account.tax.group': 6,
|
||||
'account.tax.template': 51,
|
||||
'account.tax.template': 56,
|
||||
},
|
||||
'tax_code_do_es_419.xml': {
|
||||
'account.tax.code.template': 43,
|
||||
'account.tax.code.line.template': 95,
|
||||
'account.tax.code.template': 44,
|
||||
'account.tax.code.line.template': 112,
|
||||
},
|
||||
'tax_rule_do_es_419.xml': {
|
||||
'account.tax.rule.template': 28,
|
||||
'account.tax.rule.line.template': 31,
|
||||
'account.tax.rule.line.template': 34,
|
||||
},
|
||||
}
|
||||
for filename, expected_counts in expected.items():
|
||||
@@ -689,6 +728,20 @@ class AccountDoUnitTestCase(unittest.TestCase):
|
||||
with self.subTest(phrase=phrase):
|
||||
self.assertNotIn(phrase, text.lower())
|
||||
|
||||
def test_english_xml_comments_are_in_english(self):
|
||||
text = '\n'.join(
|
||||
(MODULE_DIR / filename).read_text(encoding='utf-8').lower()
|
||||
for filename in [
|
||||
'account_chart_do_en.xml', 'tax_do_en.xml',
|
||||
'tax_code_do_en.xml', 'tax_rule_do_en.xml'])
|
||||
for marker in [
|
||||
'á', 'é', 'í', 'ó', 'ú', 'ñ',
|
||||
' cuenta ', ' impuesto ', ' proveedor ', ' regla ',
|
||||
' retención ', ' servicios ', ' tasa ',
|
||||
]:
|
||||
with self.subTest(marker=marker):
|
||||
self.assertNotIn(marker, text)
|
||||
|
||||
def test_spanish_chart_has_complete_parallel_identifiers(self):
|
||||
pairs = [
|
||||
('account_chart_do_en.xml', 'account_chart_do_es_419.xml'),
|
||||
@@ -729,6 +782,9 @@ class AccountDoUnitTestCase(unittest.TestCase):
|
||||
self.assertEqual(
|
||||
spanish_accounts['do_account_110201_es_419']['name'],
|
||||
'Cuentas por cobrar clientes')
|
||||
self.assertEqual(
|
||||
spanish_accounts['do_account_110406_es_419']['name'],
|
||||
'ITBIS retenido en adquisiciones a recuperar')
|
||||
self.assertEqual(
|
||||
spanish_taxes['do_tax_itbis_18_venta_es_419']['description'],
|
||||
'ITBIS 18% Ventas')
|
||||
@@ -891,7 +947,7 @@ class AccountDoUnitTestCase(unittest.TestCase):
|
||||
'account_chart_do_en.xml')
|
||||
if model == 'account.account.template'
|
||||
}
|
||||
self.assertEqual(len(taxes), 51)
|
||||
self.assertEqual(len(taxes), 56)
|
||||
for record_id, values in taxes.items():
|
||||
with self.subTest(record_id=record_id):
|
||||
self.assertEqual(
|
||||
@@ -939,6 +995,26 @@ class AccountDoUnitTestCase(unittest.TestCase):
|
||||
self.assertEqual(
|
||||
taxes['do_tax_ret_isr_bovine_1']['start_date'],
|
||||
'datetime.date(2025, 6, 20)')
|
||||
for old_id in [
|
||||
'do_tax_ret_isr_serv_10',
|
||||
'do_tax_ret_isr_alq_10',
|
||||
'do_tax_ret_isr_premios_10',
|
||||
'do_tax_ret_isr_premios_15',
|
||||
'do_tax_ret_isr_tragamonedas_10',
|
||||
'do_tax_ret_isr_other_income_10',
|
||||
]:
|
||||
self.assertEqual(
|
||||
taxes[old_id]['end_date'], 'datetime.date(2026, 6, 30)')
|
||||
for current_id in [
|
||||
'do_tax_ret_isr_serv_15',
|
||||
'do_tax_ret_isr_alq_15',
|
||||
'do_tax_ret_isr_betting_15',
|
||||
'do_tax_ret_isr_tragamonedas_15',
|
||||
'do_tax_ret_isr_other_income_15',
|
||||
]:
|
||||
self.assertEqual(
|
||||
taxes[current_id]['start_date'],
|
||||
'datetime.date(2026, 7, 1)')
|
||||
self.assertIn(
|
||||
'Tax Code Arts. 401-405; Art. 404',
|
||||
taxes['do_tax_activos_1']['legal_notice'])
|
||||
@@ -1036,6 +1112,37 @@ class AccountDoUnitTestCase(unittest.TestCase):
|
||||
self.assertEqual(lines['do_tcl_isc_comb_inv']['operator'], '+')
|
||||
self.assertEqual(lines['do_tcl_prop_inv']['code'], 'do_tc_others_tip')
|
||||
|
||||
def test_tax_code_lines_reverse_credit_notes(self):
|
||||
"""Every reported amount must have an inverse credit-note line."""
|
||||
pairs = {}
|
||||
for _, record_id, model, values in _iter_xml_records(
|
||||
'tax_code_do_en.xml'):
|
||||
if model != 'account.tax.code.line.template':
|
||||
continue
|
||||
key = (values['tax'], values['amount'])
|
||||
pair = pairs.setdefault(key, {})
|
||||
self.assertNotIn(values['type'], pair)
|
||||
pair[values['type']] = (
|
||||
record_id, values['operator'])
|
||||
for key, lines in pairs.items():
|
||||
with self.subTest(tax=key[0], amount=key[1]):
|
||||
self.assertEqual(set(lines), {'invoice', 'credit'})
|
||||
self.assertNotEqual(
|
||||
lines['invoice'][1], lines['credit'][1])
|
||||
|
||||
def test_every_tax_is_reported_by_a_tax_code(self):
|
||||
taxes = {
|
||||
record_id for _, record_id, model, _values
|
||||
in _iter_xml_records('tax_do_en.xml')
|
||||
if model == 'account.tax.template'
|
||||
}
|
||||
reported = {
|
||||
values['tax'] for _, _, model, values
|
||||
in _iter_xml_records('tax_code_do_en.xml')
|
||||
if model == 'account.tax.code.line.template'
|
||||
}
|
||||
self.assertEqual(taxes, reported)
|
||||
|
||||
def test_tax_rule_templates_include_date_sensitive_bank_tax(self):
|
||||
rules = {
|
||||
record_id: values
|
||||
|
||||
Reference in New Issue
Block a user