Browse Source

Nova forma de serialização para NFS-e

pull/7/head
Leonardo Tada 10 years ago
parent
commit
3417612f7e
  1. 1126
      pynfe/data/XSDs/NFS-e/nfse_v202.xsd
  2. 316
      pynfe/data/XSDs/NFS-e/xmldsig-core-schema20020212.xsd
  3. 1
      pynfe/entidades/servico.py
  4. 11
      pynfe/processamento/assinatura.py
  5. 78
      pynfe/processamento/serializacao.py
  6. 18
      pynfe/processamento/validacao.py
  7. 2211
      pynfe/utils/_dsig.py
  8. 7780
      pynfe/utils/nfse_v202.py
  9. 8
      tests/valida.py

1126
pynfe/data/XSDs/NFS-e/nfse_v202.xsd
File diff suppressed because it is too large
View File

316
pynfe/data/XSDs/NFS-e/xmldsig-core-schema20020212.xsd

@ -0,0 +1,316 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Schema for XML Signatures
http://www.w3.org/2000/09/xmldsig#
$Revision: 1.7 $ on $Date: 2007/09/20 19:06:50 $ by $Author: p052373 $
Copyright 2001 The Internet Society and W3C (Massachusetts Institute
of Technology, Institut National de Recherche en Informatique et en
Automatique, Keio University). All Rights Reserved.
http://www.w3.org/Consortium/Legal/
This document is governed by the W3C Software License [1] as described
in the FAQ [2].
[1] http://www.w3.org/Consortium/Legal/copyright-software-19980720
[2] http://www.w3.org/Consortium/Legal/IPR-FAQ-20000620.html#DTD
-->
<!--
Download em 23/02/2007
Link:
http://www.w3.org/TR/xmldsig-core/#sec-Schema
http://www.w3.org/TR/xmldsig-core/xmldsig-core-schema.xsd
-->
<schema xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
targetNamespace="http://www.w3.org/2000/09/xmldsig#"
version="0.1" elementFormDefault="qualified">
<!-- Basic Types Defined for Signatures -->
<simpleType name="CryptoBinary">
<restriction base="base64Binary">
</restriction>
</simpleType>
<!-- Start Signature -->
<element name="Signature" type="ds:SignatureType"/>
<complexType name="SignatureType">
<sequence>
<element ref="ds:SignedInfo"/>
<element ref="ds:SignatureValue"/>
<element ref="ds:KeyInfo" minOccurs="0"/>
<element ref="ds:Object" minOccurs="0" maxOccurs="unbounded"/>
</sequence>
<attribute name="Id" type="ID" use="optional"/>
</complexType>
<element name="SignatureValue" type="ds:SignatureValueType"/>
<complexType name="SignatureValueType">
<simpleContent>
<extension base="base64Binary">
<attribute name="Id" type="ID" use="optional"/>
</extension>
</simpleContent>
</complexType>
<!-- Start SignedInfo -->
<element name="SignedInfo" type="ds:SignedInfoType"/>
<complexType name="SignedInfoType">
<sequence>
<element ref="ds:CanonicalizationMethod"/>
<element ref="ds:SignatureMethod"/>
<element ref="ds:Reference" maxOccurs="unbounded"/>
</sequence>
<attribute name="Id" type="ID" use="optional"/>
</complexType>
<element name="CanonicalizationMethod" type="ds:CanonicalizationMethodType"/>
<complexType name="CanonicalizationMethodType" mixed="true">
<sequence>
<any namespace="##any" minOccurs="0" maxOccurs="unbounded"/>
<!-- (0,unbounded) elements from (1,1) namespace -->
</sequence>
<attribute name="Algorithm" type="anyURI" use="required"/>
</complexType>
<element name="SignatureMethod" type="ds:SignatureMethodType"/>
<complexType name="SignatureMethodType" mixed="true">
<sequence>
<element name="HMACOutputLength" minOccurs="0" type="ds:HMACOutputLengthType"/>
<any namespace="##other" minOccurs="0" maxOccurs="unbounded"/>
<!-- (0,unbounded) elements from (1,1) external namespace -->
</sequence>
<attribute name="Algorithm" type="anyURI" use="required"/>
</complexType>
<!-- Start Reference -->
<element name="Reference" type="ds:ReferenceType"/>
<complexType name="ReferenceType">
<sequence>
<element ref="ds:Transforms" minOccurs="0"/>
<element ref="ds:DigestMethod"/>
<element ref="ds:DigestValue"/>
</sequence>
<attribute name="Id" type="ID" use="optional"/>
<attribute name="URI" type="anyURI" use="optional"/>
<attribute name="Type" type="anyURI" use="optional"/>
</complexType>
<element name="Transforms" type="ds:TransformsType"/>
<complexType name="TransformsType">
<sequence>
<element ref="ds:Transform" maxOccurs="unbounded"/>
</sequence>
</complexType>
<element name="Transform" type="ds:TransformType"/>
<complexType name="TransformType" mixed="true">
<choice minOccurs="0" maxOccurs="unbounded">
<any namespace="##other" processContents="lax"/>
<!-- (1,1) elements from (0,unbounded) namespaces -->
<element name="XPath" type="string"/>
</choice>
<attribute name="Algorithm" type="anyURI" use="required"/>
</complexType>
<!-- End Reference -->
<element name="DigestMethod" type="ds:DigestMethodType"/>
<complexType name="DigestMethodType" mixed="true">
<sequence>
<any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
</sequence>
<attribute name="Algorithm" type="anyURI" use="required"/>
</complexType>
<element name="DigestValue" type="ds:DigestValueType"/>
<simpleType name="DigestValueType">
<restriction base="base64Binary"/>
</simpleType>
<!-- End SignedInfo -->
<!-- Start KeyInfo -->
<element name="KeyInfo" type="ds:KeyInfoType"/>
<complexType name="KeyInfoType" mixed="true">
<choice maxOccurs="unbounded">
<element ref="ds:KeyName"/>
<element ref="ds:KeyValue"/>
<element ref="ds:RetrievalMethod"/>
<element ref="ds:X509Data"/>
<element ref="ds:PGPData"/>
<element ref="ds:SPKIData"/>
<element ref="ds:MgmtData"/>
<any processContents="lax" namespace="##other"/>
<!-- (1,1) elements from (0,unbounded) namespaces -->
</choice>
<attribute name="Id" type="ID" use="optional"/>
</complexType>
<element name="KeyName" type="string"/>
<element name="MgmtData" type="string"/>
<element name="KeyValue" type="ds:KeyValueType"/>
<complexType name="KeyValueType" mixed="true">
<choice>
<element ref="ds:DSAKeyValue"/>
<element ref="ds:RSAKeyValue"/>
<any namespace="##other" processContents="lax"/>
</choice>
</complexType>
<element name="RetrievalMethod" type="ds:RetrievalMethodType"/>
<complexType name="RetrievalMethodType">
<sequence>
<element ref="ds:Transforms" minOccurs="0"/>
</sequence>
<attribute name="URI" type="anyURI"/>
<attribute name="Type" type="anyURI" use="optional"/>
</complexType>
<!-- Start X509Data -->
<element name="X509Data" type="ds:X509DataType"/>
<complexType name="X509DataType">
<sequence maxOccurs="unbounded">
<choice>
<element name="X509IssuerSerial" type="ds:X509IssuerSerialType"/>
<element name="X509SKI" type="base64Binary"/>
<element name="X509SubjectName" type="string"/>
<element name="X509Certificate" type="base64Binary"/>
<element name="X509CRL" type="base64Binary"/>
<any namespace="##other" processContents="lax"/>
</choice>
</sequence>
</complexType>
<complexType name="X509IssuerSerialType">
<sequence>
<element name="X509IssuerName" type="string"/>
<element name="X509SerialNumber" type="integer"/>
</sequence>
</complexType>
<!-- End X509Data -->
<!-- Begin PGPData -->
<element name="PGPData" type="ds:PGPDataType"/>
<complexType name="PGPDataType">
<choice>
<sequence>
<element name="PGPKeyID" type="base64Binary"/>
<element name="PGPKeyPacket" type="base64Binary" minOccurs="0"/>
<any namespace="##other" processContents="lax" minOccurs="0"
maxOccurs="unbounded"/>
</sequence>
<sequence>
<element name="PGPKeyPacket" type="base64Binary"/>
<any namespace="##other" processContents="lax" minOccurs="0"
maxOccurs="unbounded"/>
</sequence>
</choice>
</complexType>
<!-- End PGPData -->
<!-- Begin SPKIData -->
<element name="SPKIData" type="ds:SPKIDataType"/>
<complexType name="SPKIDataType">
<sequence maxOccurs="unbounded">
<element name="SPKISexp" type="base64Binary"/>
<any namespace="##other" processContents="lax" minOccurs="0"/>
</sequence>
</complexType>
<!-- End SPKIData -->
<!-- End KeyInfo -->
<!-- Start Object (Manifest, SignatureProperty) -->
<element name="Object" type="ds:ObjectType"/>
<complexType name="ObjectType" mixed="true">
<sequence minOccurs="0" maxOccurs="unbounded">
<any namespace="##any" processContents="lax"/>
</sequence>
<attribute name="Id" type="ID" use="optional"/>
<attribute name="MimeType" type="string" use="optional"/> <!-- add a grep facet -->
<attribute name="Encoding" type="anyURI" use="optional"/>
</complexType>
<element name="Manifest" type="ds:ManifestType"/>
<complexType name="ManifestType">
<sequence>
<element ref="ds:Reference" maxOccurs="unbounded"/>
</sequence>
<attribute name="Id" type="ID" use="optional"/>
</complexType>
<element name="SignatureProperties" type="ds:SignaturePropertiesType"/>
<complexType name="SignaturePropertiesType">
<sequence>
<element ref="ds:SignatureProperty" maxOccurs="unbounded"/>
</sequence>
<attribute name="Id" type="ID" use="optional"/>
</complexType>
<element name="SignatureProperty" type="ds:SignaturePropertyType"/>
<complexType name="SignaturePropertyType" mixed="true">
<choice maxOccurs="unbounded">
<any namespace="##other" processContents="lax"/>
<!-- (1,1) elements from (1,unbounded) namespaces -->
</choice>
<attribute name="Target" type="anyURI" use="required"/>
<attribute name="Id" type="ID" use="optional"/>
</complexType>
<!-- End Object (Manifest, SignatureProperty) -->
<!-- Start Algorithm Parameters -->
<simpleType name="HMACOutputLengthType">
<restriction base="integer"/>
</simpleType>
<!-- Start KeyValue Element-types -->
<element name="DSAKeyValue" type="ds:DSAKeyValueType"/>
<complexType name="DSAKeyValueType">
<sequence>
<sequence minOccurs="0">
<element name="P" type="ds:CryptoBinary"/>
<element name="Q" type="ds:CryptoBinary"/>
</sequence>
<element name="G" type="ds:CryptoBinary" minOccurs="0"/>
<element name="Y" type="ds:CryptoBinary"/>
<element name="J" type="ds:CryptoBinary" minOccurs="0"/>
<sequence minOccurs="0">
<element name="Seed" type="ds:CryptoBinary"/>
<element name="PgenCounter" type="ds:CryptoBinary"/>
</sequence>
</sequence>
</complexType>
<element name="RSAKeyValue" type="ds:RSAKeyValueType"/>
<complexType name="RSAKeyValueType">
<sequence>
<element name="Modulus" type="ds:CryptoBinary"/>
<element name="Exponent" type="ds:CryptoBinary"/>
</sequence>
</complexType>
<!-- End KeyValue Element-types -->
<!-- End Signature -->
</schema>

