import unittest from collections import Counter from decimal import Decimal from pathlib import Path from xml.etree import ElementTree as ET from trytond.modules.account_do.tax import 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') 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', '210201', '210202', '210203', '210206', '210207', '210208', '210209', '210210']: 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.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'), ('rate', '=', Decimal('-0.054')), ], 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), 31) self.assertFalse([ record_id for record_id, values in taxes.items() if not values.get('tax_kind')]) self.assertEqual( taxes['do_tax_itbis_18_venta']['invoice_account'], 'do_account_210201') self.assertEqual( taxes['do_tax_itbis_18_compra']['invoice_account'], 'do_account_110401') 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_cheques_020']['tax_kind'], 'others') self.assertIn( "Decimal('0.20')/100", taxes['do_tax_cheques_020']['rate']) 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_ret_itbis_inv']['operator'], '+') self.assertEqual(lines['do_tcl_ret_itbis_adq_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))) del ModuleTestCase