diff --git a/l10n_br_mdfe/models/document.py b/l10n_br_mdfe/models/document.py index b71f7cf5fca3..47470b8872a1 100644 --- a/l10n_br_mdfe/models/document.py +++ b/l10n_br_mdfe/models/document.py @@ -986,7 +986,7 @@ def atualiza_status_mdfe(self, processo): response=infProt.xMotivo, protocol_date=protocol_date, protocol_number=infProt.nProt, - file_response_xml=processo.processo_xml, + file_response_xml=processo.processo_xml.decode("utf-8"), ) self.write( { diff --git a/l10n_br_mdfe/tests/__init__.py b/l10n_br_mdfe/tests/__init__.py index 8d16e3d86251..bbc2801ea8b6 100644 --- a/l10n_br_mdfe/tests/__init__.py +++ b/l10n_br_mdfe/tests/__init__.py @@ -4,4 +4,5 @@ from . import test_mdfe_import from . import test_mdfe_structure from . import test_mdfe_res_partner +from . import test_mdfe_webservices from . import test_damdfe diff --git a/l10n_br_mdfe/tests/test_damdfe.py b/l10n_br_mdfe/tests/test_damdfe.py index 9db972215296..3ec3ace4fde5 100644 --- a/l10n_br_mdfe/tests/test_damdfe.py +++ b/l10n_br_mdfe/tests/test_damdfe.py @@ -2,7 +2,6 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import re -from datetime import datetime from odoo.exceptions import UserError @@ -13,47 +12,8 @@ class MDFeDAMDFeTest(TestMDFeSerialize): def setUp(self): super().setUp(mdfe_list=[]) - FiscalDocument = self.env["l10n_br_fiscal.document"] - - self.acre_state = self.env.ref("base.state_br_ac") - self.acre_city = self.env.ref("l10n_br_base.city_1200013") - self.mdfe_document_type_id = self.env.ref("l10n_br_fiscal.document_58") - self.sn_company_id = self.env.ref("l10n_br_base.empresa_simples_nacional") - self.serie_id = self.env.ref("l10n_br_fiscal.empresa_sn_document_58_serie_1") - self.related_nfe_id = self.env.ref("l10n_br_mdfe.demo_mdfe_related_nfe") - self.formatted_related_nfe_key = FiscalDocument._format_document_key( - self.related_nfe_id.mdfe30_chNFe - ) - self._create_documents() - def _get_default_document_data(self): - info_descarregamento = [ - ( - 0, - 0, - { - "state_id": self.acre_state.id, - "city_id": self.acre_city.id, - "document_type": "nfe", - "nfe_ids": [(6, 0, [self.related_nfe_id.id])], - }, - ) - ] - - return { - "document_type_id": self.mdfe_document_type_id.id, - "company_id": self.sn_company_id.id, - "document_serie_id": self.serie_id.id, - "document_date": datetime.now(), - "mdfe_initial_state_id": self.acre_state.id, - "mdfe_final_state_id": self.acre_state.id, - "mdfe_loading_city_ids": [(4, self.acre_city.id)], - "mdfe30_infMunDescarga": info_descarregamento, - "manual_fiscal_additional_data": "FISCAL ADDITIONAL DATA", - "manual_customer_additional_data": "CUSTOMER ADDITIONAL DATA", - } - def _create_documents(self): FiscalDocument = self.env["l10n_br_fiscal.document"] default_document_data = self._get_default_document_data() diff --git a/l10n_br_mdfe/tests/test_mdfe_serialize.py b/l10n_br_mdfe/tests/test_mdfe_serialize.py index 73cc87aeed39..be9326706d3c 100644 --- a/l10n_br_mdfe/tests/test_mdfe_serialize.py +++ b/l10n_br_mdfe/tests/test_mdfe_serialize.py @@ -27,6 +27,18 @@ def setUp(self, mdfe_list): "odoo.addons.l10n_br_mdfe_spec.models.v3_0.mdfe_tipos_basico_v3_00", ) + FiscalDocument = self.env["l10n_br_fiscal.document"] + + self.acre_state = self.env.ref("base.state_br_ac") + self.acre_city = self.env.ref("l10n_br_base.city_1200013") + self.mdfe_document_type_id = self.env.ref("l10n_br_fiscal.document_58") + self.sn_company_id = self.env.ref("l10n_br_base.empresa_simples_nacional") + self.serie_id = self.env.ref("l10n_br_fiscal.empresa_sn_document_58_serie_1") + self.related_nfe_id = self.env.ref("l10n_br_mdfe.demo_mdfe_related_nfe") + self.formatted_related_nfe_key = FiscalDocument._format_document_key( + self.related_nfe_id.mdfe30_chNFe + ) + self.mdfe_list = mdfe_list for mdfe_data in self.mdfe_list: mdfe = self.env.ref(mdfe_data["record_ref"]) @@ -223,6 +235,33 @@ def prepare_modal_ferroviario_data(self, mdfe): ), ] + def _get_default_document_data(self): + info_descarregamento = [ + ( + 0, + 0, + { + "state_id": self.acre_state.id, + "city_id": self.acre_city.id, + "document_type": "nfe", + "nfe_ids": [(6, 0, [self.related_nfe_id.id])], + }, + ) + ] + + return { + "document_type_id": self.mdfe_document_type_id.id, + "company_id": self.sn_company_id.id, + "document_serie_id": self.serie_id.id, + "document_date": datetime.now(), + "mdfe_initial_state_id": self.acre_state.id, + "mdfe_final_state_id": self.acre_state.id, + "mdfe_loading_city_ids": [(4, self.acre_city.id)], + "mdfe30_infMunDescarga": info_descarregamento, + "manual_fiscal_additional_data": "FISCAL ADDITIONAL DATA", + "manual_customer_additional_data": "CUSTOMER ADDITIONAL DATA", + } + def serialize_xml(self, mdfe_data): mdfe = mdfe_data["mdfe"] xml_path = os.path.join( diff --git a/l10n_br_mdfe/tests/test_mdfe_webservices.py b/l10n_br_mdfe/tests/test_mdfe_webservices.py new file mode 100644 index 000000000000..386ef7ce5e0b --- /dev/null +++ b/l10n_br_mdfe/tests/test_mdfe_webservices.py @@ -0,0 +1,196 @@ +# Copyright 2023 KMEE (Felipe Zago Rodrigues ) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +# pylint: disable=line-too-long + +from datetime import datetime +from unittest import mock + +from nfelib.mdfe.bindings.v3_0.ret_cons_reci_mdfe_v3_00 import RetConsReciMdfe +from nfelib.mdfe.bindings.v3_0.ret_evento_mdfe_v3_00 import RetEventoMdfe +from nfelib.nfe.ws.edoc_legacy import ( + DocumentoElectronicoAdapter as DocumentoEletronico, + analisar_retorno_raw_xsdata, +) + +from odoo.fields import Datetime + +from odoo.addons.l10n_br_fiscal.constants.fiscal import ( + AUTORIZADO, + SITUACAO_EDOC_AUTORIZADA, + SITUACAO_EDOC_CANCELADA, + SITUACAO_EDOC_ENCERRADA, + SITUACAO_EDOC_REJEITADA, +) + +from .test_mdfe_serialize import TestMDFeSerialize + +# flake8: noqa: B950 +response_autorizada = """353.002RS20230517152856359000011934294104Arquivo processado352RS20230517152856352310054724750001025802000007000316367643752023-10-06T13:00:00-03:00935230000069676MZn64O3kFyMQ+DEP79F3gt2F0PU=100Autorizado o uso do MDF-e""" + +response_rejeitada = """353.002RS20230517152856359000011934276104Arquivo processado352RS20230517152856352310054724750001025802000007000216365726652023-10-06T13:00:00-03:00204Rejeição: Duplicidade de MDF-e [nProt:935230000069668][dhAut:2023-10-06T13:00:00-03:00]""" + +response_cancelamento = """353.002RS2023092708580435135Evento registrado e vinculado ao MDF-e35231005472475000102580200000700041639188984110111Cancelamento0012023-10-06T13:00:00-03:00935230000069678""" + +response_encerramento = """353.002RS2023092708580435135Evento registrado e vinculado ao MDF-e35231005472475000102580200000700011636567559110112Encerramento0012023-10-06T13:00:00-03:00935230000069680""" + + +class FakeRetorno(object): + def __init__(self, text): + self.text = text + self.content = text.encode("utf-8") + + def raise_for_status(self): + pass + + +def mocked_mdfe_autorizada(*args, **kwargs): + result = analisar_retorno_raw_xsdata( + "nfeAutorizacaoLote", + object(), + b"", + FakeRetorno(response_autorizada), + RetConsReciMdfe, + ) + result.processo_xml = b"dummy" + return result + + +def mocked_mdfe_rejeitada(*args, **kwargs): + result = analisar_retorno_raw_xsdata( + "nfeAutorizacaoLote", + object(), + b"", + FakeRetorno(response_rejeitada), + RetConsReciMdfe, + ) + result.processo_xml = b"dummy" + return result + + +def mock_mdfe_cancelamento(*args, **kwargs): + result = analisar_retorno_raw_xsdata( + "nfeRecepcaoEvento", + object(), + b"", + FakeRetorno(response_cancelamento), + RetEventoMdfe, + ) + result.processo_xml = b"dummy" + return result + + +def mock_mdfe_encerramento(*args, **kwargs): + result = analisar_retorno_raw_xsdata( + "nfeRecepcaoEvento", + object(), + b"", + FakeRetorno(response_encerramento), + RetEventoMdfe, + ) + result.processo_xml = b"dummy" + return result + + +class TestMDFeWebServices(TestMDFeSerialize): + def setUp(self): + super().setUp(mdfe_list=[]) + + self.sn_company_id.district = "TEST" + self.sn_company_id.street2 = "TEST" + + self.document_id = self.env["l10n_br_fiscal.document"].create( + self._get_default_document_data() + ) + self.prepare_test_mdfe(self.document_id) + + @mock.patch.object(DocumentoEletronico, "_post", side_effect=mocked_mdfe_autorizada) + def test_mdfe_success(self, _mock_post): + self.document_id.action_document_send() + + self.assertEqual(self.document_id.state_edoc, SITUACAO_EDOC_AUTORIZADA) + + cancel_wizard = ( + self.env["l10n_br_fiscal.document.cancel.wizard"] + .with_context( + active_model="l10n_br_fiscal.document", active_id=self.document_id.id + ) + .create( + { + "document_id": self.document_id.id, + "justification": "Era apenas um teste.", + } + ) + ) + with mock.patch.object( + DocumentoEletronico, "_post", side_effect=mock_mdfe_cancelamento + ): + cancel_wizard.doit() + + self.assertEqual(self.document_id.state_edoc, SITUACAO_EDOC_CANCELADA) + self.assertIsNotNone(self.document_id.cancel_event_id) + self.assertEqual(self.document_id.cancel_event_id.state, "done") + self.assertEqual(self.document_id.cancel_event_id.status_code, "135") + self.assertEqual( + Datetime.to_string(self.document_id.cancel_event_id.protocol_date), + "2023-10-06 13:00:00", + ) + + @mock.patch.object(DocumentoEletronico, "_post", side_effect=mocked_mdfe_rejeitada) + def test_mdfe_rejeitada(self, _mock_post): + self.document_id.action_document_send() + self.assertEqual(self.document_id.state_edoc, SITUACAO_EDOC_REJEITADA) + + @mock.patch.object(DocumentoEletronico, "_post", side_effect=mocked_mdfe_autorizada) + def test_encerramento_mdfe(self, _mock_post): + self.document_id.action_document_send() + + closure_wizard = ( + self.env["l10n_br_fiscal.document.closure.wizard"] + .with_context( + active_model="l10n_br_fiscal.document", active_id=self.document_id.id + ) + .create( + { + "document_id": self.document_id.id, + "company_id": self.sn_company_id.id, + "state_id": self.acre_state.id, + "city_id": self.acre_city.id, + } + ) + ) + with mock.patch.object( + DocumentoEletronico, "_post", side_effect=mock_mdfe_encerramento + ): + closure_wizard.doit() + + self.assertEqual(self.document_id.state_edoc, SITUACAO_EDOC_ENCERRADA) + self.assertIsNotNone(self.document_id.closure_event_id) + self.assertEqual(self.document_id.closure_event_id.state, "done") + self.assertEqual(self.document_id.closure_event_id.status_code, "135") + self.assertEqual( + Datetime.to_string(self.document_id.closure_event_id.protocol_date), + "2023-10-06 13:00:00", + ) + + def test_atualiza_status_mdfe(self): + mock_autorizada = mock.MagicMock(spec=["resposta"]) + mock_autorizada.resposta.protMDFe.infProt.cStat = AUTORIZADO[0] + mock_autorizada.resposta.protMDFe.infProt.xMotivo = "TESTE AUTORIZADO" + mock_autorizada.resposta.protMDFe.infProt.dhRecbto = datetime.now() + mock_autorizada.processo_xml = b"dummy" + self.document_id.atualiza_status_mdfe(mock_autorizada) + + self.assertEqual(self.document_id.state_edoc, SITUACAO_EDOC_AUTORIZADA) + self.assertEqual(self.document_id.status_code, AUTORIZADO[0]) + self.assertEqual(self.document_id.status_name, "TESTE AUTORIZADO") + + def test_qrcode(self): + old_document_type = self.document_id.document_type_id + self.document_id.document_type_id = False + + qr_code = self.document_id.get_mdfe_qrcode() + self.assertIsNone(qr_code) + + self.document_id.document_type_id = old_document_type + qr_code = self.document_id.get_mdfe_qrcode() + self.assertIsNotNone(qr_code)