Browse Source

Comunicação com o webservice da prefeitura paulistana funcionando

tags/0.1.5
Danimar Ribeiro 10 years ago
parent
commit
4989a833eb
  1. 26
      pytrustnfe/certificado.py
  2. 3
      pytrustnfe/client.py
  3. 23
      pytrustnfe/nfse/paulistana/__init__.py
  4. 82
      pytrustnfe/nfse/paulistana/templates/TesteEnvioLoteRPS.xml
  5. 54
      pytrustnfe/nfse/paulistana/templates/envio_lote_rps.xml
  6. 27
      pytrustnfe/xml/__init__.py

26
pytrustnfe/certificado.py

@ -4,6 +4,7 @@ Created on Jun 16, 2015
@author: danimar @author: danimar
''' '''
from uuid import uuid4
import os.path import os.path
from OpenSSL import crypto from OpenSSL import crypto
@ -18,13 +19,28 @@ def converte_pfx_pem(pfx_stream, senha):
try: try:
certificado = crypto.load_pkcs12(pfx_stream, senha) 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: except Exception as e:
if len(e.message) == 1 and len(e.message[0]) == 3 and \ if len(e.message) == 1 and len(e.message[0]) == 3 and \
e.message[0][2] == 'mac verify failure': e.message[0][2] == 'mac verify failure':
raise Exception('Senha inválida') raise Exception('Senha inválida')
raise 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

3
pytrustnfe/client.py