1
pynfe/entidades/servico.py

@ -27,6 +27,7 @@ class Servico(Entidade):
exigibilidade = int() exigibilidade = int()
# Lista com todos os codigos divididos por estados na pasta data/MunIBGE # Lista com todos os codigos divididos por estados na pasta data/MunIBGE
codigo_municipio = str() codigo_municipio = str()
municipio_incidencia = str()
def __str__(self): def __str__(self):
return self.discriminacao return self.discriminacao

11
pynfe/processamento/assinatura.py

@ -19,6 +19,7 @@ class Assinatura(object):
"""Efetua a assinatura da nota""" """Efetua a assinatura da nota"""
pass pass
class AssinaturaA1(Assinatura): class AssinaturaA1(Assinatura):
"""Classe responsavel por efetuar a assinatura do certificado """Classe responsavel por efetuar a assinatura do certificado
digital no XML informado.""" digital no XML informado."""
@ -26,7 +27,7 @@ class AssinaturaA1(Assinatura):
def assinar(self, xml, retorna_string=False): def assinar(self, xml, retorna_string=False):
try: try:
# No raiz do XML de saida # No raiz do XML de saida
tag = 'infNFe'; # tag que será assinada
tag = 'infNFe' # tag que será assinada
raiz = etree.Element('Signature', xmlns='http://www.w3.org/2000/09/xmldsig#') raiz = etree.Element('Signature', xmlns='http://www.w3.org/2000/09/xmldsig#')
siginfo = etree.SubElement(raiz, 'SignedInfo') siginfo = etree.SubElement(raiz, 'SignedInfo')
etree.SubElement(siginfo, 'CanonicalizationMethod', Algorithm='http://www.w3.org/TR/2001/REC-xml-c14n-20010315') etree.SubElement(siginfo, 'CanonicalizationMethod', Algorithm='http://www.w3.org/TR/2001/REC-xml-c14n-20010315')
@ -65,15 +66,16 @@ class AssinaturaA1(Assinatura):
def assinarNfse(self, xml, retorna_string=False): def assinarNfse(self, xml, retorna_string=False):
try: try:
xml = etree.fromstring(xml)
# No raiz do XML de saida # No raiz do XML de saida
tag = 'InfDeclaracaoPrestacaoServico'; # tag que será assinada
tag = 'InfDeclaracaoPrestacaoServico' # tag que será assinada
raiz = etree.Element('Signature', xmlns='http://www.w3.org/2000/09/xmldsig#') raiz = etree.Element('Signature', xmlns='http://www.w3.org/2000/09/xmldsig#')
siginfo = etree.SubElement(raiz, 'SignedInfo') siginfo = etree.SubElement(raiz, 'SignedInfo')
etree.SubElement(siginfo, 'CanonicalizationMethod', Algorithm='http://www.w3.org/TR/2001/REC-xml-c14n-20010315') etree.SubElement(siginfo, 'CanonicalizationMethod', Algorithm='http://www.w3.org/TR/2001/REC-xml-c14n-20010315')
etree.SubElement(siginfo, 'SignatureMethod', Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1') etree.SubElement(siginfo, 'SignatureMethod', Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1')
# Tenta achar a tag infNFe # Tenta achar a tag infNFe
ref = etree.SubElement(siginfo, 'Reference', URI='#'+xml.xpath('Rps/InfDeclaracaoPrestacaoServico')[0].attrib['Id'])
# TODO a proxima linha nao eh encontrada pq precisa colocar o namespace, GerarNfseEnvio.
ref = etree.SubElement(siginfo, 'Reference', URI='#'+xml.xpath('GerarNfseEnvio/Rps/InfDeclaracaoPrestacaoServico')[0].attrib['Id'])
trans = etree.SubElement(ref, 'Transforms') trans = etree.SubElement(ref, 'Transforms')
etree.SubElement(trans, 'Transform', Algorithm='http://www.w3.org/2000/09/xmldsig#enveloped-signature') etree.SubElement(trans, 'Transform', Algorithm='http://www.w3.org/2000/09/xmldsig#enveloped-signature')
@ -100,4 +102,3 @@ class AssinaturaA1(Assinatura):
return xml return xml
except Exception as e: except Exception as e:
raise e raise e

