diff --git a/pytrustnfe/certificado.py b/pytrustnfe/certificado.py index a2485b9..b385a03 100644 --- a/pytrustnfe/certificado.py +++ b/pytrustnfe/certificado.py @@ -4,6 +4,7 @@ Created on Jun 16, 2015 @author: danimar ''' +from uuid import uuid4 import os.path from OpenSSL import crypto @@ -18,13 +19,28 @@ 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()) + cert = crypto.dump_certificate(crypto.FILETYPE_PEM, + certificado.get_certificate()) + key = crypto.dump_privatekey(crypto.FILETYPE_PEM, + certificado.get_privatekey()) 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 + return cert, key + + +def save_cert_key(cert, key): + cert_temp = '/tmp/' + uuid4().hex + key_temp = '/tmp/' + uuid4().hex + + arq_temp = open(cert_temp, 'w') + arq_temp.write(cert) + arq_temp.close() + + arq_temp = open(key_temp, 'w') + arq_temp.write(key) + arq_temp.close() + + return cert_temp, key_temp diff --git a/pytrustnfe/client.py b/pytrustnfe/client.py index 7e4ad4e..6bd130b 100644 --- a/pytrustnfe/client.py +++ b/pytrustnfe/client.py @@ -4,13 +4,12 @@ import suds.client import suds_requests -def get_authenticated_client(base_url, key_p, cert): +def get_authenticated_client(base_url, cert, key): 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, diff --git a/pytrustnfe/nfse/paulistana/__init__.py b/pytrustnfe/nfse/paulistana/__init__.py index 3a6c026..b31e95f 100644 --- a/pytrustnfe/nfse/paulistana/__init__.py +++ b/pytrustnfe/nfse/paulistana/__init__.py @@ -1,7 +1,10 @@ import os -from pytrustnfe.xml import render_xml +import logging +import suds +from lxml import etree +from pytrustnfe.xml import render_xml, valida_schema from pytrustnfe.client import get_authenticated_client -from pytrustnfe.certificado import converte_pfx_pem +from pytrustnfe.certificado import converte_pfx_pem, save_cert_key def _send(certificado, method, **kwargs): @@ -12,14 +15,16 @@ def _send(certificado, method, **kwargs): else: xml = render_xml(path, '%s.xml' % method, **kwargs) - base_url = 'https://nfe.prefeitura.sp.gov.br/ws/lotenfe.asmx' + base_url = 'https://nfe.prefeitura.sp.gov.br/ws/lotenfe.asmx?wsdl' - import ipdb; ipdb.set_trace() - key, cert = converte_pfx_pem(certificado.pfx, certificado.password) - client = get_authenticated_client(base_url, key, cert) - - response = client.teste_envio_lote_rps(xml) + cert, key = converte_pfx_pem(certificado.pfx, certificado.password) + cert, key = save_cert_key(cert, key)eh + client = get_authenticated_client(base_url, cert, key) + try: + response = getattr(client.service, method)(1, xml) + except suds.WebFault, e: + response = e.fault.faultstring return response @@ -30,7 +35,7 @@ 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) + return _send(certificado, 'TesteEnvioLoteRPS', **kwargs) def cancelamento_nfe(certificado, **kwargs): return _send(certificado, 'cancelamento_n_fe', **kwargs) diff --git a/pytrustnfe/nfse/paulistana/templates/TesteEnvioLoteRPS.xml b/pytrustnfe/nfse/paulistana/templates/TesteEnvioLoteRPS.xml new file mode 100644 index 0000000..f657b75 --- /dev/null +++ b/pytrustnfe/nfse/paulistana/templates/TesteEnvioLoteRPS.xml @@ -0,0 +1,82 @@ + + + + {{ nfse.cpf_cnpj }} + + false + 2016-02-08 + 2016-02-08 + 1 + {{ nfse.total_servicos }} + {{ nfse.total_deducoes }} + + {% for rps in nfse.lista_rps -%} + + 31000000OL03 00000000000120070103TNN00000000205000000000000050000002658100013167474254 + + {{ rps.prestador.inscricao_municipal }} + {{ rps.serie }} + {{ rps.numero }} + + RPS + {{ rps.data_emissao }} + N + T + 1000 + 0.00 + 0.00 + 0.00 + 0.00 + 0.00 + 0.00 + {{ 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 %} + + 23354900 + {{ 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 }} + + + + + + + + + + + +AkHyCjCwkANg3aRAnltAXR1YQ4c= + + + +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 + + + + + {% endfor %} + diff --git a/pytrustnfe/nfse/paulistana/templates/envio_lote_rps.xml b/pytrustnfe/nfse/paulistana/templates/envio_lote_rps.xml deleted file mode 100644 index e0cc849..0000000 --- a/pytrustnfe/nfse/paulistana/templates/envio_lote_rps.xml +++ /dev/null @@ -1,54 +0,0 @@ - - - - {{ 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/xml/__init__.py b/pytrustnfe/xml/__init__.py index dbac3df..8f5c95a 100644 --- a/pytrustnfe/xml/__init__.py +++ b/pytrustnfe/xml/__init__.py @@ -20,3 +20,30 @@ def render_xml(path, template_name, **nfe): parser = etree.XMLParser(remove_blank_text=True, remove_comments=True) elem = etree.fromstring(xml, parser=parser) return etree.tostring(elem) + + +def valida_schema(xml, arquivo_xsd): + '''Função que valida um XML usando lxml do Python via arquivo XSD''' + # Carrega o esquema XML do arquivo XSD + xsd = etree.XMLSchema(file=arquivo_xsd) + # Converte o XML passado em XML do lxml + xml = etree.fromstring(str(xml)) + # Verifica a validade do xml + erros = [] + if not xsd(xml): + # Caso tenha erros, cria uma lista de erros + for erro in xsd.error_log: + erros.append({ + 'message': erro.message, + 'domain': erro.domain, + 'type': erro.type, + 'level': erro.level, + 'line': erro.line, + 'column': erro.column, + 'filename': erro.filename, + 'domain_name': erro.domain_name, + 'type_name': erro.type_name, + 'level_name': erro.level_name + }) + # Retorna os erros, sendo uma lista vazia caso não haja erros + return erros