odoo18/addons/l10n_es_edi_verifactu/tests/common.py

193 lines
8.8 KiB
Python

import base64
import json
import requests
from unittest import mock
from odoo import _, release, Command
from odoo.tools import file_open, html_sanitize, misc, zeep
from odoo.addons.account.tests.common import AccountTestInvoicingCommon
class TestL10nEsEdiVerifactuCommon(AccountTestInvoicingCommon):
@classmethod
@AccountTestInvoicingCommon.setup_country('es')
def setUpClass(cls):
super().setUpClass()
fake_db_identifier = '7244834601315494189'
db_identifier_function = 'odoo.addons.l10n_es_edi_verifactu.models.verifactu_document.L10nEsEdiVerifactuDocument._get_db_identifier'
patch_db_identifier = mock.patch(db_identifier_function, lambda _self: fake_db_identifier)
cls.startClassPatcher(patch_db_identifier)
# Allow to see the full result of AssertionError.
cls.maxDiff = None
cls.other_currency = cls.setup_other_currency('USD')
certificate_path = 'l10n_es_edi_verifactu/demo/certificates/Certificado_RPJ_A39200019_CERTIFICADO_ENTIDAD_PRUEBAS_5_Pre.p12'
cls.certificate = cls.env['certificate.certificate'].create({
'content': base64.encodebytes(misc.file_open(certificate_path, 'rb').read()),
'pkcs12_password': '1234',
'scope': 'verifactu',
})
cls.company = cls.company_data['company']
cls.company.write({
'country_id': cls.env.ref('base.es').id,
'state_id': cls.env.ref('base.state_es_z').id,
# Use the VAT / NIF that was used to generate the responses
# This is needed to have the correct record identifiers on the invoices
'vat': 'ESA39200019',
'l10n_es_edi_verifactu_required': True,
'l10n_es_edi_verifactu_certificate_ids': [Command.set(cls.certificate.ids)],
'l10n_es_edi_verifactu_test_environment': True,
})
cls.partner_a.write({
'vat': 'BE0477472701',
'country_id': cls.env.ref('base.be').id,
})
cls.partner_b.write({
'vat': 'ESF35999705',
})
cls.product_1 = cls.env['product.product'].create({
'name': "Product 1",
})
ChartTemplate = cls.env['account.chart.template'].with_company(cls.company)
cls.tax21_goods = ChartTemplate.ref('account_tax_template_s_iva21b')
cls.tax21_services = ChartTemplate.ref('account_tax_template_s_iva21s')
cls.tax10_goods = ChartTemplate.ref('account_tax_template_s_iva10b')
cls.tax10_services = ChartTemplate.ref('account_tax_template_s_iva10s')
cls.tax1p4_services_recargo = ChartTemplate.ref('account_tax_template_s_req014')
cls.tax5p2_services_recargo = ChartTemplate.ref('account_tax_template_s_req52')
cls.tax1_withholding = ChartTemplate.ref('account_tax_template_s_irpf1')
cls.tax0_no_sujeto_loc = ChartTemplate.ref('account_tax_template_s_iva_ns')
cls.tax0_isp = ChartTemplate.ref('account_tax_template_s_iva0_isp')
cls.tax0_exento = ChartTemplate.ref('account_tax_template_s_iva0')
cls.tax0_exento_export = ChartTemplate.ref('account_tax_template_s_iva0_g_e')
# We create a 'no_sujeto' tax since there is currently no such tax in the standard chart
cls.tax0_no_sujeto = cls.tax0_no_sujeto_loc.copy()
cls.tax0_no_sujeto.l10n_es_type = 'no_sujeto'
# Everything in the tests should be possible without being administrator.
# We do not want to hide access errors the user may have in production (i.e. with access to the certificates)
cls.user.groups_id = [Command.unlink(cls.env.ref('base.group_system').id)]
# Do not do any zeep operations by default.
# I.e. do not do xml / xsd validation during tests (needs network connection to create the client).
cls.startClassPatcher(cls._mock_get_zeep_operation(None, None, None))
# Do avoid updating the test files for every version we just assume that we are in 17.0
cls.startClassPatcher(mock.patch.object(release, 'version', '17.0+e'))
@classmethod
def _read_file(cls, path, *args):
with file_open(path, *args) as f:
content = f.read()
return content
def _json_file_to_dict(self, json_file):
json_string = self._read_file(json_file, 'rb')
return json.loads(json_string)
def _mock_response(self, status_code, response_file, content_type='text/xml;charset=UTF-8'):
response = mock.Mock(spec=requests.Response)
response.status_code = status_code
response.text = self._read_file(response_file)
response.content = response.text.encode()
response.headers = {
'content-type': content_type,
}
return response
def _mock_request(self, mock_response):
request_function_path = 'odoo.addons.l10n_es_edi_verifactu.models.verifactu_document.L10nEsEdiVerifactuDocument._soap_request'
return mock.patch(request_function_path, return_value=mock_response)
def _mock_get_zeep_operation(self, registration_return_value=None, registration_xml_return_value=None):
request_function_path = 'odoo.addons.l10n_es_edi_verifactu.models.verifactu_document._get_zeep_operation'
def mocked_get_zeep_operation(company, operation):
if operation not in ('registration', 'registration_xml'):
raise NotImplementedError()
function = registration_return_value if operation == 'registration' else registration_xml_return_value
return (function, {})
return mock.patch(request_function_path, mocked_get_zeep_operation)
def _mock_zeep_registration_operation(self, response_file_json):
# Note: The real result is of type 'odoo.tools.zeep.client.SerialProxy'; here it is a dict
zeep_response_dict = json.loads(self._read_file(response_file_json))
return self._mock_get_zeep_operation(registration_return_value=lambda *args, **kwargs: zeep_response_dict)
def _mock_zeep_registration_operation_certificate_issue(self):
def _raise_certificate_error(*args, **kwargs):
certificate_error = "No autorizado. Se ha producido un error al verificar el certificado presentado"
raise zeep.exceptions.TransportError(certificate_error)
return self._mock_get_zeep_operation(registration_return_value=_raise_certificate_error)
def _mock_cron_trigger(self, cron_trigger_result_dict):
trigger_function_path = 'odoo.addons.base.models.ir_cron.ir_cron._trigger'
def _put_at_in_dict(self, at=None):
cron_trigger_result_dict['at'] = at
return mock.patch(trigger_function_path, _put_at_in_dict)
def _mock_format_document_errors(self, errors, title):
"""Mock the computation of field `errors` of model 'l10n_es_edi_verifactu.document' from a list of translated errors."""
error = {
'error_title': title,
'errors': errors,
}
return html_sanitize(self.env['account.move.send']._format_error_html(error))
def _mock_format_document_generic_errors(self, errors):
title = _("Error")
return self._mock_format_document_errors(errors, title)
def _mock_format_document_aeat_errors(self, errors):
title = _("The Veri*Factu document contains the following errors according to the AEAT")
return self._mock_format_document_errors(errors, title)
def _mock_format_document_generation_errors(self, errors):
title = _("The Veri*Factu document could not be created")
return self._mock_format_document_errors(errors, title)
def _mock_last_document(self, document):
# Note: returns the same document for all companies
function_path = 'odoo.addons.l10n_es_edi_verifactu.models.res_company.ResCompany._l10n_es_edi_verifactu_get_last_document'
return mock.patch(function_path, return_value=(document or self.env['l10n_es_edi_verifactu.document']))
def _mock_create_date(self, date):
return mock.patch.object(self.env.cr, 'now', lambda: date)
def _create_dummy_invoice(self, name=None, invoice_date=None):
# The only values we care about are the ones relevant for the record identifier.
invoice_vals = {
'move_type': 'out_invoice',
'invoice_date': '2019-01-30',
'date': '2019-01-30',
'partner_id': self.partner_b.id, # Spanish customer
'invoice_line_ids': [
Command.create({'product_id': self.product_1.id, 'price_unit': 100.0, 'tax_ids': [Command.set(self.tax21_goods.ids)]}),
],
}
# Adjust some values to give the record the needed record identifier
if invoice_date is not None:
invoice_vals['invoice_date'] = invoice_date
if name is not None:
invoice_vals['name'] = name
invoice = self.env['account.move'].create(invoice_vals)
invoice.action_post()
return invoice