# -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. import base64 from pytz import timezone from datetime import date, datetime import requests from unittest.mock import Mock from odoo.tools import file_open from odoo.addons.account.tests.common import AccountTestInvoicingCommon from odoo.addons.account.tests.test_account_move_send import TestAccountMoveSendCommon class TestEsEdiTbaiCommon(TestAccountMoveSendCommon): @classmethod @AccountTestInvoicingCommon.setup_country('es') def setUpClass(cls): super().setUpClass() cls.frozen_today = datetime(year=2022, month=1, day=1, hour=0, minute=0, second=0, tzinfo=timezone('utc')) # Allow to see the full result of AssertionError. cls.maxDiff = None # ==== Config ==== cls.company_data['company'].write({ 'name': 'EUS Company', 'state_id': cls.env.ref('base.state_es_ss').id, 'vat': 'ESA12345674', 'l10n_es_tbai_test_env': True, }) cls._set_tax_agency('gipuzkoa') # ==== Business ==== cls.partner_a.write({ 'name': "&@àÁ$£€èêÈÊöÔÇç¡⅛™³", # special characters should be escaped appropriately 'vat': 'BE0477472701', 'country_id': cls.env.ref('base.be').id, 'street': 'Rue Sans Souci 1', 'zip': 93071, 'invoice_edi_format': False, }) cls.partner_b.write({ 'vat': 'ESF35999705', }) @classmethod def _set_tax_agency(cls, agency): if agency == "araba": cert_name = 'araba_1234.p12' cert_password = '1234' elif agency == 'bizkaia': cert_name = 'bizkaia_111111.p12' cert_password = '111111' elif agency == 'gipuzkoa': cert_name = 'gipuzkoa_IZDesa2021.p12' cert_password = 'IZDesa2021' else: raise ValueError("Unknown tax agency: " + agency) cls.certificate = cls.env['certificate.certificate'].create({ 'name': 'Test ES TBAI certificate', 'content': base64.b64encode( file_open("l10n_es_edi_tbai/demo/certificates/" + cert_name, 'rb').read()), 'pkcs12_password': cert_password, 'scope': 'tbai', 'company_id': cls.company_data['company'].id, }) cls.company_data['company'].write({ 'l10n_es_tbai_tax_agency': agency, 'l10n_es_tbai_certificate_id': cls.certificate.id, }) @classmethod def _get_tax_by_xml_id(cls, trailing_xml_id): """ Helper to retrieve a tax easily. :param trailing_xml_id: The trailing tax's xml id. :return: An account.tax record """ return cls.env.ref(f'account.{cls.env.company.id}_account_tax_template_{trailing_xml_id}') @classmethod def create_invoice(cls, **kwargs): return cls.env['account.move'].with_context(edi_test_mode=True).create({ 'move_type': 'out_invoice', 'partner_id': cls.partner_a.id, 'invoice_date': '2022-01-01', 'date': '2022-01-01', **kwargs, 'invoice_line_ids': [(0, 0, { 'product_id': cls.product_a.id, 'price_unit': 1000.0, **line_vals, }) for line_vals in kwargs.get('invoice_line_ids', [])], }) @classmethod def _create_posted_invoice(cls): out_invoice = cls.env['account.move'].create({ 'move_type': 'out_invoice', 'invoice_date': date(2022, 1, 1), 'partner_id': cls.partner_a.id, 'invoice_line_ids': [(0, 0, { 'product_id': cls.product_a.id, 'price_unit': 1000.0, 'quantity': 5, 'discount': 20.0, 'tax_ids': [(6, 0, cls._get_tax_by_xml_id('s_iva21b').ids)], })], }) out_invoice.action_post() return out_invoice @classmethod def _get_invoice_send_wizard(cls, invoice): out_invoice_send_wizard = cls.env['account.move.send.wizard']\ .with_context(active_model='account.move', active_ids=invoice.ids)\ .create({'sending_methods': []}) return out_invoice_send_wizard @classmethod def _create_posted_bill(cls): bill = cls.env['account.move'].create({ 'move_type': 'in_invoice', 'invoice_date': date.today(), 'partner_id': cls.partner_a.id, 'ref': "INV123", 'invoice_line_ids': [(0, 0, { 'product_id': cls.product_a.id, 'price_unit': 1000.0, 'quantity': 5, 'discount': 20.0, 'tax_ids': [(6, 0, cls._get_tax_by_xml_id('p_iva21_bc').ids)], })], }) bill.action_post() return bill @classmethod def _get_sample_xml(cls, filename): with file_open(f'l10n_es_edi_tbai/tests/document_xmls/{filename}', 'rb') as file: content = file.read() return content @classmethod def _get_response_xml(cls, filename): with file_open(f'l10n_es_edi_tbai/tests/response_xmls/{filename}', 'rb') as file: content = file.read() return content def create_mock_response(content, headers=None): mock_response = Mock(spec=requests.Response) mock_response.content = content mock_response.headers = headers or {} return mock_response class TestEsEdiTbaiCommonGipuzkoa(TestEsEdiTbaiCommon): @classmethod def setUpClass(cls): super().setUpClass() cls.mock_response_post_invoice_success = create_mock_response(cls._get_response_xml('post_invoice_success_gi.xml')) cls.mock_response_cancel_invoice_success = create_mock_response(cls._get_response_xml('cancel_invoice_success_gi.xml')) cls.mock_response_failure = create_mock_response(cls._get_response_xml('post_or_cancel_invoice_failure_gi.xml')) cls.mock_request_error = requests.exceptions.RequestException("A request exception") class TestEsEdiTbaiCommonBizkaia(TestEsEdiTbaiCommon): @classmethod def setUpClass(cls): super().setUpClass() cls.mock_response_post_invoice_success = create_mock_response( cls._get_response_xml('post_invoice_success_bi.xml'), cls.RESPONSE_HEADERS_SUCCESS ) cls.mock_response_cancel_invoice_success = create_mock_response( cls._get_response_xml('cancel_invoice_success_bi.xml'), cls.RESPONSE_HEADERS_SUCCESS ) cls.mock_response_post_invoice_failure = create_mock_response( cls._get_response_xml('post_invoice_failure_bi.xml'), cls.RESPONSE_HEADERS_FAILURE ) cls.mock_response_cancel_invoice_failure = create_mock_response( cls._get_response_xml('cancel_invoice_failure_bi.xml'), cls.RESPONSE_HEADERS_FAILURE ) cls.mock_response_post_bill_success = create_mock_response( cls._get_response_xml('post_bill_success_bi.xml'), cls.RESPONSE_HEADERS_SUCCESS ) cls.mock_response_cancel_bill_success = create_mock_response( cls._get_response_xml('cancel_bill_success_bi.xml'), cls.RESPONSE_HEADERS_SUCCESS ) cls.mock_response_post_bill_failure = create_mock_response( None, cls.RESPONSE_HEADERS_FAILURE ) cls.mock_response_cancel_bill_failure = create_mock_response( cls._get_response_xml('cancel_bill_failure_bi.xml'), cls.RESPONSE_HEADERS_FAILURE ) cls.mock_request_error = requests.exceptions.RequestException("A request exception") cls.company.l10n_es_tbai_tax_agency = 'bizkaia' RESPONSE_HEADERS_SUCCESS = { 'eus-bizkaia-n3-tipo-respuesta': 'Correcto', 'eus-bizkaia-n3-codigo-respuesta': '', } RESPONSE_HEADERS_FAILURE = { 'eus-bizkaia-n3-tipo-respuesta': 'Incorrecto', 'eus-bizkaia-n3-codigo-respuesta': 'B4_1000002', 'eus-bizkaia-n3-mensaje-respuesta': 'An error msg.', }