Browse Source

Primeira NFSe de SP emitida em ambiente de teste.

Faltam ainda validações e melhorias no código e implementação de outros metodos
tags/0.1.5
Danimar Ribeiro 9 years ago
parent
commit
0ae9da3622
  1. 9
      pytrustnfe/certificado.py
  2. 4
      pytrustnfe/nfe/__init__.py
  3. 28
      pytrustnfe/nfe/assinatura.py
  4. 42
      pytrustnfe/nfse/paulistana/__init__.py
  5. 12
      pytrustnfe/nfse/paulistana/templates/EnvioLoteRPS.xml

9
pytrustnfe/certificado.py

@ -4,7 +4,6 @@
from uuid import uuid4 from uuid import uuid4
import os.path
from OpenSSL import crypto from OpenSSL import crypto
@ -13,6 +12,13 @@ class Certificado(object):
self.pfx = pfx self.pfx = pfx
self.password = password self.password = password
def save_pfx(self):
pfx_temp = '/tmp/' + uuid4().hex
arq_temp = open(pfx_temp, 'w')
arq_temp.write(self.pfx)
arq_temp.close()
return pfx_temp
def extract_cert_and_key_from_pfx(pfx, password): def extract_cert_and_key_from_pfx(pfx, password):
pfx = crypto.load_pkcs12(pfx, password) pfx = crypto.load_pkcs12(pfx, password)
@ -25,6 +31,7 @@ def extract_cert_and_key_from_pfx(pfx, password):
return cert, key return cert, key
def save_cert_key(cert, key): def save_cert_key(cert, key):
cert_temp = '/tmp/' + uuid4().hex cert_temp = '/tmp/' + uuid4().hex
key_temp = '/tmp/' + uuid4().hex key_temp = '/tmp/' + uuid4().hex

4
pytrustnfe/nfe/__init__.py

@ -6,7 +6,7 @@
import os import os
from lxml import etree from lxml import etree
from .comunicacao import Comunicacao from .comunicacao import Comunicacao
from .assinatura import assinar, Assinatura
from .assinatura import sign_xml, Assinatura
from pytrustnfe import utils from pytrustnfe import utils
from pytrustnfe.xml import render_xml from pytrustnfe.xml import render_xml
@ -40,7 +40,7 @@ class NFe(Comunicacao):
xml = render_xml(path, 'nfeEnv.xml', **nfe) xml = render_xml(path, 'nfeEnv.xml', **nfe)
xmlElem = etree.fromstring(xml) xmlElem = etree.fromstring(xml)
xml_signed = assinar(xmlElem, self.cert, self.key, '#%s' % nfe_id)
xml_signed = sign_xml(xmlElem, self.cert, self.key, '#%s' % nfe_id)
xml_response, obj = self._executar_consulta(xml_signed) xml_response, obj = self._executar_consulta(xml_signed)

28
pytrustnfe/nfe/assinatura.py

