From 3d57240edd5387c3ec9f585f9dd97bcc1073b6a6 Mon Sep 17 00:00:00 2001 From: Danimar Ribeiro Date: Tue, 2 Aug 2016 19:54:28 -0300 Subject: [PATCH] =?UTF-8?q?Implementa=C3=A7=C3=A3o=20da=20NFSe=20de=20S?= =?UTF-8?q?=C3=A3o=20Paulo=20-=20em=20andamento?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pytrustnfe/Certificado.py | 27 ----------- pytrustnfe/certificado.py | 30 ++++++++++++ pytrustnfe/client.py | 31 +++++++++++++ pytrustnfe/nfse/paulistana/__init__.py | 49 +++++++++++++------- .../nfse/paulistana/templates/cancelamento.xml | 18 ++++++++ .../nfse/paulistana/templates/consulta_cnpj.xml | 12 +++++ .../nfse/paulistana/templates/consulta_lote.xml | 10 ++++ .../paulistana/templates/consulta_nfse_por_rps.xml | 19 ++++++++ .../nfse/paulistana/templates/envio_lote_rps.xml | 54 ++++++++++++++++++++++ pytrustnfe/nfse/paulistana/templates/envio_rps.xml | 51 ++++++++++++++++++++ .../nfse/paulistana/templates/soap_header.xml | 12 +++++ pytrustnfe/xml/__init__.py | 6 +-- setup.py | 3 +- 13 files changed, 275 insertions(+), 47 deletions(-) delete mode 100644 pytrustnfe/Certificado.py create mode 100644 pytrustnfe/certificado.py create mode 100644 pytrustnfe/client.py create mode 100755 pytrustnfe/nfse/paulistana/templates/cancelamento.xml create mode 100644 pytrustnfe/nfse/paulistana/templates/consulta_cnpj.xml create mode 100755 pytrustnfe/nfse/paulistana/templates/consulta_lote.xml create mode 100755 pytrustnfe/nfse/paulistana/templates/consulta_nfse_por_rps.xml create mode 100644 pytrustnfe/nfse/paulistana/templates/envio_lote_rps.xml create mode 100644 pytrustnfe/nfse/paulistana/templates/envio_rps.xml create mode 100755 pytrustnfe/nfse/paulistana/templates/soap_header.xml diff --git a/pytrustnfe/Certificado.py b/pytrustnfe/Certificado.py deleted file mode 100644 index 1d37302..0000000 --- a/pytrustnfe/Certificado.py +++ /dev/null @@ -1,27 +0,0 @@ -# coding=utf-8 -''' -Created on Jun 16, 2015 - -@author: danimar -''' -import os.path -from OpenSSL import crypto - - -def converte_pfx_pem(caminho, senha): - if not os.path.isfile(caminho): - raise Exception('Certificado não existe') - stream = open(caminho, 'rb').read() - try: - certificado = crypto.load_pkcs12(stream, senha) - - privada = crypto.dump_privatekey(crypto.FILETYPE_PEM, - certificado.get_privatekey()) - certificado = crypto.dump_certificate(crypto.FILETYPE_PEM, - certificado.get_certificate()) - except Exception as e: - if len(e.message) == 1 and len(e.message[0]) == 3 and \ - e.message[0][2] == 'mac verify failure': - raise Exception('Senha inválida') - raise - return privada, certificado diff --git a/pytrustnfe/certificado.py b/pytrustnfe/certificado.py new file mode 100644 index 0000000..a2485b9 --- /dev/null +++ b/pytrustnfe/certificado.py @@ -0,0 +1,30 @@ +# coding=utf-8 +''' +Created on Jun 16, 2015 + +@author: danimar +''' +import os.path +from OpenSSL import crypto + + +class Certificado(object): + def __init__(self, pfx, password): + self.pfx = pfx + self.password = password + + +def converte_pfx_pem(pfx_stream, senha): + try: + certificado = crypto.load_pkcs12(pfx_stream, senha) + + privada = crypto.dump_privatekey(crypto.FILETYPE_PEM, + certificado.get_privatekey()) + certificado = crypto.dump_certificate(crypto.FILETYPE_PEM, + certificado.get_certificate()) + except Exception as e: + if len(e.message) == 1 and len(e.message[0]) == 3 and \ + e.message[0][2] == 'mac verify failure': + raise Exception('Senha inválida') + raise + return certificado, privada diff --git a/pytrustnfe/client.py b/pytrustnfe/client.py new file mode 100644 index 0000000..7e4ad4e --- /dev/null +++ b/pytrustnfe/client.py @@ -0,0 +1,31 @@ + +import requests +import suds.client +import suds_requests + + +def get_authenticated_client(base_url, key_p, cert): + cache_location = '/tmp/suds' + cache = suds.cache.DocumentCache(location=cache_location) + + session = requests.Session() + session.cert = (cert, key) + + return suds.client.Client( + base_url, + cache=cache, + transport=suds_requests.RequestsTransport(session) + ) + + +def get_client(base_url): + cache_location = '/tmp/suds' + cache = suds.cache.DocumentCache(location=cache_location) + + session = requests.Session() + + return suds.client.Client( + base_url, + cache=cache, + transport=suds_requests.RequestsTransport(session) + ) diff --git a/pytrustnfe/nfse/paulistana/__init__.py b/pytrustnfe/nfse/paulistana/__init__.py index a978e59..3a6c026 100644 --- a/pytrustnfe/nfse/paulistana/__init__.py +++ b/pytrustnfe/nfse/paulistana/__init__.py @@ -1,27 +1,45 @@ +import os +from pytrustnfe.xml import render_xml +from pytrustnfe.client import get_authenticated_client +from pytrustnfe.certificado import converte_pfx_pem -def _send(method, data): - pass - # TODO Assinar xml e retornar objeto de resposta +def _send(certificado, method, **kwargs): + # A little hack to test + path = os.path.join(os.path.dirname(__file__), 'templates') + if method == 'teste_envio_lote_rps': + xml = render_xml(path, 'envio_lote_rps.xml', **kwargs) + else: + xml = render_xml(path, '%s.xml' % method, **kwargs) + base_url = 'https://nfe.prefeitura.sp.gov.br/ws/lotenfe.asmx' -def envio_rps(data=None): - return _send('envio_rps', data) + import ipdb; ipdb.set_trace() + key, cert = converte_pfx_pem(certificado.pfx, certificado.password) + client = get_authenticated_client(base_url, key, cert) -def envio_lote_rps(data=None): - return _send('envio_lote_rps', data) + response = client.teste_envio_lote_rps(xml) -def teste_envio_lote_rps(data=None): - return _send('teste_envio_lote_rps', data) + return response -def cancelamento_nfe(data=None): - return _send('cancelamento_n_fe', data) -def consulta_nfe(data=None): - return _send('consulta_n_fe', data) +def envio_rps(certificado, **kwargs): + return _send(certificado, 'envio_rps', **kwargs) -def consulta_nfe_recebidas(data=None): - return _send('consulta_n_fe_recebidas', data) +def envio_lote_rps(certificado, **kwargs): + return _send(certificado, 'envio_lote_rps', **kwargs) + +def teste_envio_lote_rps(certificado, **kwargs): + return _send(certificado, 'teste_envio_lote_rps', **kwargs) + +def cancelamento_nfe(certificado, **kwargs): + return _send(certificado, 'cancelamento_n_fe', **kwargs) + +def consulta_nfe(certificado, **kwargs): + return _send('consulta_n_fe', **kwargs) + +def consulta_nfe_recebidas(certificado, **kwargs): + return _send('consulta_n_fe_recebidas', **kwargs) def consulta_nfe_emitidas(data=None): return _send('consulta_n_fe_emitidas', data) @@ -34,4 +52,3 @@ def consulta_informacoes_lote(data=None): def consulta_cnpj(data=None): return _send('consulta_cnpj', data) - diff --git a/pytrustnfe/nfse/paulistana/templates/cancelamento.xml b/pytrustnfe/nfse/paulistana/templates/cancelamento.xml new file mode 100755 index 0000000..886e0a6 --- /dev/null +++ b/pytrustnfe/nfse/paulistana/templates/cancelamento.xml @@ -0,0 +1,18 @@ + + + {{ cancelamento.cidade }} + {{ cancelamento.cpf_cnpj }} + {{ cancelamento.transacao }} + 1 + + + + {{ cancelamento.inscricao_municipal }} + {{ cancelamento.nota_id }} + {{ cancelamento.assinatura }} + {{ cancelamento.motivo }} + + + diff --git a/pytrustnfe/nfse/paulistana/templates/consulta_cnpj.xml b/pytrustnfe/nfse/paulistana/templates/consulta_cnpj.xml new file mode 100644 index 0000000..9b4bbcb --- /dev/null +++ b/pytrustnfe/nfse/paulistana/templates/consulta_cnpj.xml @@ -0,0 +1,12 @@ + + + + {{ nfse.cpf_cnpj }} + + + + 08944335000170 + + diff --git a/pytrustnfe/nfse/paulistana/templates/consulta_lote.xml b/pytrustnfe/nfse/paulistana/templates/consulta_lote.xml new file mode 100755 index 0000000..24afc5d --- /dev/null +++ b/pytrustnfe/nfse/paulistana/templates/consulta_lote.xml @@ -0,0 +1,10 @@ + + + {{ consulta.cidade }} + {{ consulta.cpf_cnpj }} + 1 + {{ consulta.lote }} + + \ No newline at end of file diff --git a/pytrustnfe/nfse/paulistana/templates/consulta_nfse_por_rps.xml b/pytrustnfe/nfse/paulistana/templates/consulta_nfse_por_rps.xml new file mode 100755 index 0000000..676a6fc --- /dev/null +++ b/pytrustnfe/nfse/paulistana/templates/consulta_nfse_por_rps.xml @@ -0,0 +1,19 @@ + + + {{ consulta.cidade }} + {{ consulta.cpf_cnpj }} + {{ consulta.transacao }} + 1 + + + + + {{ consulta.inscricao_municipal }} + {{ consulta.rps_id }} + {{ consulta.serie_prestacao }} + + + + diff --git a/pytrustnfe/nfse/paulistana/templates/envio_lote_rps.xml b/pytrustnfe/nfse/paulistana/templates/envio_lote_rps.xml new file mode 100644 index 0000000..e0cc849 --- /dev/null +++ b/pytrustnfe/nfse/paulistana/templates/envio_lote_rps.xml @@ -0,0 +1,54 @@ + + + + {{ nfse.cpf_cnpj }} + + false + 2016-02-08 + 2016-02-08 + 1 + {{ nfse.total_servicos }} + {{ nfse.total_deducoes }} + + {% for rps in nfse.lista_rps -%} + + {{ rps.assinatura }} + + {{ rps.prestador.inscricao_municipal }} + {{ rps.serie }} + {{ rps.numero }} + + RPS + {{ rps.data_emissao }} + N + T + 1000 + 100 + {{ rps.codigo_atividade }} + {{ rps.aliquota_atividade }} + false + + {% if rps.tomador.tipo_cpfcnpj == 1 -%} + {{ rps.tomador.cpf_cnpj }} + {% endif %} + {% if rps.tomador.tipo_cpfcnpj == 2 -%} + {{ rps.tomador.cpf_cnpj }} + {% endif %} + + {{ rps.tomador.razao_social }} + + {{ rps.tomador.tipo_logradouro }} + {{ rps.tomador.logradouro }} + {{ rps.tomador.numero }} + {{ rps.tomador.complemento }} + {{ rps.tomador.bairro }} + {{ rps.tomador.cidade }} + {{ rps.tomador.uf }} + {{ rps.tomador.cep }} + + {{ rps.descricao }} + {% endfor %} + + diff --git a/pytrustnfe/nfse/paulistana/templates/envio_rps.xml b/pytrustnfe/nfse/paulistana/templates/envio_rps.xml new file mode 100644 index 0000000..d828534 --- /dev/null +++ b/pytrustnfe/nfse/paulistana/templates/envio_rps.xml @@ -0,0 +1,51 @@ + + + + {{ nfse.cpf_cnpj }} + + + {% for rps in nfse.lista_rps -%} + + {{ rps.assinatura }} + + {{ rps.prestador.inscricao_municipal }} + {{ rps.serie }} + {{ rps.numero }} + + RPS-M + {{ rps.data_emissao }} + N + T + {{ nfse.total_servicos }} + {{ nfse.total_deducoes }} + {{ rps.valor_pis }} + {{ rps.valor_cofins }} + {{ rps.valor_inss }} + {{ rps.valor_pis }} + {{ rps.valor_csll }} + {{ rps.codigo_atividade }} + {{ rps.aliquota_atividade }} + false + + {% if rps.tomador.tipo_cpfcnpj == 1 -%} + {{ rps.tomador.cpf_cnpj }} + {% endif %} + {% if rps.tomador.tipo_cpfcnpj == 2 -%} + {{ rps.tomador.cpf_cnpj }} + {% endif %} + + {{ rps.tomador.razao_social }} + + {{ rps.tomador.tipo_logradouro }} + {{ rps.tomador.logradouro }} + {{ rps.tomador.numero }} + {{ rps.tomador.complemento }} + {{ rps.tomador.bairro }} + {{ rps.tomador.cidade }} + {{ rps.tomador.uf }} + {{ rps.tomador.cep }} + + {{ rps.descricao }} + {% endfor %} + + diff --git a/pytrustnfe/nfse/paulistana/templates/soap_header.xml b/pytrustnfe/nfse/paulistana/templates/soap_header.xml new file mode 100755 index 0000000..e9d1dd2 --- /dev/null +++ b/pytrustnfe/nfse/paulistana/templates/soap_header.xml @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/pytrustnfe/xml/__init__.py b/pytrustnfe/xml/__init__.py index 759854f..dbac3df 100644 --- a/pytrustnfe/xml/__init__.py +++ b/pytrustnfe/xml/__init__.py @@ -4,8 +4,7 @@ from jinja2 import Environment, FileSystemLoader from . import filters -def render_xml(template_name, **nfe): - path = os.path.dirname(__file__) +def render_xml(path, template_name, **nfe): env = Environment( loader=FileSystemLoader(path), extensions=['jinja2.ext.with_']) @@ -19,4 +18,5 @@ def render_xml(template_name, **nfe): xml = template.render(**nfe) xml = xml.replace('&', '&') parser = etree.XMLParser(remove_blank_text=True, remove_comments=True) - return etree.fromstring(xml, parser=parser) + elem = etree.fromstring(xml, parser=parser) + return etree.tostring(elem) diff --git a/setup.py b/setup.py index 04fa4a2..5daabff 100644 --- a/setup.py +++ b/setup.py @@ -18,7 +18,8 @@ setup( 'Topic :: Software Development :: Libraries :: Python Modules', ], packages=find_packages(exclude=['*test*']), - package_data={'pytrustnfe': ['xml/*xml']}, + package_data={'pytrustnfe': ['xml/*xml', + 'nfse/paulistana/templates/*xml']}, url='https://github.com/danimaribeiro/PyNfeTrust', license='LGPL-v2.1+', description='PyNfeTrust é uma biblioteca para envio de NF-e',