Files
account_do/tests/test_module.py
T

501 lines
22 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import unittest
from collections import Counter
from datetime import date
from decimal import Decimal
from pathlib import Path
from xml.etree import ElementTree as ET
from trytond.modules.account_do.tax import (
TAX_APPLICATION, TAX_FISCAL_TYPE, TAX_KIND)
from trytond.pool import Pool
from trytond.tests.test_tryton import ModuleTestCase, with_transaction
from trytond.transaction import Transaction
MODULE_DIR = Path(__file__).resolve().parent.parent
def _iter_xml_records(*filenames):
for filename in filenames:
path = MODULE_DIR / filename
root = ET.parse(path).getroot()
for record in root.findall('.//record'):
values = {
field.get('name'): (
field.get('ref') or field.get('eval') or field.text or '')
for field in record.findall('field')
}
yield filename, record.get('id'), record.get('model'), values
class AccountDoTestCase(ModuleTestCase):
"Test account_do module"
module = 'account_do'
@with_transaction()
def test_chart_template_is_loaded_by_default(self):
'Test Dominican chart template is loaded without Spanish language setup'
pool = Pool()
ModelData = pool.get('ir.model.data')
self.assertTrue(ModelData.get_id('account_do', 'do_account_root'))
@with_transaction()
def test_chart_creates_company_accounts_and_taxes(self):
'Test Dominican chart creates usable accounts and taxes'
pool = Pool()
Account = pool.get('account.account')
AccountTemplate = pool.get('account.account.template')
Company = pool.get('company.company')
Currency = pool.get('currency.currency')
ModelData = pool.get('ir.model.data')
Party = pool.get('party.party')
Tax = pool.get('account.tax')
TaxCode = pool.get('account.tax.code')
TaxRule = pool.get('account.tax.rule')
DgiiTaxMapping = pool.get('account.do.dgii.tax.mapping')
CreateChart = pool.get('account.create_chart', type='wizard')
currency, = Currency.create([{
'name': 'Dominican Peso',
'code': 'DOP',
'symbol': 'RD$',
'digits': 2,
'rounding': '0.01',
}])
party, = Party.create([{
'name': 'Empresa Dominicana',
}])
company, = Company.create([{
'party': party.id,
'currency': currency.id,
}])
template = AccountTemplate(ModelData.get_id(
'account_do', 'do_account_root'))
session_id, _start, _end = CreateChart.create()
chart = CreateChart(session_id)
chart.account.account_template = template
chart.account.company = company
chart.transition_create_account()
receivable, = Account.search([
('company', '=', company.id),
('code', '=', '110201'),
('type.receivable', '=', True),
('party_required', '=', True),
('closed', '!=', True),
], limit=1)
payable, = Account.search([
('company', '=', company.id),
('code', '=', '210101'),
('type.payable', '=', True),
('party_required', '=', True),
('closed', '!=', True),
], limit=1)
for code in [
'110401', '110406', '110407', '110408',
'11040101', '11040102', '11040103', '11040104',
'11040601', '11040801', '11040802', '11040803',
'210201', '210202', '210203', '210206', '210207',
'210208', '210209', '210210', '210211', '210212',
'210213', '210214', '210215', '210216', '210217',
'210218', '210219',
'21020101', '21020102', '21020103', '21020104',
'21020105', '21020201', '21020501', '21020502',
'21020601', '21020701', '21020702', '21020801',
'21020802', '21020803', '21020804', '21020805',
'21020901', '21021001', '21021101', '21021201',
'21021301', '21021302', '21021401', '21021501',
'21021502', '21021701', '21021801', '21021901',
'21021902', '6208']:
with self.subTest(code=code):
self.assertTrue(Account.search([
('company', '=', company.id),
('code', '=', code),
('closed', '!=', True),
], limit=1))
self.assertGreaterEqual(len(Account.search([
('company', '=', company.id),
])), 200)
self.assertGreaterEqual(len(Tax.search([
('company', '=', company.id),
])), 25)
self.assertTrue(TaxCode.search([
('company', '=', company.id),
('name', '=', 'ITBIS — Balance Neto (Débito Crédito)'),
], limit=1))
self.assertTrue(TaxRule.search([
('company', '=', company.id),
('kind', '=', 'sale'),
], limit=1))
self.assertTrue(TaxRule.search([
('company', '=', company.id),
('kind', '=', 'purchase'),
], limit=1))
self.assertEqual(DgiiTaxMapping.search_count([]), 55)
self.assertTrue(DgiiTaxMapping.search([
('tax_template', '=', ModelData.get_id(
'account_do', 'do_tax_cheques_020')),
('form', '=', 'ir2'),
('box', '=', 'financial_expenses_check_transfer_tax_020'),
('effective_from', '=', date(2026, 7, 3)),
], limit=1))
self.assertTrue(Tax.search([
('company', '=', company.id),
('description', '=', 'ITBIS 18% Ventas'),
('tax_kind', '=', 'itbis'),
('rate', '=', Decimal('0.18')),
], limit=1))
self.assertTrue(Tax.search([
('company', '=', company.id),
('description', '=', 'Retención ITBIS Gran Contribuyente 30%'),
('tax_kind', '=', 'itbis_withholding'),
('tax_fiscal_type', '=', 'itbis_withholding_30'),
('rate', '=', Decimal('-0.054')),
], limit=1))
self.assertTrue(Tax.search([
('company', '=', company.id),
('description', '=', 'Retención ITBIS Proveedor Informal 75%'),
('tax_kind', '=', 'itbis_withholding'),
('tax_fiscal_type', '=', 'itbis_withholding_75_informal'),
('rate', '=', Decimal('-0.135')),
], limit=1))
chart.properties.company = company
chart.properties.account_receivable = receivable
chart.properties.account_payable = payable
with Transaction().set_context(company=company.id):
chart.transition_create_properties()
CreateChart.delete(session_id)
class AccountDoUnitTestCase(unittest.TestCase):
def test_xml_references_are_resolved_inside_module(self):
records = list(_iter_xml_records(
'account_chart_do.xml',
'tax_do.xml',
'tax_code_do.xml',
'tax_rule_do.xml',
))
ids = {record_id for _, record_id, _, _ in records}
missing = []
for filename in [
'account_chart_do.xml',
'tax_do.xml',
'tax_code_do.xml',
'tax_rule_do.xml',
]:
root = ET.parse(MODULE_DIR / filename).getroot()
for record in root.findall('.//record'):
for field in record.findall('field'):
ref = field.get('ref')
if ref and '.' not in ref and ref not in ids:
missing.append(
(filename, record.get('id'), field.get('name'), ref))
self.assertEqual(missing, [])
def test_chart_template_codes_are_unique(self):
accounts = [
values for _, _, model, values in _iter_xml_records(
'account_chart_do.xml')
if model == 'account.account.template'
]
codes = [values['code'] for values in accounts if values.get('code')]
duplicates = [
code for code, count in Counter(codes).items() if count > 1]
self.assertEqual(duplicates, [])
def test_chart_root_and_statement_classification(self):
accounts = {
record_id: values
for _, record_id, model, values in _iter_xml_records(
'account_chart_do.xml')
if model == 'account.account.template'
}
types = {
record_id: values
for _, record_id, model, values in _iter_xml_records(
'account_chart_do.xml')
if model == 'account.account.type.template'
}
self.assertEqual([
accounts[f'do_account_{code}']['code']
for code in range(1, 8)
], ['1', '2', '3', '4', '5', '6', '7'])
self.assertEqual(
types['do_type_retained_earnings']['statement'], 'balance')
self.assertEqual(types['do_type_revenue']['statement'], 'income')
self.assertEqual(types['do_type_expense']['statement'], 'income')
self.assertEqual(
accounts['do_account_110201']['type'], 'do_type_receivable')
self.assertEqual(
accounts['do_account_210101']['type'], 'do_type_payable')
def test_no_spanish_technical_other_tax_ids_remain(self):
ids = {
record_id
for _, record_id, _, _ in _iter_xml_records(
'tax_do.xml', 'tax_code_do.xml')
}
self.assertFalse([record_id for record_id in ids if 'otros' in record_id])
def test_tax_templates_are_classified(self):
taxes = {
record_id: values
for _, record_id, model, values in _iter_xml_records('tax_do.xml')
if model == 'account.tax.template'
}
self.assertEqual(len(taxes), 55)
self.assertFalse([
record_id for record_id, values in taxes.items()
if not values.get('tax_kind')])
self.assertFalse([
record_id for record_id, values in taxes.items()
if not values.get('tax_fiscal_type')])
self.assertFalse([
record_id for record_id, values in taxes.items()
if not values.get('tax_application')])
self.assertEqual(
taxes['do_tax_itbis_18_venta']['invoice_account'],
'do_account_21020101')
self.assertEqual(
taxes['do_tax_itbis_18_compra']['invoice_account'],
'do_account_11040101')
expected_accounts = {
'do_tax_itbis_18_venta': 'do_account_21020101',
'do_tax_itbis_16_venta': 'do_account_21020102',
'do_tax_itbis_9_venta': 'do_account_21020103',
'do_tax_itbis_8_venta': 'do_account_21020104',
'do_tax_itbis_tasa_cero': 'do_account_21020105',
'do_tax_itbis_18_compra': 'do_account_11040101',
'do_tax_itbis_16_compra': 'do_account_11040102',
'do_tax_itbis_9_compra': 'do_account_11040103',
'do_tax_itbis_8_compra': 'do_account_11040104',
'do_tax_ret_itbis_2_adq': 'do_account_11040601',
'do_tax_ret_itbis_30': 'do_account_21021101',
'do_tax_ret_itbis_100_inf': 'do_account_21020201',
'do_tax_ret_itbis_75_inf': 'do_account_21021201',
'do_tax_ret_itbis_75_inf_16': 'do_account_21021202',
'do_tax_ret_itbis_100_goods_18': 'do_account_21021203',
'do_tax_ret_itbis_100_goods_16': 'do_account_21021204',
'do_tax_ret_itbis_rst_18': 'do_account_21020202',
'do_tax_ret_itbis_rst_16': 'do_account_21020203',
'do_tax_ret_itbis_insurance_100': 'do_account_21020204',
'do_tax_ret_itbis_airline_100': 'do_account_11040701',
'do_tax_ret_itbis_society_30_suf': 'do_account_11040702',
'do_tax_ret_itbis_hotel_100': 'do_account_11040703',
'do_tax_ret_itbis_state_100': 'do_account_11040704',
'do_tax_ret_isr_hon_5': 'do_account_21021301',
'do_tax_ret_isr_serv_10': 'do_account_21021302',
'do_tax_ret_isr_div_10': 'do_account_21020601',
'do_tax_ret_isr_int_10': 'do_account_21021501',
'do_tax_ret_isr_int_pj_1': 'do_account_21021502',
'do_tax_ret_isr_alq_10': 'do_account_21021401',
'do_tax_ret_isr_est_15': 'do_account_11040801',
'do_tax_ret_isr_est_5': 'do_account_11040802',
'do_tax_ret_isr_exporter_25': 'do_account_11040803',
'do_tax_ret_isr_bovine_1': 'do_account_21021801',
'do_tax_ret_isr_premios_25': 'do_account_21021701',
'do_tax_ret_isr_premios_10': 'do_account_21021702',
'do_tax_ret_isr_premios_15': 'do_account_21021703',
'do_tax_ret_isr_tragamonedas_10': 'do_account_21021704',
'do_tax_ret_isr_other_income_10': 'do_account_21020301',
'do_tax_ret_isr_ext_27': 'do_account_21020701',
'do_tax_ret_isr_ext_10': 'do_account_21020702',
'do_tax_isc_bebidas_alc': 'do_account_21020801',
'do_tax_isc_tabaco': 'do_account_21020802',
'do_tax_isc_telecom': 'do_account_21020803',
'do_tax_isc_combustibles_16': 'do_account_21020804',
'do_tax_isc_avtur_65': 'do_account_21020807',
'do_tax_isc_fuel_rd2_gallon': 'do_account_21020808',
'do_tax_ret_isc_insurance_100': 'do_account_21020806',
'do_tax_isc_vehiculos': 'do_account_21020805',
'do_tax_cdt_indotel': 'do_account_21020901',
'do_tax_propina_10': 'do_account_21020501',
'do_tax_cheques_015': 'do_account_21021901',
'do_tax_cheques_020': 'do_account_21021902',
'do_tax_activos_1': 'do_account_21020502',
'do_tax_iti_3': 'do_account_21021001',
}
for record_id, account_id in expected_accounts.items():
with self.subTest(record_id=record_id):
self.assertEqual(taxes[record_id]['invoice_account'], account_id)
self.assertEqual(
taxes[record_id]['credit_note_account'], account_id)
self.assertEqual(
taxes['do_tax_ret_itbis_30']['tax_kind'],
'itbis_withholding')
self.assertIn(
"Decimal('-5.4')/100",
taxes['do_tax_ret_itbis_30']['rate'])
self.assertEqual(
taxes['do_tax_ret_itbis_75_inf']['tax_fiscal_type'],
'itbis_withholding_75_informal')
self.assertIn(
"Decimal('-13.5')/100",
taxes['do_tax_ret_itbis_75_inf']['rate'])
for record_id in [
'do_tax_ret_isr_hon_5',
'do_tax_ret_isr_serv_10',
'do_tax_ret_isr_alq_10',
'do_tax_ret_isr_est_5',
'do_tax_ret_isr_bovine_1',
'do_tax_ret_isr_exporter_25',
'do_tax_ret_isr_ext_27',
]:
self.assertIn("Decimal('-", taxes[record_id]['rate'])
self.assertEqual(
taxes['do_tax_itbis_9_venta']['tax_fiscal_type'],
'itbis_sale_9')
self.assertIn(
"Decimal('9')/100",
taxes['do_tax_itbis_9_compra']['rate'])
self.assertEqual(
taxes['do_tax_isc_bebidas_alc']['tax_fiscal_type'],
'isc_alcohol_10')
self.assertIn(
"Decimal('10')/100",
taxes['do_tax_isc_bebidas_alc']['rate'])
self.assertEqual(
taxes['do_tax_isc_combustibles_16']['tax_fiscal_type'],
'isc_fossil_fuel_16')
self.assertEqual(
taxes['do_tax_cheques_020']['tax_kind'], 'others')
self.assertEqual(
taxes['do_tax_cheques_020']['invoice_account'],
'do_account_21021902')
self.assertIn(
"Decimal('0.20')/100",
taxes['do_tax_cheques_020']['rate'])
def test_dgii_tax_mapping_formalizes_every_tax_template(self):
taxes = {
record_id: values
for _, record_id, model, values in _iter_xml_records('tax_do.xml')
if model == 'account.tax.template'
}
mappings = {
record_id: values
for _, record_id, model, values in _iter_xml_records(
'dgii_tax_mapping_data.xml')
if model == 'account.do.dgii.tax.mapping'
}
mapped_taxes = {
values['tax_template'] for values in mappings.values()}
self.assertEqual(len(mappings), 55)
self.assertEqual(mapped_taxes, set(taxes))
for record_id, values in mappings.items():
with self.subTest(record_id=record_id):
self.assertTrue(values.get('form'))
self.assertTrue(values.get('box'))
self.assertTrue(values.get('box_label'))
self.assertTrue(values.get('amount_source'))
self.assertIn(values.get('operator'), {'+', '-', 'info'})
self.assertTrue(values.get('flow'))
self.assertTrue(values.get('legal_basis'))
tax = taxes[values['tax_template']]
if tax.get('type') != 'none':
self.assertTrue(values.get('account_template'))
by_tax = {
values['tax_template']: values
for values in mappings.values()
}
self.assertEqual(by_tax['do_tax_itbis_18_venta']['form'], 'it1')
self.assertEqual(by_tax['do_tax_itbis_18_venta']['box'], '16')
self.assertEqual(by_tax['do_tax_itbis_18_compra']['box'], '22')
self.assertEqual(by_tax['do_tax_itbis_exento']['form'], 'it1_annex_a')
self.assertEqual(by_tax['do_tax_itbis_exento']['box'], '04')
self.assertEqual(by_tax['do_tax_ret_itbis_75_inf']['box'], '56')
self.assertEqual(by_tax['do_tax_ret_isr_ext_27']['form'], '609')
self.assertEqual(by_tax['do_tax_cheques_015']['effective_to'],
'2026-07-02')
self.assertEqual(by_tax['do_tax_cheques_020']['effective_from'],
'2026-07-03')
def test_tax_code_templates_use_expected_signs(self):
codes = {
record_id: values
for _, record_id, model, values in _iter_xml_records(
'tax_code_do.xml')
if model == 'account.tax.code.template'
}
lines = {
record_id: values
for _, record_id, model, values in _iter_xml_records(
'tax_code_do.xml')
if model == 'account.tax.code.line.template'
}
for record_id in [
'do_tc_itbis',
'do_tc_isr',
'do_tc_isc',
'do_tc_cdt',
'do_tc_others',
]:
self.assertEqual(codes[record_id].get('parent'), 'None')
self.assertEqual(codes['do_tc_isr']['name'], 'ISR - Retenciones')
self.assertEqual(
lines['do_tcl_isr_int_pf_inv']['code'],
'do_tc_isr_intereses_pf')
self.assertEqual(
lines['do_tcl_isr_int_pf_cr']['code'],
'do_tc_isr_intereses_pf')
self.assertEqual(
lines['do_tcl_isr_int_pj_inv']['code'],
'do_tc_isr_intereses')
self.assertEqual(
lines['do_tcl_isr_int_pj_cr']['code'],
'do_tc_isr_intereses')
self.assertEqual(
lines['do_tcl_chq020_inv']['code'],
'do_tc_others_checks_020')
self.assertEqual(
lines['do_tcl_chq020_cr']['code'],
'do_tc_others_checks_020')
self.assertEqual(lines['do_tcl_itbis18v_inv']['operator'], '+')
self.assertEqual(lines['do_tcl_itbis18c_inv']['operator'], '-')
self.assertEqual(lines['do_tcl_itbis9v_inv']['operator'], '+')
self.assertEqual(lines['do_tcl_itbis9c_inv']['operator'], '-')
self.assertEqual(lines['do_tcl_ret_itbis_inv']['operator'], '-')
self.assertEqual(lines['do_tcl_ret_itbis_adq_inv']['operator'], '+')
self.assertEqual(lines['do_tcl_ret_itbis_inf75_inv']['operator'], '-')
self.assertEqual(lines['do_tcl_isr_hon_inv']['operator'], '-')
self.assertEqual(lines['do_tcl_isr_est_inv']['operator'], '+')
self.assertEqual(lines['do_tcl_isr_est5_inv']['operator'], '+')
self.assertEqual(lines['do_tcl_isr_bovine_inv']['operator'], '-')
self.assertEqual(lines['do_tcl_isr_exporter_inv']['operator'], '+')
self.assertEqual(lines['do_tcl_isc_comb_inv']['operator'], '+')
self.assertEqual(lines['do_tcl_prop_inv']['code'], 'do_tc_others_tip')
def test_tax_kind_contains_dominican_tax_categories(self):
codes = {code for code, _ in TAX_KIND}
self.assertEqual(codes, {
'',
'itbis',
'itbis_withholding',
'itbis_withholding_acquirer',
'isr_withholding',
'isc',
'cdt',
'tip',
'others',
})
def test_tax_kind_codes_are_unique(self):
codes = [code for code, _ in TAX_KIND]
self.assertEqual(len(codes), len(set(codes)))
def test_fiscal_classification_codes_are_unique(self):
fiscal_codes = [code for code, _ in TAX_FISCAL_TYPE]
application_codes = [code for code, _ in TAX_APPLICATION]
self.assertEqual(len(fiscal_codes), len(set(fiscal_codes)))
self.assertEqual(len(application_codes), len(set(application_codes)))
del ModuleTestCase