From e2c72d26fa04925457bb6852e26d4c1fdde5a211 Mon Sep 17 00:00:00 2001 From: Felipe Zago Date: Tue, 19 Sep 2023 18:44:02 -0300 Subject: [PATCH 1/2] [NEW] MDFe implementation --- src/erpbrasil/edoc/edoc.py | 3 + src/erpbrasil/edoc/mdfe.py | 293 ++++++++++++++++++++++++++++++------- 2 files changed, 240 insertions(+), 56 deletions(-) diff --git a/src/erpbrasil/edoc/edoc.py b/src/erpbrasil/edoc/edoc.py index a6ed891..3660055 100644 --- a/src/erpbrasil/edoc/edoc.py +++ b/src/erpbrasil/edoc/edoc.py @@ -216,6 +216,9 @@ def _hora_agora(self): datetime.now(tz=timezone(timedelta(hours=-3))), FORMAT ) + str(timezone(timedelta(hours=-3)))[3:] + def _data_hoje(self): + return datetime.strftime(datetime.now(), "%Y-%m-%d") + def assina_raiz(self, raiz, id, getchildren=False): xml_string, xml_etree = self._generateds_to_string_etree(raiz) xml_assinado = Assinatura(self._transmissao.certificado).assina_xml2( diff --git a/src/erpbrasil/edoc/mdfe.py b/src/erpbrasil/edoc/mdfe.py index 0dde484..73dbbee 100644 --- a/src/erpbrasil/edoc/mdfe.py +++ b/src/erpbrasil/edoc/mdfe.py @@ -6,6 +6,9 @@ from __future__ import unicode_literals import datetime +import time +import binascii +import base64 from erpbrasil.assinatura.assinatura import Assinatura from lxml import etree @@ -13,73 +16,159 @@ from erpbrasil.edoc.edoc import DocumentoEletronico try: - from mdfelib.v3_00 import consMDFeNaoEnc - from mdfelib.v3_00 import consReciMDFe - from mdfelib.v3_00 import consSitMDFe - from mdfelib.v3_00 import consStatServMDFe - from mdfelib.v3_00 import enviMDFe + from mdfelib.v3_00 import retConsMDFeNaoEnc + from mdfelib.v3_00 import retConsReciMDFe + from mdfelib.v3_00 import retConsSitMDFe + from mdfelib.v3_00 import retConsStatServMDFe + from mdfelib.v3_00 import retEnviMDFe + from mdfelib.v3_00 import procMDFe + from mdfelib.v3_00 import retEventoMDFe + from mdfelib.v3_00 import evCancMDFe + from mdfelib.v3_00 import evEncMDFe except ImportError: pass +WS_MDFE_CONSULTA = "MDFeConsulta" +WS_MDFE_SITUACAO = "MDFeStatusServico" +WS_MDFE_CONSULTA_NAO_ENCERRADOS = "MDFeConsNaoEnc" +WS_MDFE_DISTRIBUICAO = "MDFeDistribuicaoDFe" + +WS_MDFE_RECEPCAO = "MDFeRecepcao" +WS_MDFE_RECEPCAO_SINC = "MDFeRecepcaoSinc" +WS_MDFE_RET_RECEPCAO = "MDFeRetRecepcao" +WS_MDFE_RECEPCAO_EVENTO = "MDFeRecepcaoEvento" + +AMBIENTE_PRODUCAO = 1 +AMBIENTE_HOMOLOGACAO = 2 + +MDFE_MODELO = "58" + +SVC_RS = { + AMBIENTE_PRODUCAO: { + "servidor": "mdfe.svrs.rs.gov.br", + WS_MDFE_RECEPCAO: "ws/MDFeRecepcao/MDFeRecepcao.asmx?wsdl", + WS_MDFE_RET_RECEPCAO: "ws/MDFeRetRecepcao/MDFeRetRecepcao.asmx?wsdl", + WS_MDFE_RECEPCAO_EVENTO: "ws/MDFeRecepcaoEvento/MDFeRecepcaoEvento.asmx?wsdl", + WS_MDFE_CONSULTA: "ws/MDFeConsulta/MDFeConsulta.asmx?wsdl", + WS_MDFE_SITUACAO: "ws/MDFeStatusServico/MDFeStatusServico.asmx?wsdl", + WS_MDFE_CONSULTA_NAO_ENCERRADOS: "ws/MDFeConsNaoEnc/MDFeConsNaoEnc.asmx?wsdl", + WS_MDFE_DISTRIBUICAO: "ws/MDFeDistribuicaoDFe/MDFeDistribuicaoDFe.asmx?wsdl", + WS_MDFE_RECEPCAO_SINC: "ws/MDFeRecepcaoSinc/MDFeRecepcaoSinc.asmx?wsdl", + }, + AMBIENTE_HOMOLOGACAO: { + "servidor": "mdfe-homologacao.svrs.rs.gov.br", + WS_MDFE_RECEPCAO: "ws/MDFeRecepcao/MDFeRecepcao.asmx?wsdl", + WS_MDFE_RET_RECEPCAO: "ws/MDFeRetRecepcao/MDFeRetRecepcao.asmx?wsdl", + WS_MDFE_RECEPCAO_EVENTO: "ws/MDFeRecepcaoEvento/MDFeRecepcaoEvento.asmx?wsdl", + WS_MDFE_CONSULTA: "ws/MDFeConsulta/MDFeConsulta.asmx?wsdl", + WS_MDFE_SITUACAO: "ws/MDFeStatusServico/MDFeStatusServico.asmx?wsdl", + WS_MDFE_CONSULTA_NAO_ENCERRADOS: "ws/MDFeConsNaoEnc/MDFeConsNaoEnc.asmx?wsdl", + WS_MDFE_DISTRIBUICAO: "ws/MDFeDistribuicaoDFe/MDFeDistribuicaoDFe.asmx?wsdl", + WS_MDFE_RECEPCAO_SINC: "ws/MDFeRecepcaoSinc/MDFeRecepcaoSinc.asmx?wsdl", + } +} + +QR_CODE_URL = "https://dfe-portal.svrs.rs.gov.br/mdfe/qrCode" + +NAMESPACES = { + "mdfe": "http://www.portalfiscal.inf.br/mdfe", + "ds": "http://www.w3.org/2000/09/xmldsig#", +} + +def localizar_url(servico, ambiente=2): + dominio = SVC_RS[ambiente]["servidor"] + complemento = SVC_RS[ambiente][servico] + + return "https://%s/%s" % (dominio, complemento) + class MDFe(DocumentoEletronico): - _namespace = 'http://www.portalfiscal.inf.br/mdfe' + _namespace = "http://www.portalfiscal.inf.br/mdfe" - _edoc_situacao_arquivo_recebido_com_sucesso = '103' - _edoc_situacao_em_processamento = '105' - _edoc_situacao_servico_em_operacao = '107' - _edoc_situacao_ja_enviado = ('100', '101', '132') + _edoc_situacao_arquivo_recebido_com_sucesso = "103" + _edoc_situacao_servico_em_operacao = "107" + _edoc_situacao_ja_enviado = ("100", "101", "132") _consulta_servico_ao_enviar = True _maximo_tentativas_consulta_recibo = 5 + def __init__(self, transmissao, uf, versao="3.00", ambiente="2", + mod="58"): + super(MDFe, self).__init__(transmissao) + self.versao = str(versao) + self.ambiente = str(ambiente) + self.uf = int(uf) + self.mod = str(mod) + + def _verifica_resposta_envio_sucesso(self, proc_envio): + return proc_envio.resposta.cStat == \ + self._edoc_situacao_arquivo_recebido_com_sucesso + + def _aguarda_tempo_medio(self, proc_envio): + time.sleep(float(proc_envio.resposta.infRec.tMed) * 1.3) + + def _edoc_situacao_em_processamento(self, proc_recibo): + return proc_recibo.resposta.cStat == "105" + def get_documento_id(self, edoc): return edoc.infMDFe.Id[:3], edoc.infMDFe.Id[3:] + def monta_qrcode(self, chave): + return f"{QR_CODE_URL}?chMDFe={chave}&tpAmb={self.ambiente}" + + def monta_qrcode_contingencia(self, edoc, xml_assinado): + chave = edoc.infMDFe.Id.replace("MDFe", "") + + xml = ET.fromstring(xml_assinado) + digest_value = xml.find('.//ds:DigestValue', namespaces=NAMESPACES).text + digest_value_hex = binascii.hexlify(digest_value.encode()).decode() + + return f"{self.monta_qrcode(chave)}&sign={digest_value_hex}" + def status_servico(self): - raiz = consStatServMDFe.TConsStatServ( - versao='4.00', - tpAmb='2', - cUF=35, - xServ='STATUS', + raiz = retConsStatServMDFe.TConsStatServ( + versao=self.versao, + tpAmb=self.ambiente, + cUF=self.uf, + xServ="STATUS", ) - raiz.original_tagname_ = 'consStatServMDFe' + raiz.original_tagname_ = "consStatServMDFe" return self._post( raiz, - 'https://mdfe-homologacao.svrs.rs.gov.br/wws/MDFeStatusServico/MDFeStatusServico.asmx?wsdl', - 'nfeStatusServicoNF', - consStatServMDFe + localizar_url(WS_MDFE_SITUACAO, int(self.ambiente)), + "mdfeStatusServicoMDF" , + retConsStatServMDFe ) def consulta_documento(self, chave): - raiz = consSitMDFe.TConsSitMDFe( - versao='4.00', - tpAmb='2', - xServ='CONSULTAR', + raiz = retConsSitMDFe.TConsSitMDFe( + versao=self.versao, + tpAmb=self.ambiente, + xServ="CONSULTAR", chMDFe=chave, ) - raiz.original_tagname_ = 'consSitMDFe' + raiz.original_tagname_ = "consSitMDFe" return self._post( raiz, - 'https://mdfe-homologacao.svrs.rs.gov.br/ws/MDFeConsulta/MDFeConsulta.asmx?wsdl', - 'MDFeConsultaNF', - consSitMDFe + localizar_url(WS_MDFE_CONSULTA, int(self.ambiente)), + "mdfeConsultaMDF", + retConsSitMDFe ) def consulta_nao_encerrados(self, cnpj): - raiz = consMDFeNaoEnc.TConsMDFeNaoEnc( - versao=self._versao, - tpAmb=str(self._ambiente), - xServ='CONSULTAR NÃO ENCERRADOS', + raiz = retConsMDFeNaoEnc.TConsMDFeNaoEnc( + versao=self.versao, + tpAmb=self.ambiente, + xServ="CONSULTAR NÃO ENCERRADOS", CNPJ=cnpj, ) - raiz.original_tagname_ = 'consMDFeNaoEnc' + raiz.original_tagname_ = "consMDFeNaoEnc" return self._post( raiz, - 'https://mdfe-homologacao.svrs.rs.gov.br/ws/MDFeConsNaoEnc/MDFeConsNaoEnc.asmx?wsdl', - 'mdfeConsNaoEnc', - consMDFeNaoEnc, + localizar_url(WS_MDFE_CONSULTA_NAO_ENCERRADOS, int(self.ambiente)), + "mdfeConsNaoEnc", + retConsMDFeNaoEnc, ) def envia_documento(self, edoc): @@ -92,16 +181,13 @@ def envia_documento(self, edoc): :param edoc: :return: """ - xml_string, xml_etree = self._generateds_to_string_etree(edoc) - xml_assinado = Assinatura(self.certificado).assina_xml2( - xml_etree, edoc.infMDFe.Id - ) + xml_assinado = self.assina_raiz(edoc, edoc.infMDFe.Id) - raiz = enviMDFe.TEnviMDFe( - versao='4.00', - idLote=datetime.datetime.now().strftime('%Y%m%d%H%M%S'), + raiz = retEnviMDFe.TEnviMDFe( + versao=self.versao, + idLote=datetime.datetime.now().strftime("%Y%m%d%H%M%S"), ) - raiz.original_tagname_ = 'enviMDFe' + raiz.original_tagname_ = "enviMDFe" xml_envio_string, xml_envio_etree = self._generateds_to_string_etree( raiz ) @@ -109,24 +195,119 @@ def envia_documento(self, edoc): return self._post( xml_envio_etree, - 'https://mdfe-homologacao.svrs.rs.gov.br/ws/MDFerecepcao/MDFeRecepcao.asmx?wsdl', - 'mdfeRecepcaoLote', - enviMDFe + localizar_url(WS_MDFE_RECEPCAO, int(self.ambiente)), + "mdfeRecepcaoLote", + retEnviMDFe ) - def consulta_recibo(self, numero): - raiz = consReciMDFe.TConsReciMDFe( - versao='4.00', - tpAmb='2', + def consulta_recibo(self, numero=False, proc_envio=False): + if proc_envio: + numero = proc_envio.resposta.infRec.nRec + + if not numero: + return + + raiz = retConsReciMDFe.TConsReciMDFe( + versao=self.versao, + tpAmb=self.ambiente, nRec=numero, ) - raiz.original_tagname_ = 'consReciMDFe' + raiz.original_tagname_ = "consReciMDFe" return self._post( raiz, - 'https://mdfe-homologacao.svrs.rs.gov.br/ws/MDFeRetRecepcao/MDFeRetRecepcao.asmx?wsdl', # 'ws/MDFeretautorizacao4.asmx' - 'mdfeRetRecepcao', - consReciMDFe, + localizar_url(WS_MDFE_RET_RECEPCAO, int(self.ambiente)), + "mdfeRetRecepcao", + retConsReciMDFe, + ) + + def monta_processo(self, edoc, proc_envio, proc_recibo): + mdfe = proc_envio.envio_raiz.find('{' + self._namespace + '}MDFe') + protocolos = proc_recibo.resposta.protMDFe + if mdfe and protocolos: + if type(protocolos) != list: + protocolos = [protocolos] + for protocolo in protocolos: + mdfe_proc = procMDFe.mdfeProc( + versao=self.versao, + protMDFe=protocolo, + ) + mdfe_proc.original_tagname_ = 'mdfeProc' + xml_file, mdfe_proc = self._generateds_to_string_etree(mdfe_proc) + proc_recibo.processo = mdfe_proc + proc_recibo.processo_xml = \ + self._generateds_to_string_etree(mdfe_proc)[0] + proc_recibo.protocolo = protocolo + + def send_event(self, evento): + raiz = retEventoMDFe.TEvento(versao="3.00", infEvento=evento) + raiz.original_tagname_ = "eventoMDFe" + xml_assinado = etree.fromstring(self.assina_raiz(raiz, raiz.infEvento.Id)) + + return self._post( + xml_assinado, + localizar_url(WS_MDFE_RECEPCAO_EVENTO, int(self.ambiente)), + "mdfeRecepcaoEvento", + retEventoMDFe + ) + + def cancela_documento(self, chave, protocolo_autorizacao, justificativa, + data_hora_evento=False): + tipo = "110111" + sequencia = "1" + data_hora_evento = data_hora_evento or self._hora_agora() + + cancelamento = evCancMDFe.evCancMDFe( + descEvento="Cancelamento", + nProt=protocolo_autorizacao, + xJust=justificativa ) + cancelamento_string, _ = self._generateds_to_string_etree(cancelamento) + + raiz = evCancMDFe.infEventoType( + Id="ID" + tipo + chave + sequencia.zfill(2), + cOrgao=self.uf, + tpAmb=self.ambiente, + CNPJ=chave[6:20], + chMDFe=chave, + dhEvento=data_hora_evento, + tpEvento=tipo, + nSeqEvento=sequencia, + detEvento=evCancMDFe.detEventoType( + versaoEvento="3.00", + anytypeobjs_=cancelamento_string + ), + ) + raiz.original_tagname_ = "infEvento" + return self.send_event(raiz) + + def encerra_documento(self, chave, protocolo_autorizacao, estado, municipio, + data_hora_evento=False): + tipo = "110112" + sequencia = "1" + data_hora_evento = data_hora_evento or self._hora_agora() - def cancela_documento(self): - pass + encerramento = evEncMDFe.evEncMDFe( + descEvento="Encerramento", + dtEnc=self._data_hoje(), + nProt=protocolo_autorizacao, + cUF=estado, + cMun=municipio + ) + encerramento_string, _ = self._generateds_to_string_etree(encerramento) + + raiz = evEncMDFe.infEventoType( + Id="ID" + tipo + chave + sequencia.zfill(2), + cOrgao=self.uf, + tpAmb=self.ambiente, + CNPJ=chave[6:20], + chMDFe=chave, + dhEvento=data_hora_evento, + tpEvento=tipo, + nSeqEvento=sequencia, + detEvento=evEncMDFe.detEventoType( + versaoEvento="3.00", + anytypeobjs_=encerramento_string + ), + ) + raiz.original_tagname_ = "infEvento" + return self.send_event(raiz) From f8e167064e129a4e4709fb87dfcf3770aeb17ef0 Mon Sep 17 00:00:00 2001 From: Felipe Zago Date: Mon, 2 Oct 2023 13:37:53 -0300 Subject: [PATCH 2/2] [ADD] MDFe with xsdata bindings --- src/erpbrasil/edoc/mdfe.py | 172 ++++++++++++++++--------------------- 1 file changed, 75 insertions(+), 97 deletions(-) diff --git a/src/erpbrasil/edoc/mdfe.py b/src/erpbrasil/edoc/mdfe.py index 73dbbee..60ab3cd 100644 --- a/src/erpbrasil/edoc/mdfe.py +++ b/src/erpbrasil/edoc/mdfe.py @@ -16,15 +16,34 @@ from erpbrasil.edoc.edoc import DocumentoEletronico try: - from mdfelib.v3_00 import retConsMDFeNaoEnc - from mdfelib.v3_00 import retConsReciMDFe - from mdfelib.v3_00 import retConsSitMDFe - from mdfelib.v3_00 import retConsStatServMDFe - from mdfelib.v3_00 import retEnviMDFe - from mdfelib.v3_00 import procMDFe - from mdfelib.v3_00 import retEventoMDFe - from mdfelib.v3_00 import evCancMDFe - from mdfelib.v3_00 import evEncMDFe + # Consulta Status + from nfelib.mdfe.bindings.v3_0.cons_stat_serv_mdfe_v3_00 import ConsStatServMdfe + from nfelib.mdfe.bindings.v3_0.ret_cons_stat_serv_mdfe_v3_00 import RetConsStatServMdfe + + # Consulta Documento + from nfelib.mdfe.bindings.v3_0.cons_sit_mdfe_v3_00 import ConsSitMdfe + from nfelib.mdfe.bindings.v3_0.ret_cons_sit_mdfe_v3_00 import RetConsSitMdfe + + # Consulta Não Encerrados + from nfelib.mdfe.bindings.v3_0.cons_mdfe_nao_enc_v3_00 import ConsMdfeNaoEnc + from nfelib.mdfe.bindings.v3_0.ret_cons_mdfe_nao_enc_v3_00 import RetConsMdfeNaoEnc + + # Envio + from nfelib.mdfe.bindings.v3_0.envi_mdfe_v3_00 import EnviMdfe + from nfelib.mdfe.bindings.v3_0.ret_envi_mdfe_v3_00 import RetEnviMdfe + + # Consulta Recibo + from nfelib.mdfe.bindings.v3_0.cons_reci_mdfe_v3_00 import ConsReciMdfe + from nfelib.mdfe.bindings.v3_0.ret_cons_reci_mdfe_v3_00 import RetConsReciMdfe + + # Processamento + from nfelib.mdfe.bindings.v3_0.proc_mdfe_v3_00 import MdfeProc + + # Eventos + from nfelib.mdfe.bindings.v3_0.evento_mdfe_v3_00 import EventoMdfe + from nfelib.mdfe.bindings.v3_0.ret_evento_mdfe_v3_00 import RetEventoMdfe + from nfelib.mdfe.bindings.v3_0.ev_canc_mdfe_v3_00 import EvCancMdfe + from nfelib.mdfe.bindings.v3_0.ev_enc_mdfe_v3_00 import EvEncMdfe except ImportError: pass @@ -104,6 +123,9 @@ def _verifica_resposta_envio_sucesso(self, proc_envio): return proc_envio.resposta.cStat == \ self._edoc_situacao_arquivo_recebido_com_sucesso + def _verifica_servico_em_operacao(self, proc_servico): + return proc_servico.resposta.cStat == self._edoc_situacao_servico_em_operacao + def _aguarda_tempo_medio(self, proc_envio): time.sleep(float(proc_envio.resposta.infRec.tMed) * 1.3) @@ -126,49 +148,37 @@ def monta_qrcode_contingencia(self, edoc, xml_assinado): return f"{self.monta_qrcode(chave)}&sign={digest_value_hex}" def status_servico(self): - raiz = retConsStatServMDFe.TConsStatServ( - versao=self.versao, - tpAmb=self.ambiente, - cUF=self.uf, - xServ="STATUS", - ) - raiz.original_tagname_ = "consStatServMDFe" return self._post( - raiz, + ConsStatServMdfe(tpAmb=self.ambiente, versao=self.versao), localizar_url(WS_MDFE_SITUACAO, int(self.ambiente)), "mdfeStatusServicoMDF" , - retConsStatServMDFe + RetConsStatServMdfe ) def consulta_documento(self, chave): - raiz = retConsSitMDFe.TConsSitMDFe( + raiz = ConsSitMdfe( versao=self.versao, tpAmb=self.ambiente, - xServ="CONSULTAR", chMDFe=chave, ) - raiz.original_tagname_ = "consSitMDFe" return self._post( raiz, localizar_url(WS_MDFE_CONSULTA, int(self.ambiente)), "mdfeConsultaMDF", - retConsSitMDFe + RetConsSitMdfe ) def consulta_nao_encerrados(self, cnpj): - raiz = retConsMDFeNaoEnc.TConsMDFeNaoEnc( + raiz = ConsMdfeNaoEnc( versao=self.versao, tpAmb=self.ambiente, - xServ="CONSULTAR NÃO ENCERRADOS", CNPJ=cnpj, ) - raiz.original_tagname_ = "consMDFeNaoEnc" - return self._post( raiz, localizar_url(WS_MDFE_CONSULTA_NAO_ENCERRADOS, int(self.ambiente)), "mdfeConsNaoEnc", - retConsMDFeNaoEnc, + RetConsMdfeNaoEnc, ) def envia_documento(self, edoc): @@ -181,23 +191,17 @@ def envia_documento(self, edoc): :param edoc: :return: """ - xml_assinado = self.assina_raiz(edoc, edoc.infMDFe.Id) - - raiz = retEnviMDFe.TEnviMDFe( + raiz = EnviMdfe( versao=self.versao, idLote=datetime.datetime.now().strftime("%Y%m%d%H%M%S"), + MDFe=edoc ) - raiz.original_tagname_ = "enviMDFe" - xml_envio_string, xml_envio_etree = self._generateds_to_string_etree( - raiz - ) - xml_envio_etree.append(etree.fromstring(xml_assinado)) - + xml_assinado = self.assina_raiz(raiz, edoc.infMDFe.Id) return self._post( - xml_envio_etree, + xml_assinado, localizar_url(WS_MDFE_RECEPCAO, int(self.ambiente)), "mdfeRecepcaoLote", - retEnviMDFe + RetEnviMdfe ) def consulta_recibo(self, numero=False, proc_envio=False): @@ -207,17 +211,16 @@ def consulta_recibo(self, numero=False, proc_envio=False): if not numero: return - raiz = retConsReciMDFe.TConsReciMDFe( + raiz = ConsReciMdfe( versao=self.versao, tpAmb=self.ambiente, nRec=numero, ) - raiz.original_tagname_ = "consReciMDFe" return self._post( raiz, localizar_url(WS_MDFE_RET_RECEPCAO, int(self.ambiente)), "mdfeRetRecepcao", - retConsReciMDFe, + RetConsReciMdfe, ) def monta_processo(self, edoc, proc_envio, proc_recibo): @@ -227,87 +230,62 @@ def monta_processo(self, edoc, proc_envio, proc_recibo): if type(protocolos) != list: protocolos = [protocolos] for protocolo in protocolos: - mdfe_proc = procMDFe.mdfeProc( - versao=self.versao, - protMDFe=protocolo, - ) - mdfe_proc.original_tagname_ = 'mdfeProc' - xml_file, mdfe_proc = self._generateds_to_string_etree(mdfe_proc) + mdfe_proc = MdfeProc(versao=self.versao, protMDFe=protocolo) proc_recibo.processo = mdfe_proc - proc_recibo.processo_xml = \ - self._generateds_to_string_etree(mdfe_proc)[0] + proc_recibo.processo_xml = mdfe_proc.to_xml() proc_recibo.protocolo = protocolo - def send_event(self, evento): - raiz = retEventoMDFe.TEvento(versao="3.00", infEvento=evento) - raiz.original_tagname_ = "eventoMDFe" + def envia_evento(self, evento, tipo, chave, sequencia="001", data_hora=False): + inf_evento = EventoMdfe.InfEvento( + Id="ID" + tipo + chave + sequencia.zfill(2), + cOrgao=self.uf, + tpAmb=self.ambiente, + CNPJ=chave[6:20], + chMDFe=chave, + dhEvento=data_hora or self._hora_agora(), + tpEvento=tipo, + nSeqEvento=sequencia, + detEvento=EventoMdfe.InfEvento.DetEvento( + versaoEvento="3.00", + any_element=evento + ), + ) + raiz = EventoMdfe(versao="3.00", infEvento=inf_evento) xml_assinado = etree.fromstring(self.assina_raiz(raiz, raiz.infEvento.Id)) return self._post( xml_assinado, localizar_url(WS_MDFE_RECEPCAO_EVENTO, int(self.ambiente)), "mdfeRecepcaoEvento", - retEventoMDFe + RetEventoMdfe ) def cancela_documento(self, chave, protocolo_autorizacao, justificativa, data_hora_evento=False): - tipo = "110111" - sequencia = "1" - data_hora_evento = data_hora_evento or self._hora_agora() - - cancelamento = evCancMDFe.evCancMDFe( + evento_canc = EvCancMdfe( descEvento="Cancelamento", nProt=protocolo_autorizacao, xJust=justificativa ) - cancelamento_string, _ = self._generateds_to_string_etree(cancelamento) - - raiz = evCancMDFe.infEventoType( - Id="ID" + tipo + chave + sequencia.zfill(2), - cOrgao=self.uf, - tpAmb=self.ambiente, - CNPJ=chave[6:20], - chMDFe=chave, - dhEvento=data_hora_evento, - tpEvento=tipo, - nSeqEvento=sequencia, - detEvento=evCancMDFe.detEventoType( - versaoEvento="3.00", - anytypeobjs_=cancelamento_string - ), + return self.envia_evento( + evento=evento_canc, + tipo="110111", + chave=chave, + data_hora=data_hora_evento ) - raiz.original_tagname_ = "infEvento" - return self.send_event(raiz) def encerra_documento(self, chave, protocolo_autorizacao, estado, municipio, data_hora_evento=False): - tipo = "110112" - sequencia = "1" - data_hora_evento = data_hora_evento or self._hora_agora() - - encerramento = evEncMDFe.evEncMDFe( + encerramento = EvEncMdfe( descEvento="Encerramento", dtEnc=self._data_hoje(), nProt=protocolo_autorizacao, cUF=estado, cMun=municipio ) - encerramento_string, _ = self._generateds_to_string_etree(encerramento) - - raiz = evEncMDFe.infEventoType( - Id="ID" + tipo + chave + sequencia.zfill(2), - cOrgao=self.uf, - tpAmb=self.ambiente, - CNPJ=chave[6:20], - chMDFe=chave, - dhEvento=data_hora_evento, - tpEvento=tipo, - nSeqEvento=sequencia, - detEvento=evEncMDFe.detEventoType( - versaoEvento="3.00", - anytypeobjs_=encerramento_string - ), + return self.envia_evento( + evento=encerramento, + tipo="110112", + chave=chave, + data_hora=data_hora_evento ) - raiz.original_tagname_ = "infEvento" - return self.send_event(raiz)