@ -31,27 +31,21 @@ def recursively_empty(e):
return all((recursively_empty(c) for c in e.iterchildren())) return all((recursively_empty(c) for c in e.iterchildren()))
def assinar(xml, cert, key, reference):
context = etree.iterwalk(xml)
for dummy, elem in context:
parent = elem.getparent()
if recursively_empty(elem):
parent.remove(elem)
element = xml.find('{' + xml.nsmap[None] + '}NFe')
def sign_xml(xml, cert, key):
parser = etree.XMLParser(remove_blank_text=True, remove_comments=True)
elem = etree.fromstring(xml, parser=parser)
signer = XMLSigner( signer = XMLSigner(
digest_algorithm=u'sha1', signature_algorithm="rsa-sha1", digest_algorithm=u'sha1', signature_algorithm="rsa-sha1",
method=methods.enveloped,
method=methods.enveloping,
c14n_algorithm='http://www.w3.org/TR/2001/REC-xml-c14n-20010315') c14n_algorithm='http://www.w3.org/TR/2001/REC-xml-c14n-20010315')
ns = {} ns = {}
ns[None] = signer.namespaces['ds'] ns[None] = signer.namespaces['ds']
signer.namespaces = ns signer.namespaces = ns
signed_root = signer.sign(element, key=str(key), cert=cert,
reference_uri=reference)
signed_root = signer.sign(elem, key=str(key), cert=cert)
return etree.tostring(signed_root)
xml.remove(element)
xml.append(signed_root)
return etree.tostring(xml)
class Assinatura(object): class Assinatura(object):
@ -84,12 +78,12 @@ class Assinatura(object):
self._inicializar_cripto() self._inicializar_cripto()
try: try:
doc_xml = libxml2.parseMemory( doc_xml = libxml2.parseMemory(
xml.encode('utf-8'), len(xml.encode('utf-8')))
xml, len(xml))
signNode = xmlsec.TmplSignature(doc_xml, xmlsec.transformInclC14NId(), signNode = xmlsec.TmplSignature(doc_xml, xmlsec.transformInclC14NId(),
xmlsec.transformRsaSha1Id(), None) xmlsec.transformRsaSha1Id(), None)
doc_xml.getLastChild().addChild(signNode)
doc_xml.getRootElement().addChild(signNode)
refNode = signNode.addReference(xmlsec.transformSha1Id(), refNode = signNode.addReference(xmlsec.transformSha1Id(),
None, reference, None) None, reference, None)
@ -129,4 +123,4 @@ class Assinatura(object):
return xml return xml
finally: finally:
doc_xml.freeDoc() doc_xml.freeDoc()
self._finalizar_cripto()
# self._finalizar_cripto()

42
pytrustnfe/nfse/paulistana/__init__.py

@ -5,44 +5,30 @@
import os import os
import logging import logging
import suds import suds
from OpenSSL import crypto
from base64 import b64encode, b64decode
from uuid import uuid4 from uuid import uuid4
from lxml import etree
from pytrustnfe.xml import render_xml, valida_schema, sanitize_response from pytrustnfe.xml import render_xml, valida_schema, sanitize_response
from pytrustnfe.client import get_authenticated_client from pytrustnfe.client import get_authenticated_client
from pytrustnfe.certificado import extract_cert_and_key_from_pfx, save_cert_key from pytrustnfe.certificado import extract_cert_and_key_from_pfx, save_cert_key
from pytrustnfe.nfe.assinatura import Assinatura
from signxml import XMLSigner
from signxml import methods
def sign_xml(xml, cert, key):
parser = etree.XMLParser(remove_blank_text=True, remove_comments=True)
elem = etree.fromstring(xml, parser=parser)
root = etree.Element('root')
rps = elem.find('RPS')
signer = XMLSigner(
digest_algorithm=u'sha1', signature_algorithm="rsa-sha1",
method=methods.enveloped,
c14n_algorithm='http://www.w3.org/TR/2001/REC-xml-c14n-20010315')
ns = {}
ns[None] = signer.namespaces['ds']
signer.namespaces = ns
signed_root = signer.sign(rps, key=str(key), cert=cert)
root.append(
signed_root.find('{http://www.w3.org/2000/09/xmldsig#}Signature'))
elem.remove(rps)
elem.append(signed_root)
elem.append(root.find('{http://www.w3.org/2000/09/xmldsig#}Signature'))
return etree.tostring(elem)
def sign_tag(certificado, **kwargs):
pkcs12 = crypto.load_pkcs12(certificado.pfx, certificado.password)
key = pkcs12.get_privatekey()
for item in kwargs['nfse']['lista_rps']:
signed = crypto.sign(key, item['assinatura'], 'SHA1')
item['assinatura'] = b64encode(signed)
def _send(certificado, method, **kwargs): def _send(certificado, method, **kwargs):
# A little hack to test # A little hack to test
path = os.path.join(os.path.dirname(__file__), 'templates') path = os.path.join(os.path.dirname(__file__), 'templates')
if method == 'TesteEnvioLoteRPS' or method == 'EnvioLoteRPS':
sign_tag(certificado, **kwargs)
if method == 'TesteEnvioLoteRPS': if method == 'TesteEnvioLoteRPS':
xml = render_xml(path, 'EnvioLoteRPS.xml', **kwargs) xml = render_xml(path, 'EnvioLoteRPS.xml', **kwargs)
else: else:
@ -54,7 +40,9 @@ def _send(certificado, method, **kwargs):
cert_path, key_path = save_cert_key(cert, key) cert_path, key_path = save_cert_key(cert, key)
client = get_authenticated_client(base_url, cert_path, key_path) client = get_authenticated_client(base_url, cert_path, key_path)
xml_signed = sign_xml(xml, cert, key)
pfx_path = certificado.save_pfx()
signer = Assinatura(pfx_path, certificado.password)
xml_signed = signer.assina_xml(xml, '')
try: try:
response = getattr(client.service, method)(1, xml_signed) response = getattr(client.service, method)(1, xml_signed)

