From a403b4b382c585c1ecd8dcce967135bfbd28543e Mon Sep 17 00:00:00 2001 From: Danimar Ribeiro Date: Mon, 22 Jun 2015 01:15:24 -0300 Subject: [PATCH] Adicionado arquivos de servicos NFe --- docs/conf.py | 2 +- pytrustnfe/servicos/Assinatura.py | 81 +++++++++++++++++++++++++++++ pytrustnfe/servicos/Comunicacao.py | 27 ++++++++-- pytrustnfe/servicos/NFeAutorizacao.py | 22 ++++++++ pytrustnfe/servicos/NFeDistribuicaoDFe.py | 22 ++++++++ pytrustnfe/servicos/NFeRetAutorizacao.py | 22 ++++++++ pytrustnfe/servicos/NfeConsultaCadastro.py | 22 ++++++++ pytrustnfe/servicos/NfeConsultaProtocolo.py | 22 ++++++++ pytrustnfe/servicos/NfeInutilizacao.py | 22 ++++++++ pytrustnfe/servicos/NfeStatusServico.py | 22 ++++++++ pytrustnfe/servicos/RecepcaoEvento.py | 22 ++++++++ pytrustnfe/servicos/assinatura.py | 81 ----------------------------- pytrustnfe/test/test_assinatura.py | 2 +- 13 files changed, 282 insertions(+), 87 deletions(-) create mode 100644 pytrustnfe/servicos/Assinatura.py create mode 100644 pytrustnfe/servicos/NFeAutorizacao.py create mode 100644 pytrustnfe/servicos/NFeDistribuicaoDFe.py create mode 100644 pytrustnfe/servicos/NFeRetAutorizacao.py create mode 100644 pytrustnfe/servicos/NfeConsultaCadastro.py create mode 100644 pytrustnfe/servicos/NfeConsultaProtocolo.py create mode 100644 pytrustnfe/servicos/NfeInutilizacao.py create mode 100644 pytrustnfe/servicos/NfeStatusServico.py create mode 100644 pytrustnfe/servicos/RecepcaoEvento.py delete mode 100644 pytrustnfe/servicos/assinatura.py diff --git a/docs/conf.py b/docs/conf.py index 031e292..eee3756 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -111,7 +111,7 @@ todo_include_todos = False # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'alabaster' +html_theme = 'nature' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the diff --git a/pytrustnfe/servicos/Assinatura.py b/pytrustnfe/servicos/Assinatura.py new file mode 100644 index 0000000..41204df --- /dev/null +++ b/pytrustnfe/servicos/Assinatura.py @@ -0,0 +1,81 @@ +#coding=utf-8 +''' +Created on Jun 14, 2015 + +@author: danimar +''' +import xmlsec, libxml2 +import os.path + +NAMESPACE_SIG = 'http://www.w3.org/2000/09/xmldsig#' + +class Assinatura(object): + + def __init__(self, arquivo, senha): + self.arquivo = arquivo + self.senha = senha + + def _checar_certificado(self): + if not os.path.isfile(self.arquivo): + raise Exception('Caminho do certificado não existe.') + + def _inicializar_cripto(self): + libxml2.initParser() + libxml2.substituteEntitiesDefault(1) + + xmlsec.init() + xmlsec.cryptoAppInit(None) + xmlsec.cryptoInit() + + def _finalizar_cripto(self): + xmlsec.cryptoShutdown() + xmlsec.cryptoAppShutdown() + xmlsec.shutdown() + + libxml2.cleanupParser() + + + def assina_xml(self, xml): + self._checar_certificado() + self._inicializar_cripto() + try: + doc_xml = libxml2.parseMemory(xml.encode('utf-8'), len(xml.encode('utf-8'))) + + signNode = xmlsec.TmplSignature(doc_xml, xmlsec.transformInclC14NId(), + xmlsec.transformRsaSha1Id(), None) + + + doc_xml.getRootElement().addChild(signNode) + refNode = signNode.addReference(xmlsec.transformSha1Id(), + None, '#NFe43150602261542000143550010000000761792265342', 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() diff --git a/pytrustnfe/servicos/Comunicacao.py b/pytrustnfe/servicos/Comunicacao.py index 0b891ae..1a3f984 100644 --- a/pytrustnfe/servicos/Comunicacao.py +++ b/pytrustnfe/servicos/Comunicacao.py @@ -14,12 +14,14 @@ from pytrustnfe.Certificado import converte_pfx_pem from pytrustnfe.Strings import CONSULTA_CADASTRO_COMPLETA class Comunicacao(object): - + url = '' + web_service = '' + def __init__(self, certificado, senha): self.certificado = certificado - self.senha = senha - - + self.senha = senha + + def _preparar_temp_pem(self): chave_temp = '/tmp/' + uuid4().hex certificado_temp = '/tmp/' + uuid4().hex @@ -34,7 +36,24 @@ class Comunicacao(object): arq_temp.close() return chave_temp, certificado_temp + + def _validar_dados(self): + assert self.url != '', "Url servidor não configurada" + assert self.web_service != '', "Web service não especificado" + assert self.certificado != '', "Certificado não configurado" + assert self.senha != '', "Senha não configurada" + + def _executar_consulta(self, xmlEnviar): + self._validar_dados() + chave, certificado = self._preparar_temp_pem() + + client = HttpClient(self.url, chave, certificado) + xml_retorno = client.post_xml(self.web_service, xmlEnviar) + obj = objectify.fromstring(xml_retorno) + return xml_retorno, obj + + def consulta_cadastro(self, obj_consulta): chave, certificado = self._preparar_temp_pem() diff --git a/pytrustnfe/servicos/NFeAutorizacao.py b/pytrustnfe/servicos/NFeAutorizacao.py new file mode 100644 index 0000000..e6e2cde --- /dev/null +++ b/pytrustnfe/servicos/NFeAutorizacao.py @@ -0,0 +1,22 @@ +#coding=utf-8 +''' +Created on 21/06/2015 + +@author: danimar +''' +from pytrustnfe.servicos.Comunicacao import Comunicacao +from pytrustnfe.xml import DynamicXml + + +class NfeAutorizacao(Comunicacao): + + def autorizar_nfe(self, nfe, sincrono=True): + xml = None + if isinstance(nfe, DynamicXml): + xml = nfe.render() + if isinstance(nfe, basestring): + xml = nfe + assert xml is not None, "Objeto nfe deve ser do tipo DynamicXml ou string" + + + return self._executar_consulta(xml) \ No newline at end of file diff --git a/pytrustnfe/servicos/NFeDistribuicaoDFe.py b/pytrustnfe/servicos/NFeDistribuicaoDFe.py new file mode 100644 index 0000000..f96dcdf --- /dev/null +++ b/pytrustnfe/servicos/NFeDistribuicaoDFe.py @@ -0,0 +1,22 @@ +#coding=utf-8 +''' +Created on 21/06/2015 + +@author: danimar +''' +from pytrustnfe.servicos.Comunicacao import Comunicacao +from pytrustnfe.xml import DynamicXml + + +class NfeDistribuicaoDFe(Comunicacao): + + def distribuicao(self, dfe): + xml = None + if isinstance(dfe, DynamicXml): + xml = dfe.render() + if isinstance(dfe, basestring): + xml = dfe + assert xml is not None, "Objeto recibo deve ser do tipo DynamicXml ou string" + + + return self._executar_consulta(xml) \ No newline at end of file diff --git a/pytrustnfe/servicos/NFeRetAutorizacao.py b/pytrustnfe/servicos/NFeRetAutorizacao.py new file mode 100644 index 0000000..628a422 --- /dev/null +++ b/pytrustnfe/servicos/NFeRetAutorizacao.py @@ -0,0 +1,22 @@ +#coding=utf-8 +''' +Created on 21/06/2015 + +@author: danimar +''' +from pytrustnfe.servicos.Comunicacao import Comunicacao +from pytrustnfe.xml import DynamicXml + + +class NfeRetAutorizacao(Comunicacao): + + def consulta_autorizacao(self, recibo): + xml = None + if isinstance(recibo, DynamicXml): + xml = recibo.render() + if isinstance(recibo, basestring): + xml = recibo + assert xml is not None, "Objeto recibo deve ser do tipo DynamicXml ou string" + + + return self._executar_consulta(xml) \ No newline at end of file diff --git a/pytrustnfe/servicos/NfeConsultaCadastro.py b/pytrustnfe/servicos/NfeConsultaCadastro.py new file mode 100644 index 0000000..e86fc1c --- /dev/null +++ b/pytrustnfe/servicos/NfeConsultaCadastro.py @@ -0,0 +1,22 @@ +#coding=utf-8 +''' +Created on 21/06/2015 + +@author: danimar +''' +from pytrustnfe.servicos.Comunicacao import Comunicacao +from pytrustnfe.xml import DynamicXml + + +class NfeConsultaCadastro(Comunicacao): + + def consultar_cadastro(self, cadastro): + xml = None + if isinstance(cadastro, DynamicXml): + xml = cadastro.render() + if isinstance(cadastro, basestring): + xml = cadastro + assert xml is not None, "Objeto cadastro deve ser do tipo DynamicXml ou string" + + + return self._executar_consulta(xml) \ No newline at end of file diff --git a/pytrustnfe/servicos/NfeConsultaProtocolo.py b/pytrustnfe/servicos/NfeConsultaProtocolo.py new file mode 100644 index 0000000..3a98c7f --- /dev/null +++ b/pytrustnfe/servicos/NfeConsultaProtocolo.py @@ -0,0 +1,22 @@ +#coding=utf-8 +''' +Created on 21/06/2015 + +@author: danimar +''' +from pytrustnfe.servicos.Comunicacao import Comunicacao +from pytrustnfe.xml import DynamicXml + + +class NfeConsultaProtocolo(Comunicacao): + + def consultar_protocolo(self, recibo): + xml = None + if isinstance(recibo, DynamicXml): + xml = recibo.render() + if isinstance(recibo, basestring): + xml = recibo + assert xml is not None, "Objeto recibo deve ser do tipo DynamicXml ou string" + + + return self._executar_consulta(xml) \ No newline at end of file diff --git a/pytrustnfe/servicos/NfeInutilizacao.py b/pytrustnfe/servicos/NfeInutilizacao.py new file mode 100644 index 0000000..3eab33c --- /dev/null +++ b/pytrustnfe/servicos/NfeInutilizacao.py @@ -0,0 +1,22 @@ +#coding=utf-8 +''' +Created on 21/06/2015 + +@author: danimar +''' +from pytrustnfe.servicos.Comunicacao import Comunicacao +from pytrustnfe.xml import DynamicXml + + +class NfeInutilizacao(Comunicacao): + + def inutilizar(self, inutilizacao): + xml = None + if isinstance(inutilizacao, DynamicXml): + xml = inutilizacao.render() + if isinstance(inutilizacao, basestring): + xml = inutilizacao + assert xml is not None, "Objeto inutilização deve ser do tipo DynamicXml ou string" + + + return self._executar_consulta(xml) \ No newline at end of file diff --git a/pytrustnfe/servicos/NfeStatusServico.py b/pytrustnfe/servicos/NfeStatusServico.py new file mode 100644 index 0000000..afe9d83 --- /dev/null +++ b/pytrustnfe/servicos/NfeStatusServico.py @@ -0,0 +1,22 @@ +#coding=utf-8 +''' +Created on 21/06/2015 + +@author: danimar +''' +from pytrustnfe.servicos.Comunicacao import Comunicacao +from pytrustnfe.xml import DynamicXml + + +class NfeStatusServico(Comunicacao): + + def status(self, consulta): + xml = None + if isinstance(consulta, DynamicXml): + xml = consulta.render() + if isinstance(consulta, basestring): + xml = consulta + assert xml is not None, "Objeto consulta deve ser do tipo DynamicXml ou string" + + + return self._executar_consulta(xml) \ No newline at end of file diff --git a/pytrustnfe/servicos/RecepcaoEvento.py b/pytrustnfe/servicos/RecepcaoEvento.py new file mode 100644 index 0000000..870aa79 --- /dev/null +++ b/pytrustnfe/servicos/RecepcaoEvento.py @@ -0,0 +1,22 @@ +#coding=utf-8 +''' +Created on 21/06/2015 + +@author: danimar +''' +from pytrustnfe.servicos.Comunicacao import Comunicacao +from pytrustnfe.xml import DynamicXml + + +class RecepcaoEvento(Comunicacao): + + def registrar_evento(self, evento): + xml = None + if isinstance(evento, DynamicXml): + xml = evento.render() + if isinstance(evento, basestring): + xml = evento + assert xml is not None, "Objeto recibo deve ser do tipo DynamicXml ou string" + + + return self._executar_consulta(xml) \ No newline at end of file diff --git a/pytrustnfe/servicos/assinatura.py b/pytrustnfe/servicos/assinatura.py deleted file mode 100644 index 41204df..0000000 --- a/pytrustnfe/servicos/assinatura.py +++ /dev/null @@ -1,81 +0,0 @@ -#coding=utf-8 -''' -Created on Jun 14, 2015 - -@author: danimar -''' -import xmlsec, libxml2 -import os.path - -NAMESPACE_SIG = 'http://www.w3.org/2000/09/xmldsig#' - -class Assinatura(object): - - def __init__(self, arquivo, senha): - self.arquivo = arquivo - self.senha = senha - - def _checar_certificado(self): - if not os.path.isfile(self.arquivo): - raise Exception('Caminho do certificado não existe.') - - def _inicializar_cripto(self): - libxml2.initParser() - libxml2.substituteEntitiesDefault(1) - - xmlsec.init() - xmlsec.cryptoAppInit(None) - xmlsec.cryptoInit() - - def _finalizar_cripto(self): - xmlsec.cryptoShutdown() - xmlsec.cryptoAppShutdown() - xmlsec.shutdown() - - libxml2.cleanupParser() - - - def assina_xml(self, xml): - self._checar_certificado() - self._inicializar_cripto() - try: - doc_xml = libxml2.parseMemory(xml.encode('utf-8'), len(xml.encode('utf-8'))) - - signNode = xmlsec.TmplSignature(doc_xml, xmlsec.transformInclC14NId(), - xmlsec.transformRsaSha1Id(), None) - - - doc_xml.getRootElement().addChild(signNode) - refNode = signNode.addReference(xmlsec.transformSha1Id(), - None, '#NFe43150602261542000143550010000000761792265342', 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() diff --git a/pytrustnfe/test/test_assinatura.py b/pytrustnfe/test/test_assinatura.py index 9642c63..90a0586 100644 --- a/pytrustnfe/test/test_assinatura.py +++ b/pytrustnfe/test/test_assinatura.py @@ -6,7 +6,7 @@ Created on Jun 14, 2015 ''' import unittest import os, os.path -from pytrustnfe.servicos.assinatura import Assinatura +from pytrustnfe.servicos.Assinatura import Assinatura XML_ASSINAR = '' \ '