Browse Source

Modificando para a biblioteca signxml a assinatura

tags/0.1.5
Danimar Ribeiro 10 years ago
parent
commit
41e219ac6e
  1. 4
      pytrustnfe/servicos/NfeStatusServico.py
  2. 1
      pytrustnfe/servicos/RecepcaoEvento.py
  3. 52
      pytrustnfe/servicos/assinatura.py
  4. 22
      pytrustnfe/servicos/comunicacao.py
  5. 23
      pytrustnfe/servicos/nfe_autorizacao.py
  6. 1
      pytrustnfe/utils.py
  7. 76
      pytrustnfe/xml/DynamicXml.py
  8. 22
      pytrustnfe/xml/__init__.py
  9. 58
      pytrustnfe/xml/filters.py
  10. 174
      pytrustnfe/xml/nfeEnv.xml
  11. 1
      setup.py

4
pytrustnfe/servicos/NfeStatusServico.py

@ -4,9 +4,7 @@ Created on 21/06/2015
@author: danimar @author: danimar
''' '''
from pytrustnfe.servicos.Comunicacao import Comunicacao
from pytrustnfe.xml import DynamicXml
from pytrustnfe.servicos.comunicacao import Comunicacao
class NfeStatusServico(Comunicacao): class NfeStatusServico(Comunicacao):

1
pytrustnfe/servicos/RecepcaoEvento.py

@ -5,7 +5,6 @@ Created on 21/06/2015
@author: danimar @author: danimar
''' '''
from pytrustnfe.servicos.Comunicacao import Comunicacao from pytrustnfe.servicos.Comunicacao import Comunicacao
from pytrustnfe.xml import DynamicXml
class RecepcaoEvento(Comunicacao): class RecepcaoEvento(Comunicacao):

52
pytrustnfe/servicos/Assinatura.py → pytrustnfe/servicos/assinatura.py

@ -4,11 +4,54 @@ Created on Jun 14, 2015
@author: danimar @author: danimar
''' '''
import xmlsec, libxml2
import xmlsec
import libxml2
import os.path import os.path
from signxml import xmldsig
from signxml import methods
from lxml import etree
NAMESPACE_SIG = 'http://www.w3.org/2000/09/xmldsig#' NAMESPACE_SIG = 'http://www.w3.org/2000/09/xmldsig#'
def recursively_empty(e):
if e.text:
return False
return all((recursively_empty(c) for c in e.iterchildren()))
def assinar(xml, cert, key, reference, pfx, senha):
context = etree.iterwalk(xml)
for action, elem in context:
parent = elem.getparent()
if recursively_empty(elem):
parent.remove(elem)
element = xml.find('{' + xml.nsmap[None] + '}NFe')
not_signed = etree.tostring(element)
not_signed = "<!DOCTYPE NFe [<!ATTLIST infNFe Id ID #IMPLIED>]>" + \
not_signed
signer = xmldsig(element, digest_algorithm=u'sha1')
ns = {}
ns[None] = signer.namespaces['ds']
signer.namespaces = ns
signed_root = signer.sign(
key=str(key), cert=cert, reference_uri=reference,
algorithm="rsa-sha1", method=methods.enveloped,
c14n_algorithm='http://www.w3.org/TR/2001/REC-xml-c14n-20010315')
xmldsig(signed_root, digest_algorithm=u'sha1').verify(x509_cert=cert)
# signature = Assinatura(pfx, senha)
# xmlsec = signature.assina_xml(not_signed, reference)
# xmlsec = xmlsec.replace("""<!DOCTYPE NFe [
# <!ATTLIST infNFe Id ID #IMPLIED>
# ]>\n""", "")
return etree.tostring(signed_root)
# , xmlsec
class Assinatura(object): class Assinatura(object):
def __init__(self, arquivo, senha): def __init__(self, arquivo, senha):
@ -34,14 +77,13 @@ class Assinatura(object):
libxml2.cleanupParser() libxml2.cleanupParser()
def assina_xml(self, xml):
def assina_xml(self, xml, reference):
self._checar_certificado() self._checar_certificado()
self._inicializar_cripto() self._inicializar_cripto()
try: try:
doc_xml = libxml2.parseMemory(xml.encode('utf-8'), doc_xml = libxml2.parseMemory(xml.encode('utf-8'),
len(xml.encode('utf-8'))) len(xml.encode('utf-8')))
import ipdb; ipdb.set_trace()
signNode = xmlsec.TmplSignature(doc_xml, signNode = xmlsec.TmplSignature(doc_xml,
xmlsec.transformInclC14NId(), xmlsec.transformInclC14NId(),
xmlsec.transformRsaSha1Id(), None) xmlsec.transformRsaSha1Id(), None)
@ -49,7 +91,7 @@ class Assinatura(object):
doc_xml.getRootElement().addChild(signNode) doc_xml.getRootElement().addChild(signNode)
refNode = signNode.addReference( refNode = signNode.addReference(
xmlsec.transformSha1Id(), xmlsec.transformSha1Id(),
None, '#NFe43150602261542000143550010000000761792265342', None)
None, reference, None)
refNode.addTransform(xmlsec.transformEnvelopedId()) refNode.addTransform(xmlsec.transformEnvelopedId())
refNode.addTransform(xmlsec.transformInclC14NId()) refNode.addTransform(xmlsec.transformInclC14NId())

