From e3e3afd0e2b9c0a8f28d98559d8674f0d222d2dd Mon Sep 17 00:00:00 2001 From: Junior Tada Date: Fri, 31 Jul 2015 00:49:51 -0300 Subject: [PATCH] Novos webservices RS --- pynfe/entidades/evento.py | 15 ++++---- pynfe/processamento/assinatura.py | 6 ++-- pynfe/processamento/comunicacao.py | 70 ++++++++++++++++++++++--------------- pynfe/processamento/serializacao.py | 36 +++++++++---------- pynfe/utils/webservices.py | 42 +++++++++++++--------- 5 files changed, 98 insertions(+), 71 deletions(-) diff --git a/pynfe/entidades/evento.py b/pynfe/entidades/evento.py index 045f8cd..67c1204 100644 --- a/pynfe/entidades/evento.py +++ b/pynfe/entidades/evento.py @@ -26,12 +26,12 @@ class EventoCancelarNota(Evento): tp_evento = '110111' # - Sequencial do evento para o mesmo tipo de evento. Para maioria dos eventos nSeqEvento=1, nos casos em quepossa existir mais de um evento, como é o caso da Carta de Correção, o autor do evento deve numerar de forma sequencial. n_seq_evento = '1' - # - Versão do detalhe do evento (grupo detEvento – HP17), informação utilizada para a SEFAZ validar o grupo detEvento. - ver_evento = str() - # - Informações do Pedido de Cancelamento - det_evento = str() - # - Versão do Pedido de Cancelamento, deve ser informado com a mesma informação da tag verEvento (HP16) - versao = str() + # # - Versão do detalhe do evento (grupo detEvento – HP17), informação utilizada para a SEFAZ validar o grupo detEvento. + # ver_evento = str() + # # - Informações do Pedido de Cancelamento + # det_evento = str() + # # - Versão do Pedido de Cancelamento, deve ser informado com a mesma informação da tag verEvento (HP16) + # versao = str() # - descEvento descricao = 'Cancelamento' # - Informar o número do Protocolo de Autorização da NF-e a ser Cancelada. (vide item 5.8). @@ -45,8 +45,9 @@ class EventoCancelarNota(Evento): Gera o valor para o campo id A regra de formação do Id é: “ID” + tpEvento + chave da NF-e + nSeqEvento """ - self.id = "ID%(tp_evento)s%(chave)s%(n_seq_evento)s"%{ + self.id = "ID%(tp_evento)s%(um)s%(chave)s%(n_seq_evento)s"%{ 'tp_evento': self.tp_evento, + 'um': '1', 'chave': self.chave, 'n_seq_evento': self.n_seq_evento, } diff --git a/pynfe/processamento/assinatura.py b/pynfe/processamento/assinatura.py index 216aac7..fde27b6 100644 --- a/pynfe/processamento/assinatura.py +++ b/pynfe/processamento/assinatura.py @@ -34,7 +34,8 @@ class AssinaturaA1(Assinatura): siginfo = etree.SubElement(raiz, 'SignedInfo') etree.SubElement(siginfo, 'CanonicalizationMethod', Algorithm='http://www.w3.org/TR/2001/REC-xml-c14n-20010315') etree.SubElement(siginfo, 'SignatureMethod', Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1') - ref = etree.SubElement(siginfo, 'Reference', URI='#'+xml.findall('infNFe')[0].attrib['Id']) + #ref = etree.SubElement(siginfo, 'Reference', URI='#'+xml.findall('infNFe')[0].attrib['Id']) + ref = etree.SubElement(siginfo, 'Reference', URI='#'+xml.findall('infEvento')[0].attrib['Id']) trans = etree.SubElement(ref, 'Transforms') etree.SubElement(trans, 'Transform', Algorithm='http://www.w3.org/2000/09/xmldsig#enveloped-signature') etree.SubElement(trans, 'Transform', Algorithm='http://www.w3.org/TR/2001/REC-xml-c14n-20010315') @@ -49,7 +50,8 @@ class AssinaturaA1(Assinatura): with open('testes.xml', 'w') as arquivo: arquivo.write(etree.tostring(xml, encoding="unicode", pretty_print=False)) - subprocess.call(['xmlsec1', '--sign', '--pkcs12', self.certificado, '--pwd', self.senha, '--crypto', 'openssl', '--output', 'funfa.xml', '--id-attr:Id', 'infNFe', 'testes.xml']) + #subprocess.call(['xmlsec1', '--sign', '--pkcs12', self.certificado, '--pwd', self.senha, '--crypto', 'openssl', '--output', 'funfa.xml', '--id-attr:Id', 'infNFe', 'testes.xml']) + subprocess.call(['xmlsec1', '--sign', '--pkcs12', self.certificado, '--pwd', self.senha, '--crypto', 'openssl', '--output', 'funfa.xml', '--id-attr:Id', 'infEvento', 'testes.xml']) xml = etree.parse('funfa.xml').getroot() if retorna_string: diff --git a/pynfe/processamento/comunicacao.py b/pynfe/processamento/comunicacao.py index ca686cb..93aef58 100644 --- a/pynfe/processamento/comunicacao.py +++ b/pynfe/processamento/comunicacao.py @@ -78,27 +78,17 @@ class ComunicacaoSefaz(Comunicacao): return self._post(url, xml) - def cancelar(self, modelo, xml): + def cancelar(self, modelo, evento): """ Envia um evento de cancelamento de nota fiscal """ - # timezone Brasília -03:00 - tz = time.strftime("%z") - tz = "{}:{}".format(tz[:-2], tz[-2:]) # url do serviço url = self._get_url(modelo=modelo, consulta='EVENTOS') # Monta XML do corpo da requisição - raiz = etree.Element('envEvento') - #etree.SubElement(raiz, 'versao').text = self._versao # Na documentaçao 6.0 está desta forma - etree.SubElement(raiz, 'versaoDados').text = self._versao # Na documentaçao 6.0 está desta forma + raiz = etree.Element('envEvento', versao='1.00', xmlns=NAMESPACE_NFE) etree.SubElement(raiz, 'idLote').text = str(1) # numero autoincremental gerado pelo sistema - evento = etree.SubElement(raiz, 'evento') - etree.SubElement(evento, 'versao').text = '1' # versao do leiaute do evento (cancelamento = 1) - etree.SubElement(raiz, 'infEvento').text = xml # Evento, um lote pode conter até 20 eventos - dados = etree.tostring(raiz, encoding="unicode") - xml = self._construir_xml_status_pr(cabecalho=self._cabecalho_soap(metodo='RecepcaoEvento'), metodo='RecepcaoEvento', dados=dados) - xml = str(xml).replace('&','').replace('lt;','<').replace('gt;','>').replace('&','') - return xml - #return self._post(url, xml) + raiz.append(evento) + xml = self._construir_xml_status_pr(cabecalho=self._cabecalho_soap(metodo='RecepcaoEvento'), metodo='RecepcaoEvento', dados=raiz) + return self._post(url, xml) def status_servico(self, modelo): """ Verifica status do servidor da receita. """ @@ -178,26 +168,50 @@ class ComunicacaoSefaz(Comunicacao): return retorno def _get_url(self, modelo, consulta): - if self._ambiente == 1: - ambiente = 'https://' - else: - ambiente = 'https://homologacao.' - if modelo == 'nfe': - # nfe Ex: https://nfe.fazenda.pr.gov.br/nfe/NFeStatusServico3 - self.url = ambiente + NFE[self.uf.upper()][consulta] - elif modelo == 'nfce': - # nfce Ex: https://homologacao.nfce.fazenda.pr.gov.br/nfce/NFeStatusServico3 - self.url = ambiente + NFCE[self.uf.upper()][consulta] + # RS utiliza um formato de url diferente dos outros estados + if self.uf.upper() == 'RS': + if modelo == 'nfe': + if consulta == 'CADASTRO': + self.url = 'https://cad.' + NFE[self.uf.upper()][consulta] + else: + # nfe Ex: https://nfe.fazenda.pr.gov.br/nfe/NFeStatusServico3 + if self._ambiente == 1: + self.url = 'https://nfe.' + NFE[self.uf.upper()][consulta] + else: + self.url = 'https://nfe-homologacao.' + NFE[self.uf.upper()][consulta] + elif modelo == 'nfce': + # nfce Ex: https://homologacao.nfce.fazenda.pr.gov.br/nfce/NFeStatusServico3 + if self._ambiente == 1: + self.url = 'https://nfce.' + NFCE[self.uf.upper()][consulta] + else: + self.url = 'https://nfce-homologacao.' + NFCE[self.uf.upper()][consulta] + else: + # TODO implementar outros tipos de notas como NFS-e + pass else: - # TODO implementar outros tipos de notas como NFS-e - pass + if self._ambiente == 1: + ambiente = 'https://' + else: + ambiente = 'https://homologacao.' + if modelo == 'nfe': + # nfe Ex: https://nfe.fazenda.pr.gov.br/nfe/NFeStatusServico3 + self.url = ambiente + NFE[self.uf.upper()][consulta] + elif modelo == 'nfce': + # nfce Ex: https://homologacao.nfce.fazenda.pr.gov.br/nfce/NFeStatusServico3 + self.url = ambiente + NFCE[self.uf.upper()][consulta] + else: + # TODO implementar outros tipos de notas como NFS-e + pass return self.url def _cabecalho_soap(self, metodo): u"""Monta o XML do cabeçalho da requisição SOAP""" raiz = etree.Element('nfeCabecMsg', xmlns=NAMESPACE_METODO+metodo) - etree.SubElement(raiz, 'versaoDados').text = VERSAO_PADRAO + if metodo == 'RecepcaoEvento': + etree.SubElement(raiz, 'versaoDados').text = '1.00' + else: + etree.SubElement(raiz, 'versaoDados').text = VERSAO_PADRAO etree.SubElement(raiz, 'cUF').text = CODIGOS_ESTADOS[self.uf.upper()] return raiz diff --git a/pynfe/processamento/serializacao.py b/pynfe/processamento/serializacao.py index 54a58a8..f063260 100644 --- a/pynfe/processamento/serializacao.py +++ b/pynfe/processamento/serializacao.py @@ -488,28 +488,28 @@ class SerializacaoXML(Serializacao): else: return raiz - def _serializar_evento(self, evento, tag_raiz='infEvento', retorna_string=False): + def _serializar_evento(self, evento, tag_raiz='evento', retorna_string=False): # timezone Brasília -03:00 tz = time.strftime("%z") tz = "{}:{}".format(tz[:-2], tz[-2:]) - - raiz = etree.Element(tag_raiz) - etree.SubElement(raiz, 'Id').text = evento.id - etree.SubElement(raiz, 'cOrgao').text = CODIGOS_ESTADOS[evento.uf.upper()] - etree.SubElement(raiz, 'tpAmb').text = str(self._ambiente) - etree.SubElement(raiz, 'CNPJ').text = evento.cnpj - #etree.SubElement(raiz, 'CPF').text = '' - etree.SubElement(raiz, 'chNFe').text = evento.chave - etree.SubElement(raiz, 'dhEvento').text = evento.data_emissao.strftime('%Y-%m-%dT%H:%M:%S') + tz - etree.SubElement(raiz, 'tpEvento').text = evento.tp_evento - etree.SubElement(raiz, 'nSeqEvento').text = evento.n_seq_evento - etree.SubElement(raiz, 'verEvento').text = evento.ver_evento - etree.SubElement(raiz, 'detEvento').text = evento.det_evento - etree.SubElement(raiz, 'versao').text = evento.versao - etree.SubElement(raiz, 'descEvento').text = evento.descricao - etree.SubElement(raiz, 'nPro').text = evento.protocolo - etree.SubElement(raiz, 'xJust').text = evento.justificativa + #import ipdb + #ipdb.set_trace() + raiz = etree.Element(tag_raiz, versao='1.00', xmlns=NAMESPACE_NFE) + e = etree.SubElement(raiz, 'infEvento', Id=evento.identificador) + etree.SubElement(e, 'cOrgao').text = CODIGOS_ESTADOS[evento.uf.upper()] + etree.SubElement(e, 'tpAmb').text = str(self._ambiente) + etree.SubElement(e, 'CNPJ').text = evento.cnpj # Empresas somente terão CNPJ + #etree.SubElement(evento, 'CPF').text = '' + etree.SubElement(e, 'chNFe').text = evento.chave + etree.SubElement(e, 'dhEvento').text = evento.data_emissao.strftime('%Y-%m-%dT%H:%M:%S') + tz + etree.SubElement(e, 'tpEvento').text = evento.tp_evento + etree.SubElement(e, 'nSeqEvento').text = evento.n_seq_evento + etree.SubElement(e, 'verEvento').text = '1.00' + det = etree.SubElement(e, 'detEvento', versao='1.00') + etree.SubElement(det, 'descEvento').text = evento.descricao + etree.SubElement(det, 'nProt').text = evento.protocolo + etree.SubElement(det, 'xJust').text = evento.justificativa if retorna_string: return etree.tostring(raiz, encoding="unicode", pretty_print=True) diff --git a/pynfe/utils/webservices.py b/pynfe/utils/webservices.py index 4aed8b1..c7c6fbf 100644 --- a/pynfe/utils/webservices.py +++ b/pynfe/utils/webservices.py @@ -182,12 +182,12 @@ NFCE = { 'EVENTOS': '' }, 'RS': { - 'STATUS': '', - 'AUTORIZACAO': '', - 'RECIBO': '', - 'CHAVE': '', - 'INUTILIZACAO': '', - 'EVENTOS': '' + 'STATUS': 'sefazrs.rs.gov.br/ws/NfeStatusServico/NfeStatusServico2.asmx', + 'AUTORIZACAO': 'sefazrs.rs.gov.br/ws/NfeAutorizacao/NFeAutorizacao.asmx', + 'RECIBO': 'sefazrs.rs.gov.br/ws/NfeRetAutorizacao/NFeRetAutorizacao.asmx', + 'CHAVE': 'sefazrs.rs.gov.br/ws/NfeConsulta/NfeConsulta2.asmx', + 'INUTILIZACAO': 'sefazrs.rs.gov.br/ws/nfeinutilizacao/nfeinutilizacao2.asmx', + 'EVENTOS': 'sefazrs.rs.gov.br/ws/recepcaoevento/recepcaoevento.asmx' }, 'MS': { 'STATUS': '', @@ -423,17 +423,27 @@ NFE = { 'EVENTOS': '', 'CADASTRO': '' }, + # 'RS': { + # 'STATUS': 'nfe.sefaz.rs.gov.br/ws/NfeStatusServico/NfeStatusServico2.asmx', + # 'AUTORIZACAO': 'nfe.sefaz.rs.gov.br/ws/NfeAutorizacao/NFeAutorizacao.asmx', + # 'RECIBO': 'nfe.sefaz.rs.gov.br/ws/NfeRetAutorizacao/NFeRetAutorizacao.asmx', + # 'CHAVE': 'nfe.sefaz.rs.gov.br/ws/NfeConsulta/NfeConsulta2.asmx', + # 'INUTILIZACAO': 'nfe.sefaz.rs.gov.br/ws/NfeInutilizacao/NfeInutilizacao2.asmx', + # 'EVENTOS': 'nfe.sefaz.rs.gov.br/ws/recepcaoevento/recepcaoevento.asmx', + # 'CADASTRO': 'nfe.sefaz.rs.gov.br/ws/cadconsultacadastro/cadconsultacadastro2.asmx', + # 'DOWNLOAD': 'nfe.sefaz.rs.gov.br/ws/nfeDownloadNF/nfeDownloadNF.asmx', + # 'DESTINADAS': 'nfe.sefaz.rs.gov.br/ws/nfeConsultaDest/nfeConsultaDest.asmx' + # }, 'RS': { - 'STATUS': 'nfe.sefaz.rs.gov.br/ws/NfeStatusServico/NfeStatusServico2.asmx', - 'AUTORIZACAO': 'nfe.sefaz.rs.gov.br/ws/NfeAutorizacao/NFeAutorizacao.asmx', - 'RECIBO': 'nfe.sefaz.rs.gov.br/ws/NfeRetAutorizacao/NFeRetAutorizacao.asmx', - 'CHAVE': 'nfe.sefaz.rs.gov.br/ws/NfeConsulta/NfeConsulta2.asmx', - 'INUTILIZACAO': 'nfe.sefaz.rs.gov.br/ws/NfeInutilizacao/NfeInutilizacao2.asmx', - 'EVENTOS': 'nfe.sefaz.rs.gov.br/ws/recepcaoevento/recepcaoevento.asmx', - 'CADASTRO': 'nfe.sefaz.rs.gov.br/ws/cadconsultacadastro/cadconsultacadastro2.asmx', - 'EVENTOS': 'nfe.sefaz.rs.gov.br/ws/recepcaoevento/recepcaoevento.asmx', - 'DOWNLOAD': 'nfe.sefaz.rs.gov.br/ws/nfeDownloadNF/nfeDownloadNF.asmx', - 'DESTINADAS': 'nfe.sefaz.rs.gov.br/ws/nfeConsultaDest/nfeConsultaDest.asmx' + 'STATUS': 'sefazrs.rs.gov.br/ws/NfeStatusServico/NfeStatusServico2.asmx', + 'AUTORIZACAO': 'sefazrs.rs.gov.br/ws/NfeAutorizacao/NFeAutorizacao.asmx', + 'RECIBO': 'sefazrs.rs.gov.br/ws/NfeRetAutorizacao/NFeRetAutorizacao.asmx', + 'CHAVE': 'sefazrs.rs.gov.br/ws/NfeConsulta/NfeConsulta2.asmx', + 'INUTILIZACAO': 'sefazrs.rs.gov.br/ws/nfeinutilizacao/nfeinutilizacao2.asmx', + 'EVENTOS': 'sefazrs.rs.gov.br/ws/recepcaoevento/recepcaoevento.asmx', + 'CADASTRO': 'sefazrs.rs.gov.br/ws/cadconsultacadastro/cadconsultacadastro2.asmx', + 'DOWNLOAD': 'sefazrs.rs.gov.br/ws/nfeDownloadNF/nfeDownloadNF.asmx', + 'DESTINADAS': 'sefazrs.rs.gov.br/ws/nfeConsultaDest/nfeConsultaDest.asmx' }, 'MS': { 'STATUS': '',