# -*- coding: utf-8 -*-
# pylint: disable=C0326
from odoo.addons.l10n_mx_edi.tests.common import TestMxEdiCommon
from odoo.addons.account_reports.tests.common import TestAccountReportsCommon
from odoo import fields, Command
from odoo.tests.common import test_xsd
from odoo.tests import tagged
from odoo.exceptions import RedirectWarning
from freezegun import freeze_time
@tagged('post_install_l10n', 'post_install', '-at_install')
class TestL10nMXTrialBalanceReportCommon(TestMxEdiCommon, TestAccountReportsCommon):
@classmethod
def setUpClass(cls):
super().setUpClass()
# Enforce old default expence account
cls.company = cls.company_data['company']
cls.company_data['default_account_expense'] = cls.env.ref(f'account.{cls.company.id}_cuenta601_84')
cls.account_tag_debit = cls.env.ref('l10n_mx.tag_debit_balance_account')
cls.account_tag_credit = cls.env.ref('l10n_mx.tag_credit_balance_account')
# Entries in 2020 to test initial balance
cls.move_2020_01 = cls.env['account.move'].create({
'move_type': 'entry',
'date': fields.Date.to_date('2020-01-01'),
'journal_id': cls.company_data['default_journal_misc'].id,
'line_ids': [
Command.create({'debit': 1000.0, 'credit': 0.0, 'account_id': cls.company_data['default_account_payable'].id}),
Command.create({'debit': 0.0, 'credit': 1000.0, 'account_id': cls.company_data['default_account_revenue'].id}),
]
})
cls.move_2020_02 = cls.env['account.move'].create({
'move_type': 'entry',
'date': fields.Date.to_date('2020-02-01'),
'journal_id': cls.company_data['default_journal_misc'].id,
'line_ids': [
Command.create(
{'debit': 500.0, 'credit': 0.0, 'account_id': cls.company_data['default_account_expense'].id}),
Command.create(
{'debit': 0.0, 'credit': 500.0, 'account_id': cls.company_data['default_account_revenue'].id}),
]
})
(cls.move_2020_01 + cls.move_2020_02).action_post()
# Entries in 2021 to test report for a specific financial year
cls.move_2021_01 = cls.env['account.move'].create({
'move_type': 'entry',
'date': fields.Date.to_date('2021-06-01'),
'journal_id': cls.company_data['default_journal_misc'].id,
'line_ids': [
Command.create(
{'debit': 250.0, 'credit': 0.0, 'account_id': cls.company_data['default_account_expense'].id}),
Command.create(
{'debit': 0.0, 'credit': 250.0, 'account_id': cls.company_data['default_account_revenue'].id}),
]
})
cls.move_2021_02 = cls.env['account.move'].create({
'move_type': 'entry',
'date': fields.Date.to_date('2021-08-01'),
'journal_id': cls.company_data['default_journal_misc'].id,
'line_ids': [
Command.create(
{'debit': 75.0, 'credit': 0.0, 'account_id': cls.company_data['default_account_payable'].id}),
Command.create(
{'debit': 0.0, 'credit': 75.0, 'account_id': cls.company_data['default_account_revenue'].id}),
]
})
(cls.move_2021_01 + cls.move_2021_02).action_post()
# Special cases: codes with extra levels from the default COA and dotted account names
cls.extra_deep_code = cls.env["account.account"].create(
{
"name": "Extra deep code",
"account_type": "liability_current",
"code": "205.06.01.001",
"reconcile": True,
}
)
cls.dotted_name = cls.env["account.account"].create(
{
"name": "Dotted name C.V.",
"account_type": "liability_current",
"code": "205.06.03",
"reconcile": True,
}
)
cls.extra_deep_code_move = cls.env["account.move"].create(
{
"move_type": "entry",
"date": fields.Date.to_date("2021-06-01"),
"journal_id": cls.company_data["default_journal_misc"].id,
"line_ids": [
Command.create(
{
"debit": 400.0,
"credit": 0.0,
"account_id": cls.dotted_name.id,
}
),
Command.create(
{
"debit": 0.0,
"credit": 400.0,
"account_id": cls.extra_deep_code.id,
}
),
],
}
)
cls.dotted_name_move = cls.env["account.move"].create(
{
"move_type": "entry",
"date": fields.Date.to_date("2021-06-01"),
"journal_id": cls.company_data["default_journal_misc"].id,
"line_ids": [
Command.create(
{
"debit": 50.0,
"credit": 0.0,
"account_id": cls.extra_deep_code.id,
}
),
Command.create(
{
"debit": 0.0,
"credit": 50.0,
"account_id": cls.dotted_name.id,
}
),
],
}
)
(cls.extra_deep_code_move + cls.dotted_name_move).action_post()
cls.report = cls.env.ref('account_reports.trial_balance_report')
cls.company_data['company'].totals_below_sections = False
@tagged('post_install_l10n', 'post_install', '-at_install')
class TestL10nMXTrialBalanceReport(TestL10nMXTrialBalanceReportCommon):
def test_generate_coa_xml(self):
""" This test will generate a COA report and verify that every
account with an entry in the selected period has been there.
CodAgrup corresponds to Account Group code
NumCta corresponds to Account Group code
Desc corresponds to Account Group Name
Nivel corresponds to Hierarchy Level
Natur corresponds to type of account (Debit or Credit)
Available values for "Natur":
D = Debit Account
A = Credit Account
Unaffected Earnings account is not include in this report because
it's custom Odoo account.
"""
expected_coa_xml = b"""
"""
options = self._generate_options(self.report, '2021-01-01', '2021-12-31')
options['l10n_mx_sat_ignore_errors'] = True
with freeze_time(self.frozen_today):
coa_report = self.env[self.report.custom_handler_model_name].action_l10n_mx_generate_coa_sat_xml(options)['file_content']
self.assertXmlTreeEqual(
self.get_xml_tree_from_string(coa_report),
self.get_xml_tree_from_string(expected_coa_xml),
)
def test_generate_coa_xml_with_prefix_7_accounts_having_debit_and_credit_tags(self):
""" This test will generate a COA report and verify that every
account with an entry in the selected period has been there. This process
should pass when having Debit and Credit tags for accounts as 7MM.NN.OO.
"""
expected_coa_xml = b"""
"""
self.env['account.account'].create({
'code': '702.02.01',
'name': 'Otros gastos',
'account_type': 'income',
'tag_ids': [Command.set((self.account_tag_debit + self.account_tag_credit).ids)],
})
options = self._generate_options(self.report, '2021-01-01', '2021-12-31')
options['l10n_mx_sat_ignore_errors'] = True
with freeze_time(self.frozen_today):
coa_report = self.env[self.report.custom_handler_model_name].with_context(skip_xsd=True).action_l10n_mx_generate_coa_sat_xml(options)['file_content']
self.assertXmlTreeEqual(
self.get_xml_tree_from_string(coa_report),
self.get_xml_tree_from_string(expected_coa_xml),
)
def test_generate_sat_xml(self):
""" This test will generate a SAT report and verify that
every account present in the trial balance (except unaffected
earnings account) is present in the xml.
NumCta corresponds to Account Group code
SaldoIni corresponds to Initial Balance
Debe corresponds to Debit in the current period
Haber corresponds to Credit in the current period
SaldoFin corresponds to End Balance
"""
expected_sat_xml = b"""
"""
# Adding a more specific group to ensure that the SAT is still generated correctly
self.env['account.group'].create({
"code_prefix_start": "201.01.01",
"code_prefix_end": "201.01.01",
"name": "Specific group",
})
options = self._generate_options(self.report, '2021-01-01', '2021-12-31')
options['l10n_mx_sat_ignore_errors'] = True
with freeze_time(self.frozen_today):
sat_report = self.env[self.report.custom_handler_model_name].action_l10n_mx_generate_sat_xml(options)['file_content']
self.assertXmlTreeEqual(
self.get_xml_tree_from_string(sat_report),
self.get_xml_tree_from_string(expected_sat_xml),
)
def test_generate_coa_xml_without_tag(self):
"""This test verifies that all accounts present in the trial balance have a Debit or a Credit balance account tag"""
self.company_data['default_account_payable'].tag_ids = [Command.clear()]
options = self._generate_options(self.report, '2021-01-01', '2021-12-31')
options['l10n_mx_sat_ignore_errors'] = True
with self.assertRaises(RedirectWarning):
self.env[self.report.custom_handler_model_name].action_l10n_mx_generate_coa_sat_xml(options)
def test_mx_trial_balance(self):
""" This test will test the Mexican Trial Balance (with and without the hierarchy) """
# Testing the report without hierarchy
options = self._generate_options(self.report, '2021-01-01', '2021-12-31', {'hierarchy': False, 'unfold_all': True})
options['l10n_mx_sat_ignore_errors'] = True
self.assertLinesValues(
self.report._get_lines(options),
# Name Initial Balance Debit Credit End Balance
[0, 1, 2, 3, 4],
[
('201.01.01 National suppliers', 1000.0, 75.0, 0.0, 1075.0),
('205.06.01.001 Extra deep code', 0.0, 50.0, 400.0, -350.0),
('205.06.03 Dotted name C.V.', 0.0, 400.0, 50.0, 350.0),
('401.01.01 Sales and/or services taxed at the general rate', 0.0, 0.0, 325.0, -325.0),
('601.84.01 Other overheads', 0.0, 250.0, 0.0, 250.0),
('Result Brought Forward - ESCUELA KEMPER URGATE', -1000.00, 0.00, 0.00, -1000.00),
('Total', 0.0, 775.0, 775.0, 0.0),
],
options,
)
# Testing the report with hierarchy
options['hierarchy'] = True
self.assertLinesValues(
self.report._get_lines(options),
# Name Initial Balance Debit Credit End Balance
[0, 1, 2, 3, 4],
[
('2 Passive', 1000.0, 525.0, 450.0, 1075.0),
('201 Suppliers', 1000.0, 75.0, 0.0, 1075.0),
('201.01 National suppliers', 1000.0, 75.0, 0.0, 1075.0),
('201.01.01 National suppliers', 1000.0, 75.0, 0.0, 1075.0),
('205 Short-term sundry creditors', 0.0, 450.0, 450.0, 0.0),
('205.06 Other short-term sundry creditors', 0.0, 450.0, 450.0, 0.0),
('205.06.01.001 Extra deep code', 0.0, 50.0, 400.0, -350.0),
('205.06.03 Dotted name C.V.', 0.0, 400.0, 50.0, 350.0),
('4 Income', 0.0, 0.0, 325.0, -325.0),
('401 Income', 0.0, 0.0, 325.0, -325.0),
('401.01 Sales and/or services taxed at the general rate', 0.0, 0.0, 325.0, -325.0),
('401.01.01 Sales and/or services taxed at the general rate', 0.0, 0.0, 325.0, -325.0),
('6 Expenditure', 0.0, 250.0, 0.0, 250.0),
('601 Overheads', 0.0, 250.0, 0.0, 250.0),
('601.84 Other overheads', 0.0, 250.0, 0.0, 250.0),
('601.84.01 Other overheads', 0.0, 250.0, 0.0, 250.0),
('Result Brought Forward - ESCUELA KEMPER URGATE', -1000.00, 0.00, 0.00, -1000.00),
('Total', 0.0, 775.0, 775.0, 0.0),
],
options,
)
def test_coa_valid_no_certificado(self):
"""
Test that the noCertificado section in the coa report contains 20 characters as specified by the format description:
http://www.sat.gob.mx/esquemas/ContabilidadE/1_1/BalanzaComprobacion/BalanzaComprobacion_1_1.xsd
"""
options = self._generate_options(self.report, '2021-01-01', '2021-12-31')
options['l10n_mx_sat_ignore_errors'] = True
with freeze_time(self.frozen_today):
self.certificate.write({
'date_start': f'{self.frozen_today.year}-01-01',
'date_end': f'{self.frozen_today.year}-12-31',
})
self.company.l10n_mx_edi_certificate_ids = self.certificate
coa_report = self.env[self.report.custom_handler_model_name].action_l10n_mx_generate_coa_sat_xml(options)['file_content']
self.assertEqual(20, len(self.get_xml_tree_from_string(coa_report).attrib.get("noCertificado")))
@tagged('external_l10n', 'post_install', '-at_install', '-standard', 'external')
class TestL10nMXTrialBalanceReportXmlValidity(TestL10nMXTrialBalanceReportCommon):
@test_xsd(url='https://www.sat.gob.mx/esquemas/ContabilidadE/1_3/CatalogoCuentas/CatalogoCuentas_1_3.xsd')
def test_coa_xml_validity(self):
options = self._generate_options(self.report, '2021-01-01', '2021-12-31')
options['l10n_mx_sat_ignore_errors'] = True
return self.env[self.report.custom_handler_model_name].action_l10n_mx_generate_coa_sat_xml(options)['file_content']
@test_xsd(url='https://www.sat.gob.mx/esquemas/ContabilidadE/1_3/BalanzaComprobacion/BalanzaComprobacion_1_3.xsd')
def test_sat_xml_validity(self):
options = self._generate_options(self.report, '2021-01-01', '2021-12-31')
options['l10n_mx_sat_ignore_errors'] = True
return self.env[self.report.custom_handler_model_name].action_l10n_mx_generate_sat_xml(options)['file_content']