22
pytrustnfe/servicos/Comunicacao.py → pytrustnfe/servicos/comunicacao.py

@ -7,7 +7,6 @@ Created on Jun 14, 2015
from lxml import objectify from lxml import objectify
from uuid import uuid4 from uuid import uuid4
from pytrustnfe.xml.DynamicXml import DynamicXml
from pytrustnfe.HttpClient import HttpClient from pytrustnfe.HttpClient import HttpClient
from pytrustnfe.Certificado import converte_pfx_pem from pytrustnfe.Certificado import converte_pfx_pem
@ -25,9 +24,9 @@ class Comunicacao(object):
metodo = '' metodo = ''
tag_retorno = '' tag_retorno = ''
def __init__(self, certificado, senha):
self.certificado = certificado
self.senha = senha
def __init__(self, cert, key):
self.certificado = cert
self.senha = key
def _soap_xml(self, body): def _soap_xml(self, body):
xml = '''<?xml version="1.0" encoding="utf-8"?> xml = '''<?xml version="1.0" encoding="utf-8"?>
@ -66,20 +65,15 @@ class Comunicacao(object):
assert self.metodo != '', "Método não configurado" assert self.metodo != '', "Método não configurado"
assert self.tag_retorno != '', "Tag de retorno não configurado" assert self.tag_retorno != '', "Tag de retorno não configurado"
def _validar_xml(self, obj):
xml = None
if isinstance(obj, DynamicXml):
xml = obj.render()
if isinstance(obj, basestring):
xml = obj
assert xml is not None, "Objeto deve ser do tipo DynamicXml ou string"
return xml
def _validar_nfe(self, obj):
if not isinstance(obj, dict):
raise u"Objeto deve ser um dicionário de valores"
def _executar_consulta(self, xmlEnviar): def _executar_consulta(self, xmlEnviar):
self._validar_dados() self._validar_dados()
chave, certificado = self._preparar_temp_pem()
# chave, certificado = self._preparar_temp_pem()
client = HttpClient(self.url, chave, certificado)
client = HttpClient(self.url, self.certificado, self.senha)
soap_xml = self._soap_xml(xmlEnviar) soap_xml = self._soap_xml(xmlEnviar)
xml_retorno = client.post_xml(self.web_service, soap_xml) xml_retorno = client.post_xml(self.web_service, soap_xml)

23
pytrustnfe/servicos/nfe_autorizacao.py