78
pynfe/processamento/serializacao.py

@ -1,12 +1,13 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import time import time
from pynfe.entidades import NotaFiscal from pynfe.entidades import NotaFiscal
from pynfe.utils import etree, so_numeros, obter_municipio_por_codigo, \ from pynfe.utils import etree, so_numeros, obter_municipio_por_codigo, \
obter_pais_por_codigo, obter_municipio_e_codigo, \
formatar_decimal, remover_acentos, obter_uf_por_codigo, obter_codigo_por_municipio
obter_pais_por_codigo, obter_municipio_e_codigo, formatar_decimal, \
remover_acentos, obter_uf_por_codigo, obter_codigo_por_municipio
from pynfe.utils.flags import CODIGOS_ESTADOS, VERSAO_PADRAO, NAMESPACE_NFE, NAMESPACE_BETHA from pynfe.utils.flags import CODIGOS_ESTADOS, VERSAO_PADRAO, NAMESPACE_NFE, NAMESPACE_BETHA
from pynfe.utils import nfse_v202 as nfse_schema
class Serializacao(object): class Serializacao(object):
"""Classe abstrata responsavel por fornecer as funcionalidades basicas para """Classe abstrata responsavel por fornecer as funcionalidades basicas para
@ -44,6 +45,7 @@ class Serializacao(object):
raise NotImplementedError raise NotImplementedError
class SerializacaoXML(Serializacao): class SerializacaoXML(Serializacao):
_versao = VERSAO_PADRAO _versao = VERSAO_PADRAO
@ -575,6 +577,7 @@ class SerializacaoXML(Serializacao):
else: else:
return raiz return raiz
class SerializacaoNfse(Serializacao): class SerializacaoNfse(Serializacao):
def exportar(self): def exportar(self):
@ -583,6 +586,75 @@ class SerializacaoNfse(Serializacao):
def importar(self): def importar(self):
pass pass
def _serializarGerar(self, nfse):
"""Retorna string de um XML gerado a partir do
XML Schema (XSD). Binding gerado pelo modulo PyXB."""
servico = nfse_schema.tcDadosServico()
valores_servico = nfse_schema.tcValoresDeclaracaoServico()
valores_servico.ValorServicos = nfse.servico.valor_servico
servico.IssRetido = nfse.servico.iss_retido
servico.ItemListaServico = nfse.servico.item_lista
servico.Discriminacao = nfse.servico.discriminacao
servico.CodigoMunicipio = nfse.servico.codigo_municipio
servico.ExigibilidadeISS = nfse.servico.exigibilidade
servico.MunicipioIncidencia = nfse.servico.municipio_incidencia
servico.Valores = valores_servico
# Prestador
id_prestador = nfse_schema.tcIdentificacaoPrestador()
id_prestador.CpfCnpj = nfse.emitente.cnpj
id_prestador.InscricaoMunicipal = nfse.emitente.inscricao_municipal
# Cliente
id_tomador = nfse_schema.tcIdentificacaoTomador()
id_tomador.CpfCnpj = nfse.cliente.numero_documento
if nfse.cliente.inscricao_municipal:
id_tomador.InscricaoMunicipal = nfse.cliente.inscricao_municipal
endereco_tomador = nfse_schema.tcEndereco()
endereco_tomador.Endereco = nfse.cliente.endereco_logradouro
endereco_tomador.Numero = nfse.cliente.endereco_numero
endereco_tomador.Bairro = nfse.cliente.endereco_bairro
endereco_tomador.CodigoMunicipio = nfse.cliente.endereco_cod_municipio
endereco_tomador.Uf = nfse.cliente.endereco_uf
endereco_tomador.CodigoPais = nfse.cliente.endereco_pais
endereco_tomador.Cep = nfse.cliente.endereco_cep
tomador = nfse_schema.tcDadosTomador()
tomador.IdentificacaoPrestador = id_tomador
tomador.RazaoSocial = nfse.cliente.razao_social
tomador.Endereco = endereco_tomador
id_rps = nfse_schema.tcIdentificacaoRps()
id_rps.Numero = nfse.identificador
id_rps.Serie = nfse.serie
id_rps.Tipo = nfse.tipo
rps = nfse_schema.tcInfRps()
rps.IdentificacaoRps = id_rps
rps.DataEmissao = nfse.data_emissao.strftime('%Y-%m-%d')
rps.Status = 1
inf_declaracao_servico = nfse_schema.tcInfDeclaracaoPrestacaoServico()
inf_declaracao_servico.Competencia = nfse.data_emissao.strftime('%Y-%m-%d')
inf_declaracao_servico.Servico = servico
inf_declaracao_servico.Prestador = id_prestador
inf_declaracao_servico.Tomador = tomador
inf_declaracao_servico.OptanteSimplesNacional = nfse.simples
inf_declaracao_servico.IncentivoFiscal = nfse.incentivo
inf_declaracao_servico.Id = nfse.identificador
inf_declaracao_servico.Rps = rps
declaracao_servico = nfse_schema.tcDeclaracaoPrestacaoServico()
declaracao_servico.InfDeclaracaoPrestacaoServico = inf_declaracao_servico
gnfse = nfse_schema.GerarNfseEnvio()
gnfse.Rps = declaracao_servico
return gnfse.toxml(element_name='GerarNfseEnvio').replace('ns1:', '').replace(':ns1', '')
def _serializar_emitente(self, emitente, tag_raiz='Prestador', retorna_string=False): def _serializar_emitente(self, emitente, tag_raiz='Prestador', retorna_string=False):
raiz = etree.Element(tag_raiz) raiz = etree.Element(tag_raiz)
documento = etree.SubElement(raiz, 'CpfCnpj') documento = etree.SubElement(raiz, 'CpfCnpj')