@ -4,13 +4,12 @@ import suds.client
import suds_requests 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_location = '/tmp/suds'
cache = suds.cache.DocumentCache(location=cache_location) cache = suds.cache.DocumentCache(location=cache_location)
session = requests.Session() session = requests.Session()
session.cert = (cert, key) session.cert = (cert, key)
return suds.client.Client( return suds.client.Client(
base_url, base_url,
cache=cache, cache=cache,

23
pytrustnfe/nfse/paulistana/__init__.py

@ -1,7 +1,10 @@
import os 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.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): def _send(certificado, method, **kwargs):
@ -12,14 +15,16 @@ def _send(certificado, method, **kwargs):
else: else:
xml = render_xml(path, '%s.xml' % method, **kwargs) 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 return response
@ -30,7 +35,7 @@ def envio_lote_rps(certificado, **kwargs):
return _send(certificado, 'envio_lote_rps', **kwargs) return _send(certificado, 'envio_lote_rps', **kwargs)
def teste_envio_lote_rps(certificado, **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): def cancelamento_nfe(certificado, **kwargs):
return _send(certificado, 'cancelamento_n_fe', **kwargs) return _send(certificado, 'cancelamento_n_fe', **kwargs)

82
pytrustnfe/nfse/paulistana/templates/TesteEnvioLoteRPS.xml

@ -0,0 +1,82 @@
<PedidoEnvioLoteRPS xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.prefeitura.sp.gov.br/nfe">
<Cabecalho xmlns="" Versao="1">
<CPFCNPJRemetente>
<CNPJ>{{ nfse.cpf_cnpj }}</CNPJ>
</CPFCNPJRemetente>
<transacao>false</transacao>
<dtInicio>2016-02-08</dtInicio>
<dtFim>2016-02-08</dtFim>
<QtdRPS>1</QtdRPS>
<ValorTotalServicos>{{ nfse.total_servicos }}</ValorTotalServicos>
<ValorTotalDeducoes>{{ nfse.total_deducoes }}</ValorTotalDeducoes>
</Cabecalho>
{% for rps in nfse.lista_rps -%}
<RPS xmlns="">
<Assinatura>31000000OL03 00000000000120070103TNN00000000205000000000000050000002658100013167474254</Assinatura>
<ChaveRPS>
<InscricaoPrestador>{{ rps.prestador.inscricao_municipal }}</InscricaoPrestador>
<SerieRPS>{{ rps.serie }}</SerieRPS>
<NumeroRPS>{{ rps.numero }}</NumeroRPS>
</ChaveRPS>
<TipoRPS>RPS</TipoRPS>
<DataEmissao>{{ rps.data_emissao }}</DataEmissao>
<StatusRPS>N</StatusRPS>
<TributacaoRPS>T</TributacaoRPS>
<ValorServicos>1000</ValorServicos>
<ValorDeducoes>0.00</ValorDeducoes>
<ValorPIS>0.00</ValorPIS>
<ValorCOFINS>0.00</ValorCOFINS>
<ValorINSS>0.00</ValorINSS>
<ValorIR>0.00</ValorIR>
<ValorCSLL>0.00</ValorCSLL>
<CodigoServico>{{ rps.codigo_atividade }}</CodigoServico>
<AliquotaServicos>{{ rps.aliquota_atividade }}</AliquotaServicos>
<ISSRetido>false</ISSRetido>
<CPFCNPJTomador>
{% if rps.tomador.tipo_cpfcnpj == 1 -%}
<CPF>{{ rps.tomador.cpf_cnpj }}</CPF>
{% endif %}
{% if rps.tomador.tipo_cpfcnpj == 2 -%}
<CNPJ>{{ rps.tomador.cpf_cnpj }}</CNPJ>
{% endif %}
</CPFCNPJTomador>
<InscricaoMunicipalTomador>23354900</InscricaoMunicipalTomador>
<RazaoSocialTomador>{{ rps.tomador.razao_social }}</RazaoSocialTomador>
<EnderecoTomador>
<TipoLogradouro>{{ rps.tomador.tipo_logradouro }}</TipoLogradouro>
<Logradouro>{{ rps.tomador.logradouro }}</Logradouro>
<NumeroEndereco>{{ rps.tomador.numero }}</NumeroEndereco>
<ComplementoEndereco>{{ rps.tomador.complemento }}</ComplementoEndereco>
<Bairro>{{ rps.tomador.bairro }}</Bairro>
<Cidade>{{ rps.tomador.cidade }}</Cidade>
<UF>{{ rps.tomador.uf }}</UF>
<CEP>{{ rps.tomador.cep }}</CEP>
</EnderecoTomador>
<Discriminacao>{{ rps.descricao }}</Discriminacao>
</RPS>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<Reference URI="">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
<Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<DigestValue>AkHyCjCwkANg3aRAnltAXR1YQ4c=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>
IkLB0qfZLDuTNXNB83tXXsZ2TFNK9X0l7gq8jRCOcwhit059iF5gNHfmuM4NoUhyhZ+rC6UGn9lSMv1A35lofsplIuWUJO13yPtHsxaY6/rP9DTB4Ve3ihzwrEkpenANoEU1C5wLenX0lRtYc1k3fWeDmZUvv+b/M81pwoPBL8k=
</SignatureValue>
<KeyInfo>
<X509Data>
<X509Certificate>
MIIFUzCCBDugAwIBAgIQSUJS8pELZyjasDkgGzKm0TANBgkqhkiG9w0BAQUFADBuMQswCQYDVQQGEwJCUjETMBEGA1UEChMKSUNQLUJyYXNpbDEsMCoGA1UECxMjU2VjcmV0YXJpYSBkYSBSZWNlaXRhIEZlZGVyYWwgLSBTUkYxHDAaBgNVBAMTE0FDIENlcnRpU2lnbiBTUkYgVjMwHhcNMDYwNzE5MDAwMDAwWhcNMDkwNzE4MjM1OTU5WjCB1DELMAkGA1UEBhMCQlIxEzARBgNVBAoUCklDUC1CcmFzaWwxKjAoBgNVBAsTIVNlY3JldGFyaWEgZGEgUmVjZWl0YSBGZWRlcmFsLVNSRjETMBEGA1UECxQKU1JGIGUtQ05QSjELMAkGA1UECBMCUkoxFzAVBgNVBAcUDlJJTyBERSBKQU5FSVJPMUkwRwYDVQQDE0BUSVBMQU4gQ09OU1VMVE9SSUEgRSBTRVJWSUNPUyBFTSBJTkZPUk1BVElDQSBMVERBOjA0NjQyNTU0MDAwMTQzMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCx86LAoJRVmtQMzmtdWpyNgKy200+bwjtz/TuywNcTjvfw7qHFGIgTjipmuZ3zhX28CgYLYXp3tj1Dfh2B7EhjHdLJPfvoF4MgbN/dQGXmGpMpF5cNxYusOGCZiyASvI7Gqt/xE4xLSIalNr6kF6CaPLkpFgTNNe+WQkG0fMqsQQIDAQABo4ICCDCCAgQwgbEGA1UdEQSBqTCBpqA/BgVgTAEDBKA2DDQyNDA3MTk3NjA3MTM4NTM3Nzg2MDAwMDAwMDAwMDAwMDAwMDAwOTI5OTA2MjFDTkggIFJKoB8GBWBMAQMCoBYMFEZFUk5BTkRPIFNJTFZBIEJSQUdBoBkGBWBMAQMDoBAMDjA0NjQyNTU0MDAwMTQzoBEGBWBMAQMHoAgMBjIzOTU0OIEUZmJyYWdhQHRpcGxhbi5jb20uYnIwCQYDVR0TBAIwADBiBgNVHR8EWzBZMFegVaBThlFodHRwOi8vaWNwLWJyYXNpbC5jZXJ0aXNpZ24uY29tLmJyL3JlcG9zaXRvcmlvL2xjci9BQ0NlcnRpU2lnblNSRlYzL0xhdGVzdENSTC5jcmwwHwYDVR0jBBgwFoAU9p1ZXf6/xXLN3c7ELmYbLu4Iz3YwDgYDVR0PAQH/BAQDAgXgMFUGA1UdIAROMEwwSgYGYEwBAgMGMEAwPgYIKwYBBQUHAgEWMmh0dHA6Ly9pY3AtYnJhc2lsLmNlcnRpc2lnbi5jb20uYnIvcmVwb3NpdG9yaW8vZHBjMB0GA1UdJQQWMBQGCCsGAQUFBwMEBggrBgEFBQcDAjA4BggrBgEFBQcBAQQsMCowKAYIKwYBBQUHMAGGHGh0dHA6Ly9vY3NwLmNlcnRpc2lnbi5jb20uYnIwDQYJKoZIhvcNAQEFBQADggEBAC5w/CBXAykvPSbBGf+u0UPcWVJATL2ix0hCfNUVtHaCjMz8hRjgYqmhpefzDm2LCTvoCPzG6XQBYxAmnDhX1f/gyjHz+E1xJg451qtqcyCJ9861o9R2bHd4zR0DuyxCNGOTiYJ4Gc/Xa4xqECorAx5ktkk1T/HOc1K/ntRGpdL+llsO/jqSRmTOnRgdeNHcKkyXsOgL5BwxxgGNuIyqirgGXW0by4Io1GnSXtixxfvEOnqOicxBY6AcVS9HHuhmOBYiK9skAUp0Sm2v41hpsC8uIkfUeRxsJIp2CNZ4DjoyfmKwNLMCRZQAKpwMXyyHZlX1a4o/9iGTszNeeShw61g=
</X509Certificate>
</X509Data>
</KeyInfo>
</Signature>
{% endfor %}
</PedidoEnvioLoteRPS>

54
pytrustnfe/nfse/paulistana/templates/envio_lote_rps.xml

@ -1,54 +0,0 @@
<PedidoEnvioLoteRPS
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.prefeitura.sp.gov.br/nfe">
<Cabecalho Versao="1" xmlns="">
<CPFCNPJRemetente>
<CNPJ>{{ nfse.cpf_cnpj }}</CNPJ>
</CPFCNPJRemetente>
<transacao>false</transacao>
<dtInicio>2016-02-08</dtInicio>
<dtFim>2016-02-08</dtFim>
<QtdRPS>1</QtdRPS>
<ValorTotalServicos>{{ nfse.total_servicos }}</ValorTotalServicos>
<ValorTotalDeducoes>{{ nfse.total_deducoes }}</ValorTotalDeducoes>
</Cabecalho>
{% for rps in nfse.lista_rps -%}
<RPS xmlns="">
<Assinatura>{{ rps.assinatura }}</Assinatura>
<ChaveRPS>
<InscricaoPrestador>{{ rps.prestador.inscricao_municipal }}</InscricaoPrestador>
<SerieRPS>{{ rps.serie }}</SerieRPS>
<NumeroRPS>{{ rps.numero }}</NumeroRPS>
</ChaveRPS>
<TipoRPS>RPS</TipoRPS>
<DataEmissao>{{ rps.data_emissao }}</DataEmissao>
<StatusRPS>N</StatusRPS>
<TributacaoRPS>T</TributacaoRPS>
<ValorServicos>1000</ValorServicos>
<ValorDeducoes>100</ValorDeducoes>
<CodigoServico>{{ rps.codigo_atividade }}</CodigoServico>
<AliquotaServicos>{{ rps.aliquota_atividade }}</AliquotaServicos>
<ISSRetido>false</ISSRetido>
<CPFCNPJTomador>
{% if rps.tomador.tipo_cpfcnpj == 1 -%}
<CPF>{{ rps.tomador.cpf_cnpj }}</CPF>
{% endif %}
{% if rps.tomador.tipo_cpfcnpj == 2 -%}
<CNPJ>{{ rps.tomador.cpf_cnpj }}</CNPJ>
{% endif %}
</CPFCNPJTomador>
<RazaoSocialTomador>{{ rps.tomador.razao_social }}</RazaoSocialTomador>
<EnderecoTomador>
<TipoLogradouro>{{ rps.tomador.tipo_logradouro }}</TipoLogradouro>
<Logradouro>{{ rps.tomador.logradouro }}</Logradouro>
<NumeroEndereco>{{ rps.tomador.numero }}</NumeroEndereco>
<ComplementoEndereco>{{ rps.tomador.complemento }}</ComplementoEndereco>
<Bairro>{{ rps.tomador.bairro }}</Bairro>
<Cidade>{{ rps.tomador.cidade }}</Cidade>
<UF>{{ rps.tomador.uf }}</UF>
<CEP>{{ rps.tomador.cep }}</CEP>
</EnderecoTomador>
<Discriminacao>{{ rps.descricao }}</Discriminacao>
{% endfor %}
</RPS>
</PedidoEnvioLoteRPS>

27
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) parser = etree.XMLParser(remove_blank_text=True, remove_comments=True)
elem = etree.fromstring(xml, parser=parser) elem = etree.fromstring(xml, parser=parser)
return etree.tostring(elem) 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
Loading…
Cancel
Save