@ -4,17 +4,23 @@ Created on 21/06/2015
@author: danimar @author: danimar
''' '''
from pytrustnfe.servicos.Comunicacao import Comunicacao
from lxml import etree
from pytrustnfe.servicos.comunicacao import Comunicacao
from pytrustnfe import utils from pytrustnfe import utils
from pytrustnfe.xml import render_xml
from pytrustnfe.servicos.assinatura import assinar
class NfeAutorizacao(Comunicacao): class NfeAutorizacao(Comunicacao):
def __init__(self, certificado, senha):
def __init__(self, cert, key, certificado, senha):
Comunicacao.__init__(self, certificado, senha) Comunicacao.__init__(self, certificado, senha)
self.cert = cert
self.key = key
def autorizar_nfe(self, nfe): def autorizar_nfe(self, nfe):
xml = self._validar_xml(nfe)
self._validar_nfe(nfe)
xml = render_xml('nfeEnv.xml', **nfe)
self.metodo = 'NFeAutorizacao' self.metodo = 'NFeAutorizacao'
self.tag_retorno = 'retEnviNFe' self.tag_retorno = 'retEnviNFe'
@ -23,8 +29,13 @@ class NfeAutorizacao(Comunicacao):
return self._executar_consulta(xml) return self._executar_consulta(xml)
def autorizar_nfe_e_recibo(self, nfe):
xml = self._validar_xml(nfe)
def autorizar_nfe_e_recibo(self, nfe, id):
self._validar_nfe(nfe)
xml = render_xml('nfeEnv.xml', **nfe)
return assinar(xml, self.cert, self.key,
'#%s' % id,
self.certificado, self.senha)
self.metodo = 'NFeAutorizacao' self.metodo = 'NFeAutorizacao'
self.tag_retorno = 'retEnviNFe' self.tag_retorno = 'retEnviNFe'
@ -34,7 +45,7 @@ class NfeAutorizacao(Comunicacao):
xml_recibo, recibo = self._executar_consulta(xml) xml_recibo, recibo = self._executar_consulta(xml)
consulta_recibo = utils.gerar_consulta_recibo(recibo) consulta_recibo = utils.gerar_consulta_recibo(recibo)
xml = self._validar_xml(nfe)
self._validar_nfe(nfe)
self.metodo = 'NFeRetAutorizacao' self.metodo = 'NFeRetAutorizacao'
self.tag_retorno = 'retConsReciNFe' self.tag_retorno = 'retConsReciNFe'

1
pytrustnfe/utils.py

@ -6,7 +6,6 @@ Created on 22/06/2015
''' '''
from datetime import date, datetime from datetime import date, datetime
from pytrustnfe.ChaveNFe import ChaveNFe from pytrustnfe.ChaveNFe import ChaveNFe
from pytrustnfe.xml.DynamicXml import DynamicXml
def date_tostring(data): def date_tostring(data):

76
pytrustnfe/xml/DynamicXml.py

@ -1,76 +0,0 @@
# coding=utf-8
'''
Created on Jun 17, 2015
@author: danimar
'''
import xml.etree.ElementTree as ET
from lxml.etree import Element, tostring
from __builtin__ import str
class DynamicXml(object):
def __getattr__(self, name):
try:
return object.__getattribute__(self, name)
except:
self.__setattr__(name, None)
return object.__getattribute__(self, name)
def __setattr__(self, obj, val):
if(obj == "value" or obj == "atributos" or obj == "_indice"):
object.__setattr__(self, obj, val)
else:
self._indice = self._indice + 1
object.__setattr__(self, obj, DynamicXml(val, self._indice))
def __init__(self, value, indice=0):
self.value = unicode(value, 'utf-8') if isinstance(value,
str) else value
self.atributos = {}
self._indice = indice
def __str__(self):
return unicode(self.value)
def __call__(self, *args, **kw):
if(len(kw) > 0):
self.atributos = kw
if(len(args) > 0):
self.value = args[0]
else:
return self.value
def __getitem__(self, i):
if not isinstance(self.value, list):
self.value = []
if(i + 1 > len(self.value)):
self.value.append(DynamicXml(None))
return self.value[i]
def render(self, pretty_print=False):
root = Element(self.value)
self._gerar_xml(root, self)
return tostring(root, pretty_print=pretty_print)
def _gerar_xml(self, xml, objeto):
items = sorted(
objeto.__dict__.items(),
key=lambda x: x[1]._indice if isinstance(x[1], DynamicXml) else 0
)
for attr, value in items:
if(attr != "value" and attr != "atributos" and attr != "_indice"):
if isinstance(value(), list):
for item in value():
sub = ET.SubElement(xml, attr)
self._gerar_xml(sub, item)
else:
sub = ET.SubElement(xml, attr)
if(unicode(value) != u"None"):
sub.text = unicode(value)
self._gerar_xml(sub, value)
elif(attr == "atributos"):
for atr, val in value.items():
xml.set(atr.replace("__", ":"), str(val))

