From e399fc2081b6ee9c703d94b6f1837d0de22fa0bb Mon Sep 17 00:00:00 2001 From: martini97 Date: Mon, 5 Sep 2016 19:21:59 -0300 Subject: [PATCH 1/3] =?UTF-8?q?Implementa=C3=A7=C3=A3o=20nota=20fiscal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pytrustnfe/Servidores.py | 4 +-- pytrustnfe/client.py | 6 ++-- pytrustnfe/nfe/__init__.py | 44 +++++++++++++++++------------ pytrustnfe/nfe/assinatura.py | 1 - pytrustnfe/nfe/comunicacao.py | 26 +++++++++-------- pytrustnfe/nfe/templates/NfeAutorizacao.xml | 2 +- pytrustnfe/utils.py | 4 +-- 7 files changed, 48 insertions(+), 39 deletions(-) diff --git a/pytrustnfe/Servidores.py b/pytrustnfe/Servidores.py index 01ba675..f43f09c 100644 --- a/pytrustnfe/Servidores.py +++ b/pytrustnfe/Servidores.py @@ -23,11 +23,11 @@ NFE_AMBIENTE_HOMOLOGACAO = 2 def localizar_url(servico, estado): - return ESTADO_WS[estado]['servidor'], ESTADO_WS[estado][servico] + return ESTADO_WS[estado][2]['servidor'], ESTADO_WS[estado][2][servico] METODO_WS = { - WS_NFE_AUTORIZACAO:{ + WS_NFE_AUTORIZACAO: { 'webservice': 'NfeAutorizacao', 'metodo': 'NfeAutorizacao', }, diff --git a/pytrustnfe/client.py b/pytrustnfe/client.py index 0867982..dbd58e8 100644 --- a/pytrustnfe/client.py +++ b/pytrustnfe/client.py @@ -43,11 +43,11 @@ class HttpClient(object): def _headers(self, action): return { u'Content-type': u'application/soap+xml; charset=utf-8; action="http://www.portalfiscal.inf.br/nfe/wsdl/%s' % action, - u'Accept': u'application/soap+xml; charset=utf-8' + u'Accept': u'application/soap+xml; charset=utf-8' } def post_soap(self, xml_soap, action): - res = requests.post(self.url, data=xml_soap, + res = requests.post(self.url, data=xml_soap, cert=(self.cert_path, self.key_path), verify=False, headers=self._headers(action)) - return res.text + return res.text diff --git a/pytrustnfe/nfe/__init__.py b/pytrustnfe/nfe/__init__.py index 5b69cd8..0c2b0a7 100644 --- a/pytrustnfe/nfe/__init__.py +++ b/pytrustnfe/nfe/__init__.py @@ -16,6 +16,7 @@ def _build_header(**kwargs): vals = {'estado': kwargs['estado'], 'soap_action': ''} return CabecalhoSoap(**vals) + def _generate_nfe_id(**kwargs): for item in kwargs['NFes']: vals = { @@ -28,71 +29,78 @@ def _generate_nfe_id(**kwargs): 'numero': item['infNFe']['ide']['nNF'], 'tipo': item['infNFe']['ide']['tpEmis'], 'codigo': item['infNFe']['ide']['cNF'], - } + } chave = ChaveNFe(**vals) - item['infNFe']['Id'] = gerar_chave(chave, 'NFe') - - + item['infNFe']['Id'] = gerar_chave(chave, 'NFe') + def _send(certificado, method, **kwargs): path = os.path.join(os.path.dirname(__file__), 'templates') xml = render_xml(path, '%s.xml' % method, **kwargs) + xml = ']>' + xml pfx_path = certificado.save_pfx() signer = Assinatura(pfx_path, certificado.password) xml_signed = signer.assina_xml(xml, kwargs['NFes'][0]['infNFe']['Id']) + xml_signed = xml_signed.replace( + '\n\n]>\n', '') + url = localizar_url(0, 'RS') cabecalho = _build_header(**kwargs) - - return executar_consulta(certificado, url, cabecalho, xml_signed) + response, obj = executar_consulta(certificado, url, cabecalho, xml_signed) + return { + 'sent_xml': xml_signed, + 'received_xml': response, + 'object': obj + } def autorizar_nfe(certificado, **kwargs): # Assinar _generate_nfe_id(**kwargs) - _send(certificado, 'NfeAutorizacao', **kwargs) + return _send(certificado, 'NfeAutorizacao', **kwargs) def retorno_autorizar_nfe(certificado, **kwargs): - _send(certificado, 'NfeRetAutorizacao', **kwargs) + return _send(certificado, 'NfeRetAutorizacao', **kwargs) def recepcao_evento_cancelamento(certificado, **kwargs): # Assinar - _send(certificado, 'RecepcaoEventoCancelamento', **kwargs) + return _send(certificado, 'RecepcaoEventoCancelamento', **kwargs) def inutilizar_nfe(certificado, **kwargs): # Assinar - _send(certificado, 'NfeInutilizacao', **kwargs) + return _send(certificado, 'NfeInutilizacao', **kwargs) def consultar_protocolo_nfe(certificado, **kwargs): - _send(certificado, 'NfeConsultaProtocolo', **kwargs) + return _send(certificado, 'NfeConsultaProtocolo', **kwargs) def nfe_status_servico(certificado, **kwargs): - _send(certificado, 'NfeStatusServico.', **kwargs) + return _send(certificado, 'NfeStatusServico.', **kwargs) def consulta_cadastro(certificado, **kwargs): - _send(certificado, 'NfeConsultaCadastro.', **kwargs) + return _send(certificado, 'NfeConsultaCadastro.', **kwargs) def recepcao_evento_carta_correcao(certificado, **kwargs): # Assinar - _send(certificado, 'RecepcaoEventoCarta.', **kwargs) + return _send(certificado, 'RecepcaoEventoCarta.', **kwargs) def recepcao_evento_manifesto(certificado, **kwargs): # Assinar - _send(certificado, 'RecepcaoEventoManifesto', **kwargs) + return _send(certificado, 'RecepcaoEventoManifesto', **kwargs) def recepcao_evento_epec(certificado, **kwargs): # Assinar - _send(certificado, 'RecepcaoEventoEPEC', **kwargs) + return _send(certificado, 'RecepcaoEventoEPEC', **kwargs) def consulta_nfe_destinada(certificado, **kwargs): - _send(certificado, 'NfeConsultaDest', **kwargs) + return _send(certificado, 'NfeConsultaDest', **kwargs) def download_nfe(certificado, **kwargs): - _send(certificado, 'NfeDownloadNF', **kwargs) + return _send(certificado, 'NfeDownloadNF', **kwargs) diff --git a/pytrustnfe/nfe/assinatura.py b/pytrustnfe/nfe/assinatura.py index 63d5f19..81831fc 100644 --- a/pytrustnfe/nfe/assinatura.py +++ b/pytrustnfe/nfe/assinatura.py @@ -38,7 +38,6 @@ class Assinatura(object): self._checar_certificado() self._inicializar_cripto() try: - xml = ']>' + xml doc_xml = libxml2.parseMemory( xml, len(xml)) diff --git a/pytrustnfe/nfe/comunicacao.py b/pytrustnfe/nfe/comunicacao.py index 9805ea4..221c6b6 100644 --- a/pytrustnfe/nfe/comunicacao.py +++ b/pytrustnfe/nfe/comunicacao.py @@ -15,14 +15,26 @@ soap_body_path = './soap:Envelope/soap:Body' soap_fault_path = './soap:Envelope/soap:Body/soap:Fault' +def _soap_xml(body): + xml = '' + xml += '' + xml += '' + xml += '433.10' + xml += '' + xml += body + xml += '' + return xml.rstrip('\n') + + def executar_consulta(certificado, url, cabecalho, xmlEnviar): cert, key = extract_cert_and_key_from_pfx(certificado.pfx, certificado.password) cert_path, key_path = save_cert_key(cert, key) url = 'https://nfe-homologacao.sefazrs.rs.gov.br/ws/NfeAutorizacao/NFeAutorizacao.asmx' web_service = 'NfeAutorizacao/nfeAutorizacaoLote' client = HttpClient(url, cert_path, key_path) - xml_retorno = client.post_soap(xmlEnviar, web_service) - + xmlEnviar = xmlEnviar.replace('', '') + xml_enviar = _soap_xml(xmlEnviar) + xml_retorno = client.post_soap(xml_enviar, web_service) return sanitize_response(xml_retorno) @@ -36,16 +48,6 @@ class Comunicacao(object): self.cert = cert self.key = key - def _soap_xml(self, body): - xml = '' - xml += '' - xml += '' - xml += '433.10' - xml += '' - xml += body - xml += '' - return xml.rstrip('\n') - def _preparar_temp_pem(self): cert_path = '/tmp/' + uuid4().hex key_path = '/tmp/' + uuid4().hex diff --git a/pytrustnfe/nfe/templates/NfeAutorizacao.xml b/pytrustnfe/nfe/templates/NfeAutorizacao.xml index 2ca11ad..f5fa1e8 100644 --- a/pytrustnfe/nfe/templates/NfeAutorizacao.xml +++ b/pytrustnfe/nfe/templates/NfeAutorizacao.xml @@ -62,7 +62,7 @@ {% endif %} {% if dest.tipo == 'company' -%} {{ dest.cnpj_cpf }} - {% endif %} + {% endif %} {{ dest.xNome }} {{ dest.enderDest.xLgr }} diff --git a/pytrustnfe/utils.py b/pytrustnfe/utils.py index 7c69009..c84ffb0 100644 --- a/pytrustnfe/utils.py +++ b/pytrustnfe/utils.py @@ -6,7 +6,7 @@ from datetime import date, datetime class CabecalhoSoap(object): - + def __init__(self, **kwargs): self.estado = kwargs.pop('estado', '') self.soap_action = kwargs.pop('soap_action', '') @@ -53,7 +53,7 @@ def gerar_chave(obj_chave, prefix=None): assert isinstance(obj_chave, ChaveNFe), "Objeto deve ser do tipo ChaveNFe" obj_chave.validar() - chave_parcial = "%s%s%s%s%s%09d%d%08d" % (obj_chave.estado, obj_chave.emissao, + chave_parcial = "%s%s%s%s%s%09d%d%s" % (obj_chave.estado, obj_chave.emissao, obj_chave.cnpj, obj_chave.modelo, obj_chave.serie.zfill(3), obj_chave.numero, obj_chave.tipo, obj_chave.codigo) From df82fcfba3b0f9793d4a03ca613354189d2e2b0c Mon Sep 17 00:00:00 2001 From: martini97 Date: Tue, 6 Sep 2016 19:16:36 -0300 Subject: [PATCH 2/3] =?UTF-8?q?Corre=C3=A7=C3=A3o=20na=20emiss=C3=A3o=20de?= =?UTF-8?q?=20nota=20fiscal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pytrustnfe/nfe/__init__.py | 11 +++++++--- pytrustnfe/nfe/assinatura.py | 51 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 3 deletions(-) diff --git a/pytrustnfe/nfe/__init__.py b/pytrustnfe/nfe/__init__.py index 0c2b0a7..824284b 100644 --- a/pytrustnfe/nfe/__init__.py +++ b/pytrustnfe/nfe/__init__.py @@ -10,6 +10,7 @@ from pytrustnfe.xml import render_xml from pytrustnfe.utils import CabecalhoSoap from pytrustnfe.utils import gerar_chave, ChaveNFe from pytrustnfe.Servidores import localizar_url +import re def _build_header(**kwargs): @@ -31,7 +32,9 @@ def _generate_nfe_id(**kwargs): 'codigo': item['infNFe']['ide']['cNF'], } chave = ChaveNFe(**vals) - item['infNFe']['Id'] = gerar_chave(chave, 'NFe') + chave_nfe = gerar_chave(chave, 'NFe') + item['infNFe']['Id'] = chave_nfe + item['infNFe']['ide']['cDV'] = chave_nfe[len(chave_nfe) - 1:] def _send(certificado, method, **kwargs): @@ -39,13 +42,15 @@ def _send(certificado, method, **kwargs): xml = render_xml(path, '%s.xml' % method, **kwargs) xml = ']>' + xml - + xml = xml.replace('\n', '') pfx_path = certificado.save_pfx() signer = Assinatura(pfx_path, certificado.password) - xml_signed = signer.assina_xml(xml, kwargs['NFes'][0]['infNFe']['Id']) + xml_signed = signer.assina_xml_nota(xml, kwargs['NFes'][0]['infNFe']['Id']) xml_signed = xml_signed.replace( '\n\n]>\n', '') + print xml_signed + xml_signed = xml_signed.replace('\n', '') url = localizar_url(0, 'RS') cabecalho = _build_header(**kwargs) diff --git a/pytrustnfe/nfe/assinatura.py b/pytrustnfe/nfe/assinatura.py index 81831fc..ad531b0 100644 --- a/pytrustnfe/nfe/assinatura.py +++ b/pytrustnfe/nfe/assinatura.py @@ -86,3 +86,54 @@ class Assinatura(object): finally: doc_xml.freeDoc() # self._finalizar_cripto() + + def assina_xml_nota(self, xml, reference): + self._checar_certificado() + self._inicializar_cripto() + try: + doc_xml = libxml2.parseMemory( + xml, len(xml)) + signNode = xmlsec.TmplSignature(doc_xml, + xmlsec.transformInclC14NId(), + xmlsec.transformRsaSha1Id(), None) + doc_xml.getRootElement().get_last().addChild(signNode) + refNode = signNode.addReference(xmlsec.transformSha1Id(), + None, '#' + str(reference), None) + + refNode.addTransform(xmlsec.transformEnvelopedId()) + refNode.addTransform(xmlsec.transformInclC14NId()) + keyInfoNode = signNode.ensureKeyInfo() + keyInfoNode.addX509Data() + + dsig_ctx = xmlsec.DSigCtx() + chave = xmlsec.cryptoAppKeyLoad(filename=str(self.arquivo), + format=xmlsec.KeyDataFormatPkcs12, + pwd=str(self.senha), + pwdCallback=None, + pwdCallbackCtx=None) + + dsig_ctx.signKey = chave + dsig_ctx.sign(signNode) + + status = dsig_ctx.status + dsig_ctx.destroy() + + if status != xmlsec.DSigStatusSucceeded: + raise RuntimeError( + 'Erro ao realizar a assinatura do arquivo; status: "' + + str(status) + + '"') + + xpath = doc_xml.xpathNewContext() + xpath.xpathRegisterNs('sig', NAMESPACE_SIG) + certificados = xpath.xpathEval( + '//sig:X509Data/sig:X509Certificate') + for i in range(len(certificados) - 1): + certificados[i].unlinkNode() + certificados[i].freeNode() + + xml = doc_xml.serialize() + return xml + finally: + doc_xml.freeDoc() + # self._finalizar_cripto() From e7249dc38d0cc1a72ff7ff99cd5213c1eefffb91 Mon Sep 17 00:00:00 2001 From: martini97 Date: Thu, 8 Sep 2016 19:56:00 -0300 Subject: [PATCH 3/3] =?UTF-8?q?Corre=C3=A7=C3=B5es=20na=20emi=C3=A7=C3=A3o?= =?UTF-8?q?=20de=20nota=20fiscal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pytrustnfe/nfe/templates/NfeAutorizacao.xml | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/pytrustnfe/nfe/templates/NfeAutorizacao.xml b/pytrustnfe/nfe/templates/NfeAutorizacao.xml index f5fa1e8..7844783 100644 --- a/pytrustnfe/nfe/templates/NfeAutorizacao.xml +++ b/pytrustnfe/nfe/templates/NfeAutorizacao.xml @@ -104,14 +104,24 @@ {% with imposto = det.imposto %} {{ imposto.vTotTrib }} + {% if imposto.ICMS.CST == '00' -%} - {{ imposto.ICMS.ICMS00.orig }} - {{ imposto.ICMS.ICMS00.CST }} - {{ imposto.ICMS.ICMS00.modBC }} - {{ imposto.ICMS.ICMS00.vBC }} - {{ imposto.ICMS.ICMS00.pICMS }} - {{ imposto.ICMS.ICMS00.vICMS }} + {{ imposto.ICMS.orig }} + {{ imposto.ICMS.CST }} + {{ imposto.ICMS.modBC }} + {{ imposto.ICMS.vBC }} + {{ imposto.ICMS.pICMS }} + {{ imposto.ICMS.vICMS }} + {% endif %} + {% if imposto.ICMS.CST == '101' -%} + + {{ imposto.ICMS.orig }} + {{ imposto.ICMS.CST }} + {{ imposto.ICMS.pCredSN }} + {{ imposto.ICMS.vCredICMSSN }} + + {% endif %} {{ imposto.IPI.cEnq }}