From e646947a6e55e7d26a346dee17080cbfa3b657c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Arturo=20Garc=C3=ADa?= Date: Mon, 10 Aug 2026 08:42:39 -0400 Subject: [PATCH] Harden localization data and validation --- CHANGELOG | 2 + account.py | 18 +++---- tax_code_do_en.xml | 5 -- tax_code_do_es_419.xml | 4 -- tax_do_en.xml | 103 ++++++++++++++++++++--------------------- tax_do_es_419.xml | 5 -- tests/test_module.py | 80 ++++++++++++++++++++++++++++---- 7 files changed, 132 insertions(+), 85 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 18fe554..200c48c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -12,3 +12,5 @@ Version 8.0.1 - 2026-08-09 technical assistance out of the 10% interest category. * Split tax rules whose duplicate match patterns made later alternatives unreachable. +* Remove unused tax-group and tax-code records from the initial data set. +* Translate every user-visible legal notice in the English tax catalog. diff --git a/account.py b/account.py index 68d0ec8..bd62cb9 100644 --- a/account.py +++ b/account.py @@ -3,6 +3,15 @@ from trytond.pool import Pool, PoolMeta +CHART_DEFAULT_ACCOUNTS = { + 'account_do.do_account_root_en': ( + 'account_do.do_account_110201_en', + 'account_do.do_account_210101_en'), + 'account_do.do_account_root_es_419': ( + 'account_do.do_account_110201_es_419', + 'account_do.do_account_210101_es_419'), + } + class CreateChart(metaclass=PoolMeta): __name__ = 'account.create_chart' @@ -12,14 +21,7 @@ class CreateChart(metaclass=PoolMeta): ModelData = pool.get('ir.model.data') defaults = super().default_properties(fields) chart_defaults = {} - for root, accounts in [ - ('account_do.do_account_root_en', ( - 'account_do.do_account_110201_en', - 'account_do.do_account_210101_en')), - ('account_do.do_account_root_es_419', ( - 'account_do.do_account_110201_es_419', - 'account_do.do_account_210101_es_419')), - ]: + for root, accounts in CHART_DEFAULT_ACCOUNTS.items(): try: chart_defaults[ModelData.get_id(root)] = accounts except KeyError: diff --git a/tax_code_do_en.xml b/tax_code_do_en.xml index 36c66f0..0d5303e 100644 --- a/tax_code_do_en.xml +++ b/tax_code_do_en.xml @@ -14,11 +14,6 @@ - - Dominican Republic Taxes - - - ITBIS — Net Balance (Debit − Credit) diff --git a/tax_code_do_es_419.xml b/tax_code_do_es_419.xml index c6dbfbf..3077b53 100644 --- a/tax_code_do_es_419.xml +++ b/tax_code_do_es_419.xml @@ -1,10 +1,6 @@ - - Impuestos República Dominicana - - ITBIS — Balance Neto (Débito − Crédito) diff --git a/tax_do_en.xml b/tax_do_en.xml index 00b0e00..9e262b5 100644 --- a/tax_do_en.xml +++ b/tax_do_en.xml @@ -12,11 +12,6 @@ ISR both - - Exempt / Zero Rate - EX0 - both - ISC - Selective Consumer ISC @@ -53,7 +48,7 @@ - Arts. 335-392 CT Ley 11-92; Ley 253-12 Art. 10; Decreto 293-11 + Tax Code Arts. 335-392; Law 253-12 Art. 10; Decree 293-11 ITBIS 16% — Special Rate Sales (Legal and Natural Persons) @@ -64,7 +59,7 @@ - Art. 345 CT Ley 11-92 mod. Ley 253-12 — tasa especial 16% + Tax Code Art. 345, amended by Law 253-12 — special 16% rate ITBIS 18% — Purchases / Tax Credit (Legal and Natural Persons) @@ -75,7 +70,7 @@ - Arts. 349-357 CT Ley 11-92 — Crédito fiscal ITBIS; Decreto 293-11 + Tax Code Arts. 349-357 — ITBIS input tax credit; Decree 293-11 ITBIS 16% — Purchases / Tax Credit Special Rate @@ -86,7 +81,7 @@ - Art. 345 CT Ley 11-92 — Crédito fiscal tasa especial 16% + Tax Code Art. 345 — input tax credit at the special 16% rate Exempt ITBIS — Exempt Goods and Services (Legal and Natural Persons) @@ -94,7 +89,7 @@ none - Arts. 343-344 CT Ley 11-92; Ley 288-04; NG 12-22 DGII + Tax Code Arts. 343-344; Law 288-04; DGII General Rule 12-22 ITBIS Zero Rate — Exports and Free Zone (Legal and Natural Persons) @@ -105,7 +100,7 @@ - Art. 342 CT; Ley 557-05; Ley 8-90 Zonas Francas; NG 05-19 DGII + Tax Code Art. 342; Laws 557-05 and 8-90; DGII General Rule 05-19 @@ -117,7 +112,7 @@ - NG 01-11 DGII 2011; Arts. 309 y 337 CT — Grandes Contribuyentes + DGII General Rule 01-11; Tax Code Arts. 309 and 337 — large taxpayers @@ -129,7 +124,7 @@ - Art. 309 CT Ley 11-92; Decreto 95-12 — Honorarios personas jurídicas + Tax Code Art. 309; Decree 95-12 — fees paid to legal entities ISR Withholding 10% — Fees and Services to Individuals @@ -140,7 +135,7 @@ - Art. 309 CT Ley 11-92; Decreto 95-12 — Honorarios personas físicas + Tax Code Art. 309; Decree 95-12 — fees paid to individuals ISR Withholding 10% — Dividends and Distributed Profits (Individuals and Legal Entities) @@ -151,7 +146,7 @@ - Art. 308 CT Ley 11-92; Decreto 95-12; Formulario IR-18 + Tax Code Art. 308; Decree 95-12; DGII Form IR-18 ISR Withholding 10% — Interest to Individuals (NG 07-19) @@ -162,7 +157,7 @@ - NG 07-19 DGII; Art. 306 bis CT — Intereses personas físicas, definitivo + DGII General Rule 07-19; Tax Code Art. 306 bis — interest paid to individuals ISR Withholding 10% — Rentals and Leases (Natural and Legal Persons) @@ -173,7 +168,7 @@ - Art. 309 CT; NG 08-11 DGII — Alquileres personas físicas y jurídicas + Tax Code Art. 309; DGII General Rule 08-11 — rentals and leases ISR Withholding 1.5% — State/Public Sector Payments (Natural and Legal Persons) @@ -184,7 +179,7 @@ - Art. 309 CT; Decreto 95-12; Formulario 623 DGII — Sector público + Tax Code Art. 309; Decree 95-12; DGII Form 623 — public sector ISR Withholding 5% — State/Public Sector Payments (Natural and Legal Persons) @@ -195,7 +190,7 @@ - Art. 309 CT mod. Ley 253-12 — retención pagos del Estado 5% + Tax Code Art. 309, amended by Law 253-12 — 5% on state payments ISR Withholding 1% — Bovine Purchases from Unregistered Individuals @@ -207,7 +202,7 @@ - NG 04-2025 arts. 2, 4, 5 y 11 — 1% del monto facturado + DGII General Rule 04-2025 Arts. 2, 4, 5 and 11 — 1% of billed amount ISR Withholding 2.5% — Exporters in Sales to the Local Market @@ -218,7 +213,7 @@ - NG 15-07 DGII — retención 2.5% ventas locales de exportadores + DGII General Rule 15-07 — 2.5% on exporters' domestic sales ISR Withholding 1% — Interest to Legal Entities (NG 07-19) @@ -229,7 +224,7 @@ - NG 07-19 DGII 2019; Art. 306 bis CT — Intereses personas jurídicas + DGII General Rule 07-19; Tax Code Art. 306 bis — interest paid to legal entities ISR Withholding 25% — Lottery Prizes, Draws and Raffles (Natural and Legal Persons) @@ -240,7 +235,7 @@ - Art. 321 CT Ley 11-92; Decreto 95-12 — Premios y loterías, definitivo + Tax Code Art. 321; Decree 95-12 — prizes and lotteries, final payment ISR Withholding 10% — Awards RD$100,001 to RD$500,000 @@ -251,7 +246,7 @@ - Art. 309 CT mod. Ley 253-12 — premios RD$100,001 a RD$500,000 10% + Tax Code Art. 309, amended by Law 253-12 — prizes from RD$100,001 to RD$500,000 ISR Withholding 15% — Awards RD$500,001 to RD$1,000,000 @@ -262,7 +257,7 @@ - Art. 309 CT mod. Ley 253-12 — premios RD$500,001 a RD$1,000,000 15% + Tax Code Art. 309, amended by Law 253-12 — prizes from RD$500,001 to RD$1,000,000 ISR Withholding 10% — Slot Machine Prizes @@ -273,7 +268,7 @@ - Art. 309 CT mod. Ley 253-12 — premios máquinas tragamonedas 10% + Tax Code Art. 309, amended by Law 253-12 — slot-machine prizes ISR Withholding 10% — Other Income Not Covered @@ -284,7 +279,7 @@ - Art. 309 CT mod. Ley 253-12 — otras rentas no contempladas 10% + Tax Code Art. 309, amended by Law 253-12 — other income not covered @@ -296,7 +291,7 @@ - Art. 305 CT Ley 11-92 — pagos gravados no sujetos a una tasa especial + Tax Code Art. 305 — taxable payments not subject to a special rate ISR Withholding 15% — Foreign Royalties, Software and Online Services @@ -332,7 +327,7 @@ - NG 01-11 DGII — Retención 100% ITBIS en servicios gravados + DGII General Rule 01-11 — 100% of ITBIS on taxable services @@ -345,7 +340,7 @@ - NG 08-10 DGII — Retención 75% ITBIS compras a proveedores informales + DGII General Rule 08-10 — 75% of ITBIS on informal-supplier purchases ITBIS Withholding 75% — Goods Informal Supplier Rate 16% @@ -356,7 +351,7 @@ - IT-1 DGII casilla 57; NG 08-10/05-19 — 75% de ITBIS tasa 16% + DGII Form IT-1 box 57; General Rules 08-10 and 05-19 — 75% at the 16% rate ITBIS Withholding 100% — Goods Informal Supplier Rate 18% @@ -367,7 +362,7 @@ - IT-1 DGII casilla 56 — bienes comprobante de compras, retención 100% + DGII Form IT-1 box 56 — goods supported by a purchase receipt, 100% withholding ITBIS Withholding 100% — Goods Informal Supplier Rate 16% @@ -378,7 +373,7 @@ - IT-1 DGII casilla 57 — bienes comprobante de compras, retención 100% + DGII Form IT-1 box 57 — goods supported by a purchase receipt, 100% withholding ITBIS Withholding 100% — RST Taxpayers Rate 18% @@ -389,7 +384,7 @@ - IT-1 DGII casilla 53 — ITBIS retenido RST operaciones gravadas 18% + DGII Form IT-1 box 53 — ITBIS withheld from RST taxable transactions at 18% ITBIS Withholding 100% — RST Taxpayers Rate 16% @@ -400,7 +395,7 @@ - IT-1 DGII casilla 54 — ITBIS retenido RST operaciones gravadas 16% + DGII Form IT-1 box 54 — ITBIS withheld from RST taxable transactions at 16% ITBIS Withholding 100% — Insurance Companies @@ -411,7 +406,7 @@ - NG 04-13 DGII — aseguradoras retienen 100% del ITBIS facturado + DGII General Rule 04-13 — insurers withhold 100% of billed ITBIS ITBIS Withheld by Airlines 100% — BSP/IATA @@ -422,7 +417,7 @@ - IT-1 DGII casilla 28; NG 02-05 — ITBIS retenido por aerolíneas + DGII Form IT-1 box 28; General Rule 02-05 — ITBIS withheld by airlines ITBIS Withheld by Companies 30% — Withholding Suffered @@ -433,7 +428,7 @@ - IT-1 DGII casilla 29; NG 02-05/07-09 — sociedades retienen ITBIS + DGII Form IT-1 box 29; General Rules 02-05 and 07-09 — ITBIS withheld by companies ITBIS Withheld by Hotels 100% — Package Commissions @@ -444,7 +439,7 @@ - IT-1 DGII casilla 30; NG 02-05 — hoteles retienen ITBIS en comisiones + DGII Form IT-1 box 30; General Rule 02-05 — ITBIS withheld by hotels on commissions ITBIS Withheld by State Entities 100% @@ -455,7 +450,7 @@ - IT-1 DGII casilla 31 — ITBIS retenido por instituciones del Estado + DGII Form IT-1 box 31 — ITBIS withheld by state institutions @@ -467,7 +462,7 @@ - Ley 253-12 mod. Art. 375 CT — ISC bebidas alcohólicas ad valorem 10% + Law 253-12, amending Tax Code Art. 375 — 10% ad valorem ISC on alcoholic beverages ISC 20% — Tobacco, Cigarettes and Derivatives @@ -478,7 +473,7 @@ - Arts. 393-441 CT Ley 11-92 — ISC tabaco y cigarrillos, ad valorem 20% + Tax Code Arts. 393-441 — 20% ad valorem ISC on tobacco and cigarettes ISC 10% — Telecommunications Services @@ -489,7 +484,7 @@ - Arts. 393-441 CT; Ley 253-12 — ISC servicios telecomunicaciones 10% + Tax Code Arts. 393-441; Law 253-12 — 10% ISC on telecommunications services ISC 16% — Fossil Fuels and Petroleum Derivatives @@ -500,7 +495,7 @@ - Ley 253-12 mod. Art. 367 CT — ISC combustibles fósiles 16% + Law 253-12, amending Tax Code Art. 367 — 16% ISC on fossil fuels ISC 6.5% — Avtur Reduced Rate @@ -511,7 +506,7 @@ - Ley 253-12 Art. 23 — tasa reducida Avtur 6.5% ad valorem + Law 253-12 Art. 23 — reduced 6.5% ad valorem rate for Avtur ISC RD$2.00 — Additional per Gallon Gasoline/Diesel @@ -522,7 +517,7 @@ - Ley 253-12 Art. 20 — adicional RD$2.00 por galón gasolina/gasoil + Law 253-12 Art. 20 — additional RD$2.00 per gallon of gasoline or diesel ISC Withholding 100% — Insurance Companies @@ -533,7 +528,7 @@ - NG 04-13 DGII — aseguradoras retienen 100% del ISC facturado + DGII General Rule 04-13 — insurers withhold 100% of billed ISC @@ -546,7 +541,7 @@ - Art. 22 Ley 557-05 — 17% sobre valor CIF para primera placa + Law 557-05 Art. 22 — 17% of CIF value on first registration @@ -589,7 +584,7 @@ - Art. 382-D CT Ley 11-92 mod. Ley 288-04 — 1.5 por mil cheques/débitos + Tax Code Art. 382-D, amended by Law 288-04 — 1.5 per thousand on checks and debits @@ -603,7 +598,7 @@ - Ley 30-26, vig. 2026-07-03 — 2.0 por mil cheques/transferencias + Law 30-26, effective 2026-07-03 — 2.0 per thousand on checks and transfers @@ -616,7 +611,7 @@ - Código Tributario, arts. 401-405; art. 404 — 1% anual + Tax Code Arts. 401-405; Art. 404 — 1% annually @@ -629,7 +624,7 @@ - Art. 20 Ley 288-04, mod. art. 7 Ley 173-07 — tasa 3% + Law 288-04 Art. 20, amended by Law 173-07 Art. 7 — 3% rate diff --git a/tax_do_es_419.xml b/tax_do_es_419.xml index f23443e..790d8f1 100644 --- a/tax_do_es_419.xml +++ b/tax_do_es_419.xml @@ -11,11 +11,6 @@ ISR both - - Exento / Tasa Cero - EX0 - both - ISC - Selectivo al Consumo ISC diff --git a/tests/test_module.py b/tests/test_module.py index 91696b7..1de6a38 100644 --- a/tests/test_module.py +++ b/tests/test_module.py @@ -306,7 +306,8 @@ class AccountDoTestCase(ModuleTestCase): '210218', '210219', '21020101', '21020102', '21020103', '21020104', '21020105', '21020201', '21020501', '21020502', - '21020601', '21020701', '21020702', '21020801', + '21020601', '21020701', '21020702', '21020703', + '21020801', '21020802', '21020803', '21020804', '21020805', '21020901', '21021001', '21021101', '21021201', '21021301', '21021302', '21021401', '21021501', @@ -372,6 +373,13 @@ class AccountDoTestCase(ModuleTestCase): 'ITBIS Withholding Informal Supplier 75%'), ('rate', '=', Decimal('-0.135')), ], limit=1)) + foreign_15, = Tax.search([ + ('company', '=', company.id), + ('description', '=', 'Foreign ISR Withholding 15%'), + ], limit=1) + self.assertEqual(foreign_15.rate, Decimal('-0.15')) + self.assertEqual( + foreign_15.start_date, datetime.date(2026, 7, 1)) check_015, = Tax.search([ ('company', '=', company.id), ('description', '=', 'Checks and Transfers Tax 0.15%'), @@ -398,6 +406,43 @@ class AccountDoTestCase(ModuleTestCase): 'date': datetime.date(2026, 7, 3), }), [check_020.id]) + foreign_27, = Tax.search([ + ('company', '=', company.id), + ('description', '=', 'Foreign ISR Withholding 27%'), + ], limit=1) + foreign_rule, = TaxRule.search([ + ('company', '=', company.id), + ('name', '=', ( + 'Foreign Royalties, Software and Online ' + 'Services 15% Rule (RD)')), + ], limit=1) + self.assertEqual( + foreign_rule.apply(foreign_27, { + 'date': datetime.date(2026, 6, 30), + }), + [foreign_27.id]) + self.assertEqual( + foreign_rule.apply(foreign_27, { + 'date': datetime.date(2026, 7, 1), + }), + [foreign_15.id]) + itbis_sale, = Tax.search([ + ('company', '=', company.id), + ('description', '=', 'ITBIS 18% Sales'), + ], limit=1) + society_withholding, = Tax.search([ + ('company', '=', company.id), + ('description', '=', 'ITBIS Withheld Companies 30%'), + ], limit=1) + society_rule, = TaxRule.search([ + ('company', '=', company.id), + ('name', '=', ( + 'ITBIS Withheld by Companies 30% Customer ' + 'Rule (RD)')), + ], limit=1) + self.assertEqual( + society_rule.apply(itbis_sale, {}), + [society_withholding.id, itbis_sale.id]) real_tax_code_signs = { ('ITBIS 18% Sales', 'invoice'): '+', ('ITBIS 18% Sales', 'credit'): '-', @@ -560,11 +605,11 @@ class AccountDoUnitTestCase(unittest.TestCase): 'account.account.template': 282, }, 'tax_do_en.xml': { - 'account.tax.group': 7, + 'account.tax.group': 6, 'account.tax.template': 51, }, 'tax_code_do_en.xml': { - 'account.tax.code.template': 44, + 'account.tax.code.template': 43, 'account.tax.code.line.template': 95, }, 'tax_rule_do_en.xml': { @@ -576,11 +621,11 @@ class AccountDoUnitTestCase(unittest.TestCase): 'account.account.template': 282, }, 'tax_do_es_419.xml': { - 'account.tax.group': 7, + 'account.tax.group': 6, 'account.tax.template': 51, }, 'tax_code_do_es_419.xml': { - 'account.tax.code.template': 44, + 'account.tax.code.template': 43, 'account.tax.code.line.template': 95, }, 'tax_rule_do_es_419.xml': { @@ -629,13 +674,17 @@ class AccountDoUnitTestCase(unittest.TestCase): 'tax_code_do_en.xml', 'tax_rule_do_en.xml'] for field in ET.parse(MODULE_DIR / filename).getroot().findall( './/field') - if field.get('name') in {'name', 'description'} and field.text) + if field.get('name') in { + 'name', 'description', 'legal_notice'} and field.text) for phrase in [ 'advance itbis', 'assets for right of use', 'charged for paying', 'clients tax rule', 'collected to pay', 'deterioration', 'in favor', 'itbis supported', 'other income and profits', 'physical persons', 'retention', 'withheld status', + ' aseguradoras ', ' bienes ', ' casilla ', + ' combustibles ', ' intereses ', ' ley ', ' pagos ', + ' personas ', ' premios ', ' retención ', ' tasa ', ]: with self.subTest(phrase=phrase): self.assertNotIn(phrase, text.lower()) @@ -722,6 +771,19 @@ class AccountDoUnitTestCase(unittest.TestCase): field.get('name'), ref)) self.assertEqual(missing, []) + def test_every_tax_group_is_used(self): + """Do not install tax groups that no tax template can produce.""" + records = list(_iter_xml_records('tax_do_en.xml')) + groups = { + record_id for _, record_id, model, _values in records + if model == 'account.tax.group' + } + used_groups = { + values['group'] for _, _, model, values in records + if model == 'account.tax.template' and values.get('group') + } + self.assertEqual(groups, used_groups) + def test_chart_template_codes_are_unique(self): accounts = [ values for _, _, model, values in _iter_xml_records( @@ -878,13 +940,13 @@ class AccountDoUnitTestCase(unittest.TestCase): taxes['do_tax_ret_isr_bovine_1']['start_date'], 'datetime.date(2025, 6, 20)') self.assertIn( - 'arts. 401-405; art. 404', + 'Tax Code Arts. 401-405; Art. 404', taxes['do_tax_activos_1']['legal_notice']) self.assertIn( - 'art. 7 Ley 173-07', + 'Law 173-07 Art. 7', taxes['do_tax_iti_3']['legal_notice']) self.assertIn( - 'art. 228', taxes['do_tax_propina_10']['legal_notice']) + 'Art. 228', taxes['do_tax_propina_10']['legal_notice']) self.assertEqual( taxes['do_tax_isc_vehiculos']['group'], 'do_tax_group_others')