22
pytrustnfe/xml/__init__.py

@ -0,0 +1,22 @@
import os.path
from lxml import etree
from jinja2 import Environment, FileSystemLoader
from . import filters
def render_xml(template_name, **nfe):
path = os.path.dirname(__file__)
env = Environment(
loader=FileSystemLoader(path), extensions=['jinja2.ext.with_'])
env.filters["normalize"] = filters.normalize_str
env.filters["format_percent"] = filters.format_percent
env.filters["format_datetime"] = filters.format_datetime
env.filters["format_date"] = filters.format_date
template = env.get_template(template_name)
xml = template.render(**nfe)
xml = xml.replace('&', '&amp;')
parser = etree.XMLParser(remove_blank_text=True, remove_comments=True)
return etree.fromstring(xml, parser=parser)

58
pytrustnfe/xml/filters.py

@ -0,0 +1,58 @@
# coding=utf-8
from decimal import Decimal
from datetime import datetime
import dateutil.parser as parser_dateutil
from unicodedata import normalize
def normalize_str(string):
"""
Remove special characters and return the ascii string
"""
if string:
if not isinstance(string, unicode):
string = unicode(string, 'utf-8', 'replace')
string = string.encode('utf-8')
return normalize(
'NFKD', string.decode('utf-8')).encode('ASCII', 'ignore')
return ''
def format_percent(value):
if value:
return Decimal(value) / 100
def format_datetime(value):
"""
Format datetime
"""
dt_format = '%Y-%m-%dT%H:%M:%I'
if isinstance(value, datetime):
return value.strftime(dt_format)
try:
value = parser_dateutil.parse(value).strftime(dt_format)
except AttributeError:
pass
return value
def format_date(value):
"""
Format date
"""
dt_format = '%Y-%m-%d'
if isinstance(value, datetime):
return value.strftime(dt_format)
try:
value = parser_dateutil.parse(value).strftime(dt_format)
except AttributeError:
pass
return value

174
pytrustnfe/xml/nfeEnv.xml