18
pynfe/processamento/validacao.py

@ -5,23 +5,7 @@ from os import path
try: try:
from lxml import etree from lxml import etree
except ImportError: except ImportError:
try:
# Python 2.5 - cElementTree
import xml.etree.cElementTree as etree
except ImportError:
try:
# Python 2.5 - ElementTree
import xml.etree.ElementTree as etree
except ImportError:
try:
# Instalacao normal do cElementTree
import cElementTree as etree
except ImportError:
try:
# Instalacao normal do ElementTree
import elementtree.ElementTree as etree
except ImportError:
raise Exception('Falhou ao importar lxml/ElementTree')
raise Exception('Falhou ao importar modulo lxml')
XSD_FOLDER = "pynfe/data/XSDs/" XSD_FOLDER = "pynfe/data/XSDs/"

2211
pynfe/utils/_dsig.py
File diff suppressed because it is too large
View File

7780
pynfe/utils/nfse_v202.py
File diff suppressed because it is too large
View File

8
tests/valida.py

@ -0,0 +1,8 @@
from lxml import etree
xmlschema_doc = etree.parse('nfse_v202.xsd')
xmlschema = etree.XMLSchema(xmlschema_doc)
xml = etree.parse('testef.xml')
print(xmlschema.validate(xml))
xmlschema.assertValid(xml)
Loading…
Cancel
Save