Files
enterprise-addons/l10n_es_reports/tests/test_boe_generation.py
T

332 lines
18 KiB
Python

# -*- coding: utf-8 -*-
from freezegun import freeze_time
from odoo.addons.l10n_es_reports.tests.common import TestEsAccountReportsCommon
from odoo import fields
from odoo.tests import tagged
@tagged('post_install_l10n', 'post_install', '-at_install')
class TestBOEGeneration(TestEsAccountReportsCommon):
""" Basic tests checking the generation of BOE files is still possible.
"""
def _check_boe_111_to_303(self, modelo_number):
self.init_invoice('out_invoice', partner=self.spanish_partner, amounts=[10000], invoice_date=fields.Date.today(), taxes=self.spanish_test_tax, post=True)
report = self.env.ref('l10n_es.mod_%s' % modelo_number)
options = self._generate_options(report, fields.Date.from_string('2020-12-01'), fields.Date.from_string('2020-12-31'))
self._check_boe_export(report, options, modelo_number)
@freeze_time('2020-12-22')
def test_boe_mod_111(self):
self._check_boe_111_to_303('111')
@freeze_time('2020-12-22')
def test_boe_mod_115(self):
self._check_boe_111_to_303('115')
@freeze_time('2020-12-22')
def test_boe_mod_130(self):
self._check_boe_111_to_303('130')
@freeze_time('2020-12-22')
def test_boe_mod_303(self):
self._check_boe_111_to_303('303')
@freeze_time('2020-12-22')
def test_boe_mod_347(self):
invoice = self.init_invoice('out_invoice', partner=self.spanish_partner, amounts=[10000], invoice_date=fields.Date.today())
invoice.l10n_es_reports_mod347_invoice_type = 'regular'
invoice._post()
spanish_partner_2 = self.env['res.partner'].create({
# ES Partner with vat not starting with 'ES', should still be included in 347
'name': 'Partner 2',
'street': "Avenida de los Informes Financieros, 43",
'zip': 4242,
'city': "Madrid",
'country_id': self.env.ref('base.es').id,
'state_id': self.env.ref('base.state_es_m').id,
'vat': '74280274A',
})
invoice_2 = self.init_invoice('out_invoice', partner=spanish_partner_2, amounts=[5000], invoice_date=fields.Date.today())
invoice_2.action_post()
invoice_3 = self.init_invoice('out_invoice', partner=spanish_partner_2, amounts=[4000], invoice_date=fields.Date.today())
invoice_3.l10n_es_reports_mod347_invoice_type = 'insurance'
invoice_3._post()
report = self.env.ref('l10n_es_reports.mod_347')
options = self._generate_options(report, fields.Date.from_string('2020-01-01'), fields.Date.from_string('2020-12-31'))
self._check_boe_export(report, options, 347)
# Check file content
report._get_lines(options)
handler = self.env[report.custom_handler_model_name]
vals = handler.export_boe(options)
expected = [
# For information about data position, see page 7 & 16 of
# https://sede.agenciatributaria.gob.es/static_files/Sede/Disenyo_registro/DR_300_399/archivos/347.pdf
# 1,347,year,company vat
"13472020A12345674COMPANY_1_DATA T BECAUSE I AM ACCOUNTMAN! 3470000000002 0000000000000000000002 000000001900000000000000 000000000000000 ",
# 2,347,year,company vat,partner vat
"23472020A1234567474280274A PARTNER 2 D28 B 000000000400000X 000000000000000 0000000000000000000 000000000000000 000000000000000 000000000000000 000000000000000 000000000000000 000000000000000 000000000400000 000000000000000 000000000000000000000 ",
"23472020A12345674A12345674 BERNARDO GANADOR D28 B 000000001000000 000000000000000 0000000000000000000 000000000000000 000000000000000 000000000000000 000000000000000 000000000000000 000000000000000 000000001000000 000000000000000 000000000000000000000 ",
"23472020A1234567474280274A PARTNER 2 D28 B 000000000500000 000000000000000 0000000000000000000 000000000000000 000000000000000 000000000000000 000000000000000 000000000000000 000000000000000 000000000500000 000000000000000 000000000000000000000 ",
]
for generated_line, expected_line in zip(vals['file_content'].decode('utf-8').splitlines(), expected):
self.assertEqual(generated_line, expected_line)
handler._retrieve_boe_manual_wizard(options, 347).write({
'complementary_declaration': True,
'substitutive_declaration': True,
})
vals = handler.export_boe(options)
header_line = vals['file_content'].decode('utf-8').splitlines()[0]
self.assertEqual('C', header_line[120], "Complementary declaration should use 'C'")
self.assertEqual('S', header_line[121], "Substitutive declaration should use 'S'")
@freeze_time('2020-12-22')
def test_boe_mod_349(self):
self.partner_a.write({
'country_id': self.env.ref('base.be').id,
'vat': "BE0477472701",
})
invoice = self.init_invoice('out_invoice', partner=self.partner_a, amounts=[10000], invoice_date=fields.Date.today())
invoice.l10n_es_reports_mod349_invoice_type = 'E'
invoice._post()
report = self.env.ref('l10n_es_reports.mod_349')
options = self._generate_options(report, fields.Date.from_string('2020-12-01'), fields.Date.from_string('2020-12-31'))
self._check_boe_export(report, options, 349)
@freeze_time('2020-12-22')
def test_boe_mod_390(self):
report = self.env.ref('l10n_es.mod_390')
options = self._generate_options(report, '2020-01-01', '2020-12-31')
self._check_boe_export(report, options, 390, additional_context={
'default_physical_person_name': "Bernard Gagnant",
'default_principal_activity': "Selling",
'default_principal_iae_epigrafe': "EAAA",
'default_principal_code_activity': "AAA",
'default_judicial_person_name': "Bebert",
'default_judicial_person_nif': "123",
'default_judicial_person_procuration_date': '2020-01-01',
'default_judicial_person_notary': "Maître Gagnant",
})
@freeze_time('2020-12-22')
def test_boe_mod_347_with_cash_payment(self):
cash_journal = self.env['account.journal'].create({
'name': 'Cash Journal Test',
'type': 'cash',
'company_id': self.company_data['company'].id,
'code': 'CASHBOE',
})
invoice = self.init_invoice('out_invoice', partner=self.spanish_partner, amounts=[1000], invoice_date=fields.Date.today())
invoice.l10n_es_reports_mod347_invoice_type = 'regular'
invoice._post()
self.env['account.payment.register'].with_context(active_ids=invoice.ids, active_model='account.move').create({
'amount': 1000,
'payment_date': invoice.date,
'journal_id': cash_journal.id,
})._create_payments()
other_spanish_partner = self.env['res.partner'].create({
'name': "Other Partner",
'state_id': self.env.ref('base.state_es_m').id,
'country_id': self.env.ref('base.es').id,
'vat': "ESA12345674",
})
other_invoice = self.init_invoice('out_invoice', partner=other_spanish_partner, amounts=[4000], invoice_date=fields.Date.today())
other_invoice.l10n_es_reports_mod347_invoice_type = 'regular'
other_invoice._post()
self.env['account.payment.register'].with_context(active_ids=other_invoice.ids, active_model='account.move').create({
'amount': 4000,
'payment_date': other_invoice.date,
'journal_id': cash_journal.id,
})._create_payments()
report = self.env.ref('l10n_es_reports.mod_347')
options = self._generate_options(report, '2020-01-01', '2020-12-31')
wizard_model = self.env[report.custom_handler_model_name]
wizard_action = wizard_model.open_boe_wizard(options, 347)
wizard = self.env[wizard_action['res_model']].with_context(wizard_action['context']).create({})
options['l10n_es_reports_boe_wizard_id'] = wizard.id
boe_result = self.env[report.custom_handler_model_name].export_boe(options)
self.assertTrue(self.spanish_partner.name.upper() not in boe_result['file_content'].decode())
# Only the other partner is above the 3 005,06 € threshold must be reported
self.assertIn(other_spanish_partner.name.upper(), boe_result['file_content'].decode())
@freeze_time('2025-05-15')
def test_boe_includes_null_lines_mod_349(self):
"""
Test that the mod 349 boe report contains the rectification lines even when the total rectification sum to 0
"""
partner = self.env['res.partner'].create({
'name': 'Test',
'company_id': self.company_data['company'].id,
'company_type': 'company',
'country_id': self.env['res.country'].search([('code', '=', 'BE')]).id,
'vat': 'BE0477472701',
})
invoice = self.init_invoice('out_invoice', partner=partner, amounts=[1000], invoice_date='2025-03-15')
invoice.action_post()
credit_note_wizard = self.env['account.move.reversal'].with_context({
'active_ids': invoice.id,
'active_id': invoice.id,
'active_model': 'account.move',
}).create({
'reason': 'modify', # 'reason' can still be used to indicate purpose
'journal_id': invoice.journal_id.id,
})
credit_note_wizard.modify_moves()
report = self.env.ref('l10n_es_reports.mod_349')
options = self._generate_options(report, fields.Date.from_string('2025-05-01'), fields.Date.from_string('2025-05-31'))
wizard_action = self.env['l10n_es.mod349.tax.report.handler'].open_boe_wizard(options, '349')
wizard = self.env[wizard_action['res_model']].with_context(wizard_action['context']).create({})
options['l10n_es_reports_boe_wizard_id'] = wizard.id
boe_file = self.env['l10n_es.mod349.tax.report.handler'].export_boe(options)
# This string represents a rectification record included in the BOE export.
# It contains:
# - the year (2025),
# - the period,
# - the rectified tax base (0.00),
# - and the previously declared tax base (1000.00).
# Under REGISTRO DE RECTIFICACIONES https://www.boe.es/buscar/doc.php?id=BOE-A-2010-5098
self.assertIn('20250300000000000000000000100000', boe_file['file_content'].decode('utf-8'))
@freeze_time('2025-05-15')
def test_only_include_credit_note_in_rectification_mod_349(self):
"""
Test that in model 349 only the credit notes are included in the computation of the rectification line
"""
partner = self.env['res.partner'].create({
'name': 'Test',
'company_id': self.company_data['company'].id,
'company_type': 'company',
'country_id': self.env['res.country'].search([('code', '=', 'BE')]).id,
'vat': 'BE0477472701',
})
invoice = self.init_invoice('out_invoice', partner=partner, amounts=[1000], invoice_date='2025-03-15')
invoice.action_post()
reversal_wizard = self.env['account.move.reversal'].with_context({
'active_ids': invoice.id,
'active_id': invoice.id,
'active_model': 'account.move',
}).create({
'reason': 'refund',
'journal_id': invoice.journal_id.id,
})
reversal = reversal_wizard.reverse_moves()
refund = self.env['account.move'].browse(reversal.get('res_id'))
refund.line_ids.write({'price_unit': 500.0})
refund.action_post()
self.env['account.payment.register'].with_context(active_model='account.move', active_ids=invoice.ids).create({
'amount': 250.0,
})._create_payments()
report = self.env.ref('l10n_es_reports.mod_349')
options = self._generate_options(report, fields.Date.from_string('2025-05-01'), fields.Date.from_string('2025-05-31'))
wizard_action = self.env['l10n_es.mod349.tax.report.handler'].open_boe_wizard(options, '349')
wizard = self.env[wizard_action['res_model']].with_context(wizard_action['context']).create({})
options['l10n_es_reports_boe_wizard_id'] = wizard.id
boe_file = self.env['l10n_es.mod349.tax.report.handler'].export_boe(options)
# Those strings represent a rectification record included in the BOE export.
# It contains:
# - the year (2025),
# - the period,
# - the rectified tax base (0.00),
# - and the previously declared tax base (1000.00).
# Under REGISTRO DE RECTIFICACIONES https://www.boe.es/buscar/doc.php?id=BOE-A-2010-5098
# Here the new value should be 500 and not 250 because the payment should not count in the rectification line
self.assertIn('20250300000000500000000000100000', boe_file['file_content'].decode('utf-8'))
self.assertNotIn('20250300000000250000000000100000', boe_file['file_content'].decode('utf-8'))
@freeze_time('2025-05-15')
def test_boe_excludes_current_period_rectification_lines(self):
"""
Test that moves from the current period are not included as rectification lines in the boe report
"""
partner = self.env['res.partner'].create({
'name': 'Test',
'company_id': self.company_data['company'].id,
'company_type': 'company',
'country_id': self.env['res.country'].search([('code', '=', 'BE')]).id,
'vat': 'BE0477472701',
})
previous_period_invoice = self.init_invoice('out_invoice', partner=partner, amounts=[1000], invoice_date='2025-03-15')
previous_period_invoice.action_post()
credit_note_wizard_previous = self.env['account.move.reversal'].with_context({
'active_ids': previous_period_invoice.id,
'active_id': previous_period_invoice.id,
'active_model': 'account.move',
}).create({
'reason': 'modify',
'journal_id': previous_period_invoice.journal_id.id,
})
credit_note_wizard_previous.reverse_moves()
current_period_invoice = self.init_invoice('out_invoice', partner=partner, amounts=[1000], invoice_date='2025-05-15')
current_period_invoice.action_post()
credit_note_wizard_current = self.env['account.move.reversal'].with_context({
'active_ids': current_period_invoice.id,
'active_id': current_period_invoice.id,
'active_model': 'account.move',
}).create({
'reason': 'modify',
'journal_id': current_period_invoice.journal_id.id,
})
credit_note_wizard_current.reverse_moves()
report = self.env.ref('l10n_es_reports.mod_349')
options = self._generate_options(report, fields.Date.from_string('2025-05-01'), fields.Date.from_string('2025-05-31'))
wizard_action = self.env['l10n_es.mod349.tax.report.handler'].open_boe_wizard(options, '349')
wizard = self.env[wizard_action['res_model']].with_context(wizard_action['context']).create({})
options['l10n_es_reports_boe_wizard_id'] = wizard.id
boe_file = self.env['l10n_es.mod349.tax.report.handler'].export_boe(options)
# This string represents a rectification record included in the BOE export.
# It contains:
# - the year (2025),
# - the period (here 5 which is the current period),
# - the rectified tax base (0.00),
# - and the previously declared tax base (1000.00).
# Under REGISTRO DE RECTIFICACIONES https://www.boe.es/buscar/doc.php?id=BOE-A-2010-5098
self.assertNotIn('20250500000000000000000000100000', boe_file['file_content'].decode('utf-8'))
@freeze_time('2020-12-22')
def test_boe_button_branch_allowed(self):
"""
Test when a parent company has branches with different VATs and not all
branches are in the company selector
"""
parent_company = self.company_data['company']
self.env['res.company'].create({
'name': 'ES Branch',
'parent_id': parent_company.id,
'vat': 'B88431804',
'country_id': self.env.ref('base.es').id,
})
report = self.env.ref('l10n_es.mod_390').with_context(
allowed_company_ids=parent_company.ids,
)
options = self._generate_options(report, '2020-01-01', '2020-12-31')
boe_button = next(b for b in options['buttons'] if b.get('action_param') == 390)
action = boe_button.get('error_action') or boe_button['action']
wizard_action = report.dispatch_report_action(options, action, boe_button['action_param'])
self.assertEqual(
'l10n_es_reports.aeat.boe.mod390.export.wizard',
wizard_action['res_model'],
)