@ -0,0 +1,174 @@
<enviNFe xmlns="http://www.portalfiscal.inf.br/nfe" versao="3.10">
<idLote>{{ idLote }}</idLote>
<indSinc>{{ indSinc }}</indSinc>
{% for NFe in NFes %}
<NFe xmlns="http://www.portalfiscal.inf.br/nfe">
<infNFe versao="3.10" Id="{{ NFe.infNFe.Id }}">
<ide>
{% with ide = NFe.infNFe.ide %}
<cUF>{{ ide.cUF }}</cUF>
<cNF>{{ ide.cNF }}</cNF>
<natOp>{{ ide.natOp }}</natOp>
<indPag>{{ ide.indPag }}</indPag>
<mod>{{ ide.mod }}</mod>
<serie>{{ ide.serie }}</serie>
<nNF>{{ ide.nNF }}</nNF>
<dhEmi>{{ ide.dhEmi }}</dhEmi>
<dhSaiEnt>{{ ide.dhSaiEnt }}</dhSaiEnt>
<tpNF>{{ ide.tpNF }}</tpNF>
<idDest>{{ ide.idDest }}</idDest>
<cMunFG>{{ ide.cMunFG }}</cMunFG>
<tpImp>{{ ide.tpImp }}</tpImp>
<tpEmis>{{ ide.tpEmis }}</tpEmis>
<cDV>{{ ide.cDV }}</cDV>
<tpAmb>{{ ide.tpAmb }}</tpAmb>
<finNFe>{{ ide.finNFe }}</finNFe>
<indFinal>{{ ide.indFinal }}</indFinal>
<indPres>{{ ide.indPres }}</indPres>
<procEmi>{{ ide.procEmi }}</procEmi>
<verProc>Odoo Brasil 9.0</verProc>
{% endwith %}
</ide>
<emit>
{% with emit = NFe.infNFe.emit %}
<CNPJ>{{ emit.CNPJ }}</CNPJ>
<xNome>{{ emit.xNome }}</xNome>
<xFant>{{ emit.xFant }}</xFant>
<enderEmit>
<xLgr>{{ emit.enderEmit.xLgr }}</xLgr>
<nro>{{ emit.enderEmit.nro }}</nro>
<xBairro>{{ emit.enderEmit.xBairro }}</xBairro>
<cMun>{{ emit.enderEmit.cMun }}</cMun>
<xMun>{{ emit.enderEmit.xMun }}</xMun>
<UF>{{ emit.enderEmit.UF }}</UF>
<CEP>{{ emit.enderEmit.CEP }}</CEP>
<cPais>{{ emit.enderEmit.cPais }}</cPais>
<xPais>{{ emit.enderEmit.xPais }}</xPais>
<fone>{{ emit.enderEmit.fone }}</fone>
</enderEmit>
<IE>{{ emit.IE }}</IE>
<CRT>{{ emit.CRT }}</CRT>
{% endwith %}
</emit>
<dest>
{% with dest = NFe.infNFe.dest %}
<CNPJ>{{ dest.CNPJ }}</CNPJ>
<CPF>{{ dest.CPF }}</CPF>
<xNome>{{ dest.xNome }}</xNome>
<enderDest>
<xLgr>{{ dest.enderDest.xLgr }}</xLgr>
<nro>{{ dest.enderDest.nro }}</nro>
<xBairro>{{ dest.enderDest.xBairro }}</xBairro>
<cMun>{{ dest.enderDest.cMun }}</cMun>
<xMun>{{ dest.enderDest.xMun }}</xMun>
<UF>{{ dest.enderDest.UF }}</UF>
<CEP>{{ dest.enderDest.CEP }}</CEP>
<cPais>{{ dest.enderDest.cPais }}</cPais>
<xPais>{{ dest.enderDest.xPais }}</xPais>
<fone>{{ dest.enderDest.fone }}</fone>
</enderDest>
<indIEDest>{{ dest.indIEDest }}</indIEDest>
<IE>{{ dest.IE }}</IE>
{% endwith %}
</dest>
{% for det in NFe.infNFe.detalhes %}
<det nItem="1">
<prod>
{% with prod = det.prod %}
<cProd>{{ prod.cProd }}</cProd>
<cEAN>{{ prod.cEAN }}</cEAN>
<xProd>{{ prod.xProd }}</xProd>
<NCM>{{ prod.NCM }}</NCM>
<CFOP>{{ prod.CFOP }}</CFOP>
<uCom>{{ prod.uCom }}</uCom>
<qCom>{{ prod.qCom }}</qCom>
<vUnCom>{{ prod.vUnCom }}</vUnCom>
<vProd>{{ prod.vProd }}</vProd>
<cEANTrib>{{ prod.cEANTrib }}</cEANTrib>
<uTrib>{{ prod.uTrib }}</uTrib>
<qTrib>{{ prod.qTrib }}</qTrib>
<vUnTrib>{{ prod.vUnTrib }}</vUnTrib>
<indTot>{{ prod.indTot }}</indTot>
{% endwith %}
</prod>
<imposto>
{% with imposto = det.imposto %}
<vTotTrib>{{ imposto.vTotTrib }}</vTotTrib>
<ICMS>
<ICMS00>
<orig>{{ imposto.ICMS.ICMS00.orig }}</orig>
<CST>{{ imposto.ICMS.ICMS00.CST }}</CST>
<modBC>{{ imposto.ICMS.ICMS00.modBC }}</modBC>
<vBC>{{ imposto.ICMS.ICMS00.vBC }}</vBC>
<pICMS>{{ imposto.ICMS.ICMS00.pICMS }}</pICMS>
<vICMS>{{ imposto.ICMS.ICMS00.vICMS }}</vICMS>
</ICMS00>
</ICMS>
<IPI>
<cEnq>{{ imposto.IPI.cEnq }}</cEnq>
<IPITrib>
<CST>{{ imposto.IPI.IPITrib.CST }}</CST>
<vBC>{{ imposto.IPI.IPITrib.vBC }}</vBC>
<pIPI>{{ imposto.IPI.IPITrib.pIPI }}</pIPI>
<vIPI>{{ imposto.IPI.IPITrib.vIPI }}</vIPI>
</IPITrib>
</IPI>
<PIS>
<PISAliq>
<CST>{{ imposto.PIS.PISAliq.CST }}</CST>
<vBC>{{ imposto.PIS.PISAliq.vBC }}</vBC>
<pPIS>{{ imposto.PIS.PISAliq.pPIS }}</pPIS>
<vPIS>{{ imposto.PIS.PISAliq.vPIS }}</vPIS>
</PISAliq>
</PIS>
<COFINS>
<COFINSAliq>
<CST>{{ imposto.COFINS.COFINSAliq.CST }}</CST>
<vBC>{{ imposto.COFINS.COFINSAliq.vBC }}</vBC>
<pCOFINS>{{ imposto.COFINS.COFINSAliq.pCOFINS }}</pCOFINS>
<vCOFINS>{{ imposto.COFINS.COFINSAliq.vCOFINS }}</vCOFINS>
</COFINSAliq>
</COFINS>
{% endwith %}
</imposto>
</det>
{% endfor %}
<total>
{% with total = NFe.infNFe.total %}
<ICMSTot>
<vBC>{{ total.vBC }}</vBC>
<vICMS>{{ total.vICMS }}</vICMS>
<vICMSDeson>{{ total.vICMSDeson }}</vICMSDeson>
<vBCST>{{ total.vBCST }}</vBCST>
<vST>{{ total.vST }}</vST>
<vProd>{{ total.vProd }}</vProd>
<vFrete>{{ total.vFrete }}</vFrete>
<vSeg>{{ total.vSeg }}</vSeg>
<vDesc>{{ total.vDesc }}</vDesc>
<vII>{{ total.vII }}</vII>
<vIPI>{{ total.vIPI }}</vIPI>
<vPIS>{{ total.vPIS }}</vPIS>
<vCOFINS>{{ total.vCOFINS }}</vCOFINS>
<vOutro>{{ total.vOutro }}</vOutro>
<vNF>{{ total.vNF }}</vNF>
<vTotTrib>{{ total.vTotTrib }}</vTotTrib>
</ICMSTot>
{% endwith %}
</total>
<transp>
<modFrete>{{ NFe.infNFe.transp.modFrete }}</modFrete>
</transp>
<cobr>
<dup>
<nDup>339/1</nDup>
<dVenc>2016-06-02</dVenc>
<vDup>8611.76</vDup>
</dup>
</cobr>
<infAdic>
<infCpl>{{ NFe.infNFe.infAdic.infCpl }}</infCpl>
</infAdic>
</infNFe>
</NFe>
{% endfor %}
</enviNFe>

1
setup.py

@ -18,6 +18,7 @@ setup(
'Topic :: Software Development :: Libraries :: Python Modules', 'Topic :: Software Development :: Libraries :: Python Modules',
], ],
packages=find_packages(exclude=['*test*']), packages=find_packages(exclude=['*test*']),
package_data={'pytrustnfe': ['xml/*xml']},
url='https://github.com/danimaribeiro/PyNfeTrust', url='https://github.com/danimaribeiro/PyNfeTrust',
license='LGPL-v2.1+', license='LGPL-v2.1+',
description='PyNfeTrust é uma biblioteca para envio de NF-e', description='PyNfeTrust é uma biblioteca para envio de NF-e',

Loading…
Cancel
Save