12
pytrustnfe/nfse/paulistana/templates/EnvioLoteRPS.xml

@ -4,8 +4,8 @@
<CNPJ>{{ nfse.cpf_cnpj }}</CNPJ> <CNPJ>{{ nfse.cpf_cnpj }}</CNPJ>
</CPFCNPJRemetente> </CPFCNPJRemetente>
<transacao>false</transacao> <transacao>false</transacao>
<dtInicio>2016-02-08</dtInicio>
<dtFim>2016-02-08</dtFim>
<dtInicio>{{ nfse.data_inicio }}</dtInicio>
<dtFim>{{ nfse.data_fim }}</dtFim>
<QtdRPS>1</QtdRPS> <QtdRPS>1</QtdRPS>
<ValorTotalServicos>{{ nfse.total_servicos }}</ValorTotalServicos> <ValorTotalServicos>{{ nfse.total_servicos }}</ValorTotalServicos>
<ValorTotalDeducoes>{{ nfse.total_deducoes }}</ValorTotalDeducoes> <ValorTotalDeducoes>{{ nfse.total_deducoes }}</ValorTotalDeducoes>
@ -22,8 +22,8 @@
<DataEmissao>{{ rps.data_emissao }}</DataEmissao> <DataEmissao>{{ rps.data_emissao }}</DataEmissao>
<StatusRPS>N</StatusRPS> <StatusRPS>N</StatusRPS>
<TributacaoRPS>T</TributacaoRPS> <TributacaoRPS>T</TributacaoRPS>
<ValorServicos>1000</ValorServicos>
<ValorDeducoes>0.00</ValorDeducoes>
<ValorServicos>{{ rps.valor_servico }}</ValorServicos>
<ValorDeducoes>{{ rps.valor_deducao }}</ValorDeducoes>
<ValorPIS>0.00</ValorPIS> <ValorPIS>0.00</ValorPIS>
<ValorCOFINS>0.00</ValorCOFINS> <ValorCOFINS>0.00</ValorCOFINS>
<ValorINSS>0.00</ValorINSS> <ValorINSS>0.00</ValorINSS>
@ -40,7 +40,9 @@
<CNPJ>{{ rps.tomador.cpf_cnpj }}</CNPJ> <CNPJ>{{ rps.tomador.cpf_cnpj }}</CNPJ>
{% endif %} {% endif %}
</CPFCNPJTomador> </CPFCNPJTomador>
<InscricaoMunicipalTomador>23354900</InscricaoMunicipalTomador>
{% if rps.tomador.inscricao_municipal -%}
<InscricaoMunicipalTomador>{{ rps.tomador.inscricao_municipal }}</InscricaoMunicipalTomador>
{% endif %}
<RazaoSocialTomador>{{ rps.tomador.razao_social }}</RazaoSocialTomador> <RazaoSocialTomador>{{ rps.tomador.razao_social }}</RazaoSocialTomador>
<EnderecoTomador> <EnderecoTomador>
<TipoLogradouro>{{ rps.tomador.tipo_logradouro }}</TipoLogradouro> <TipoLogradouro>{{ rps.tomador.tipo_logradouro }}</TipoLogradouro>

Loading…
Cancel
Save