diff --git a/CHANGELOG b/CHANGELOG index 8dbd85f..0ef2eed 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -8,3 +8,7 @@ Version 8.1.0 - 2026-08-10 (``es_419``) accounting charts, pending availability of ``es_DO``. * Preserve 8.0.0 chart records when migrating to language-scoped identifiers. * Normalize the English chart to standard accounting terminology. +* Add the 15% foreign-payment withholding introduced by Law 30-26 and keep + technical assistance out of the 10% interest category. +* Split tax rules whose duplicate match patterns made later alternatives + unreachable. diff --git a/account_chart_do_en.xml b/account_chart_do_en.xml index 8cdcd56..18037df 100644 --- a/account_chart_do_en.xml +++ b/account_chart_do_en.xml @@ -1716,13 +1716,19 @@ 21020701 - Foreign ISR services and royalties 27% payable + Foreign ISR other taxable payments 27% payable 21020702 - Foreign ISR interest and assistance 10% payable + Foreign ISR interest 10% payable + + + + + 21020703 + Foreign ISR royalties, software and online services 15% payable diff --git a/account_chart_do_es_419.xml b/account_chart_do_es_419.xml index 01394d5..0efec60 100644 --- a/account_chart_do_es_419.xml +++ b/account_chart_do_es_419.xml @@ -1708,13 +1708,19 @@ 21020701 - ISR exterior servicios y regalías 27% por pagar + ISR exterior otros pagos gravados 27% por pagar 21020702 - ISR exterior intereses y asistencia 10% por pagar + ISR exterior intereses 10% por pagar + + + + + 21020703 + ISR exterior regalías, software y servicios en línea 15% por pagar @@ -2057,4 +2063,4 @@ - \ No newline at end of file + diff --git a/doc/core_readiness.rst b/doc/core_readiness.rst index 095322a..38175ea 100644 --- a/doc/core_readiness.rst +++ b/doc/core_readiness.rst @@ -19,7 +19,7 @@ the reviewer and review date rather than claiming certification in module data. Chart Size and Provenance ------------------------- -The chart has 281 templates: 70 grouping nodes and 211 posting leaves. Of the +The chart has 282 templates: 70 grouping nodes and 212 posting leaves. Of the posting leaves, 61 are tax control accounts (45 payable and 16 recoverable). Those controls deliberately provide stable identifiers for accounting and reporting modules. The remaining leaves cover cash, receivables, payables, @@ -36,7 +36,7 @@ designed Tryton baseline using these objective inclusion rules: * separate presentation is needed for a documented IFRS accounting policy. Grouping nodes are closed and cannot receive postings. The test suite checks -281 unique templates, required optional-module accounts, account types and the +282 unique templates, required optional-module accounts, account types and the IFRS policy coverage map. This is a transparent functional justification, which is the appropriate evidence where no official general chart exists. @@ -45,7 +45,7 @@ accounts. That is not decidable from Dominican law because no mandatory general catalogue supplies a target count. If requested during review, the safe reduction is to merge control accounts only after confirming that ``dgii_reports`` does not depend on their stable identifiers; claiming that -281 codes are official would be incorrect. +282 codes are official would be incorrect. Upstream Repository ------------------- diff --git a/doc/sources.rst b/doc/sources.rst index 23c324d..a1c910a 100644 --- a/doc/sources.rst +++ b/doc/sources.rst @@ -56,8 +56,10 @@ Official References `_ * `General Rule 04-2025 (bovine subsector withholding) `_ -* `Law 30-26 (bank checks and electronic transfers) +* `Law 30-26 (foreign payments, bank checks and electronic transfers) `_ +* `DGII implementation calendar for Law 30-26 + `_ Review Record ------------- diff --git a/doc/tax_blueprint.rst b/doc/tax_blueprint.rst index 0dc2d47..44cdf01 100644 --- a/doc/tax_blueprint.rst +++ b/doc/tax_blueprint.rst @@ -87,6 +87,13 @@ The check and electronic-transfer templates preserve 0.15% through 2 July 2026 and 0.20% from 3 July 2026 under Law 30-26. Their tax rule switches rates by accounting date. +Law 30-26 also introduced articles 305-1 and 305-2 of the Tax Code. From +1 July 2026, royalties or rights, software licences, online advertising and +the right to use or store data paid abroad are subject to a distinct 15% +withholding. Other taxable payments abroad remain at 27%, while article 306 +interest remains at 10%. Technical assistance must therefore not be +classified as article 306 interest. + Validation Boundary ------------------- diff --git a/doc/tax_validation.rst b/doc/tax_validation.rst index 0783f3e..ead4f8a 100644 --- a/doc/tax_validation.rst +++ b/doc/tax_validation.rst @@ -62,7 +62,8 @@ ISR Withholdings * ``do_tax_ret_isr_premios_15`` — 15% qualifying prize band. * ``do_tax_ret_isr_tragamonedas_10`` — 10% slot-machine prize case. * ``do_tax_ret_isr_other_income_10`` — 10% residual income case under article 309. -* ``do_tax_ret_isr_ext_27`` — 27% qualifying service or royalty paid abroad. +* ``do_tax_ret_isr_ext_27`` — 27% qualifying payment abroad not governed by a special rate. +* ``do_tax_ret_isr_ext_15`` — 15% royalties or rights, software licences, online advertising and data use or storage paid abroad from 1 July 2026. * ``do_tax_ret_isr_ext_10`` — 10% qualifying interest paid abroad. Selective and Sector Charges diff --git a/tax_code_do_en.xml b/tax_code_do_en.xml index 0b6fa94..36c66f0 100644 --- a/tax_code_do_en.xml +++ b/tax_code_do_en.xml @@ -740,6 +740,22 @@ credit + + + + - + + tax + invoice + + + + + + + tax + credit + + diff --git a/tax_code_do_es_419.xml b/tax_code_do_es_419.xml index 57391cf..c6dbfbf 100644 --- a/tax_code_do_es_419.xml +++ b/tax_code_do_es_419.xml @@ -661,6 +661,20 @@ tax credit + + + - + + tax + invoice + + + + + + + tax + credit + - @@ -872,4 +886,4 @@ credit - \ No newline at end of file + diff --git a/tax_do_en.xml b/tax_do_en.xml index 9ec5373..00b0e00 100644 --- a/tax_do_en.xml +++ b/tax_do_en.xml @@ -288,7 +288,7 @@ - ISR Withholding 27% — Payments Abroad: Services and Royalties (Non-Residents) + ISR Withholding 27% — Other Taxable Payments Abroad (Non-Residents) Foreign ISR Withholding 27% percentage @@ -296,10 +296,22 @@ - Art. 305 CT Ley 11-92 — Servicios y regalías a no residentes 27% + Art. 305 CT Ley 11-92 — pagos gravados no sujetos a una tasa especial + + + ISR Withholding 15% — Foreign Royalties, Software and Online Services + Foreign ISR Withholding 15% + percentage + + + + + + + Arts. 305-1 and 305-2 CT, introduced by Law 30-26 — royalties or rights, software licences, online advertising and data use or storage - ISR Withholding 10% — Payments Abroad: Interest and Technical Assistance (Non-Residents) + ISR Withholding 10% — Interest Paid Abroad (Non-Residents) Foreign ISR Withholding 10% percentage @@ -307,7 +319,7 @@ - Arts. 305-306 CT — Intereses y asistencia técnica a no residentes + Art. 306 CT — interest paid or credited abroad diff --git a/tax_do_es_419.xml b/tax_do_es_419.xml index e9c3778..f23443e 100644 --- a/tax_do_es_419.xml +++ b/tax_do_es_419.xml @@ -279,7 +279,7 @@ Art. 309 CT mod. Ley 253-12 — otras rentas no contempladas 10% - Retención ISR 27% — Pagos al Exterior: Servicios y Regalías (No Residentes) + Retención ISR 27% — Otros Pagos Gravados al Exterior (No Residentes) Retención ISR Exterior 27% percentage @@ -287,10 +287,22 @@ - Art. 305 CT Ley 11-92 — Servicios y regalías a no residentes 27% + Art. 305 CT Ley 11-92 — pagos gravados no sujetos a una tasa especial + + + Retención ISR 15% — Regalías, Software y Servicios en Línea al Exterior + Retención ISR Exterior 15% + percentage + + + + + + + Arts. 305-1 y 305-2 CT, introducidos por Ley 30-26 — regalías o derechos, licencias de software, publicidad en línea y uso o almacenamiento de datos - Retención ISR 10% — Pagos al Exterior: Intereses y Asistencia Técnica (No Residentes) + Retención ISR 10% — Intereses Pagados al Exterior (No Residentes) Retención ISR Exterior 10% percentage @@ -298,7 +310,7 @@ - Arts. 305-306 CT — Intereses y asistencia técnica a no residentes + Art. 306 CT — intereses pagados o acreditados al exterior Retención ITBIS 100% — Servicios Gravados Sujetos a Retención diff --git a/tax_rule_do_en.xml b/tax_rule_do_en.xml index f60c99f..b0a8c22 100644 --- a/tax_rule_do_en.xml +++ b/tax_rule_do_en.xml @@ -67,6 +67,18 @@ + + Foreign Royalties, Software and Online Services 15% Rule (RD) + purchase + + + + + + + + + @@ -219,11 +231,16 @@ + + ISC Rule Additional RD$2 per Gallon of Fuel (RD) + sale + + - + - + @@ -299,38 +316,53 @@ - - Customer Rule with ITBIS Sectoral Withholding (RD) + + ITBIS Withheld by Companies 30% Customer Rule (RD) sale - + + + ITBIS Withheld by Airlines Customer Rule (RD) + sale + + - + - + + + + ITBIS Withheld by Hotels Customer Rule (RD) + sale + - + - + + + + ITBIS Withheld by State Entities Customer Rule (RD) + sale + - + - + diff --git a/tax_rule_do_es_419.xml b/tax_rule_do_es_419.xml index b8ffc32..7646185 100644 --- a/tax_rule_do_es_419.xml +++ b/tax_rule_do_es_419.xml @@ -56,6 +56,18 @@ + + Regla Regalías, Software y Servicios en Línea al Exterior 15% (RD) + purchase + + + + + + + + + Regla Clientes ITBIS 16% Tasa Especial (RD) sale @@ -202,11 +214,16 @@ + + Regla ISC Adicional RD$2 por Galón de Combustible (RD) + sale + + - + - + Regla Bancaria Cheques / Transferencias Electrónicas (RD) @@ -277,38 +294,53 @@ - - Regla Cliente con Retención Sectorial ITBIS (RD) + + Regla Cliente ITBIS Retenido por Sociedades 30% (RD) sale - + + + Regla Cliente ITBIS Retenido por Aerolíneas (RD) + sale + + - + - + + + + Regla Cliente ITBIS Retenido por Hoteles (RD) + sale + - + - + + + + Regla Cliente ITBIS Retenido por Instituciones del Estado (RD) + sale + - + - + diff --git a/tests/test_module.py b/tests/test_module.py index 943699a..2110f89 100644 --- a/tests/test_module.py +++ b/tests/test_module.py @@ -557,35 +557,35 @@ class AccountDoUnitTestCase(unittest.TestCase): expected = { 'account_chart_do_en.xml': { 'account.account.type.template': 36, - 'account.account.template': 281, + 'account.account.template': 282, }, 'tax_do_en.xml': { 'account.tax.group': 7, - 'account.tax.template': 50, + 'account.tax.template': 51, }, 'tax_code_do_en.xml': { 'account.tax.code.template': 44, - 'account.tax.code.line.template': 93, + 'account.tax.code.line.template': 95, }, 'tax_rule_do_en.xml': { - 'account.tax.rule.template': 23, - 'account.tax.rule.line.template': 30, + 'account.tax.rule.template': 28, + 'account.tax.rule.line.template': 31, }, 'account_chart_do_es_419.xml': { 'account.account.type.template': 36, - 'account.account.template': 281, + 'account.account.template': 282, }, 'tax_do_es_419.xml': { 'account.tax.group': 7, - 'account.tax.template': 50, + 'account.tax.template': 51, }, 'tax_code_do_es_419.xml': { 'account.tax.code.template': 44, - 'account.tax.code.line.template': 93, + 'account.tax.code.line.template': 95, }, 'tax_rule_do_es_419.xml': { - 'account.tax.rule.template': 23, - 'account.tax.rule.line.template': 30, + 'account.tax.rule.template': 28, + 'account.tax.rule.line.template': 31, }, } for filename, expected_counts in expected.items(): @@ -829,7 +829,7 @@ class AccountDoUnitTestCase(unittest.TestCase): 'account_chart_do_en.xml') if model == 'account.account.template' } - self.assertEqual(len(taxes), 50) + self.assertEqual(len(taxes), 51) for record_id, values in taxes.items(): with self.subTest(record_id=record_id): self.assertEqual( @@ -991,6 +991,12 @@ class AccountDoUnitTestCase(unittest.TestCase): self.assertEqual( lines['do_trline_supp_ext_isr27']['group'], 'do_tax_group_isr_ext') + self.assertEqual( + lines['do_trline_supp_ext_isr15']['tax'], + 'do_tax_ret_isr_ext_15') + self.assertEqual( + lines['do_trline_supp_ext_isr15']['start_date'], + 'datetime.date(2026, 7, 1)') self.assertEqual( lines['do_trline_bank_check_transfer_015']['origin_tax'], 'do_tax_cheques_015') @@ -1010,5 +1016,19 @@ class AccountDoUnitTestCase(unittest.TestCase): lines['do_trline_bank_check_transfer_020']['start_date'], 'datetime.date(2026, 7, 3)') + def test_tax_rule_lines_have_reachable_match_patterns(self): + """A rule must not contain two lines with the same match pattern.""" + patterns = {} + for _, record_id, model, values in _iter_xml_records( + 'tax_rule_do_en.xml'): + if model != 'account.tax.rule.line.template': + continue + pattern = tuple(values.get(field) for field in [ + 'rule', 'group', 'origin_tax', 'start_date', 'end_date']) + self.assertNotIn( + pattern, patterns, + msg=f'{record_id} is shadowed by {patterns.get(pattern)}') + patterns[pattern] = record_id + del ModuleTestCase