Browse Source
Enviando arquivos esquecidos e iniciando trabalho na assinatura do XML.
Enviando arquivos esquecidos e iniciando trabalho na assinatura do XML.
Tambem iniciado o arquivo de autores, devido ao inicio da colaboracao da TaugaRStags/0.1
6 changed files with 259 additions and 16 deletions
-
25AUTHORS
-
27pynfe/entidades/certificado.py
-
128pynfe/entidades/fonte_dados.py
-
33pynfe/processamento/assinatura.py
-
31tests/01-basico.txt
-
25tests/03-processamento-03-assinatura.txt
@ -0,0 +1,25 @@ |
|||||
|
Sobre os autores deste e outros créditos |
||||
|
======================================== |
||||
|
|
||||
|
O PyNFe foi criado por Marinho Brandão <marinho at gmail.com> e seu |
||||
|
desenvolvimento só foi possível graças à massiva colaboração de pessoas, |
||||
|
empresas e outros projetos livres para o mesmo fim, disponívels para outras |
||||
|
linguagens. |
||||
|
|
||||
|
A TaugaRS Sistemas <http://taugars.com.br/> gentil e voluntariamente forneceu o |
||||
|
código-fonte de seu pacote já testado e maduro para servir de base de apoio |
||||
|
neste processo, assim como seu know-how de mais de 2 anos em NF-eletronicas. |
||||
|
Por esse motivo, encontra-se em igual ou maior importância que a do criador, o |
||||
|
desenvolvedor Ari Caldeira e o restante da diretoria desta empresa. |
||||
|
|
||||
|
Outra fonte importante de informação foi do projeto de componentes para Delphi |
||||
|
voltados para automação comercial ACBr <http://acbr.sourceforge.net/>, que |
||||
|
inclui um componente que tem a mesma finalidade que o PyNFe. |
||||
|
|
||||
|
Outras pessoas que devem ser referenciadas por sua colaboração, seja através de |
||||
|
know-how e debates, seja através de código enviado para colaborar: |
||||
|
|
||||
|
- Diogo Daniel (Prosig Sistemas) |
||||
|
- Antonio Prado (Antonio Prado Sistemas) |
||||
|
- Italo Maia <italomaia at gmail.com> |
||||
|
|
||||
@ -0,0 +1,27 @@ |
|||||
|
# -*- coding; utf-8 -*- |
||||
|
from base import Entidade |
||||
|
|
||||
|
class Certificado(Entidade): |
||||
|
u"""Classe abstrata responsavel por definir o modelo padrao para as demais |
||||
|
classes de certificados digitais. |
||||
|
|
||||
|
Caso va implementar um novo formato de certificado, crie uma classe que |
||||
|
herde desta.""" |
||||
|
|
||||
|
def __new__(cls, *args, **kwargs): |
||||
|
if cls == Certificado: |
||||
|
raise Exception('Esta classe nao pode ser instanciada diretamente!') |
||||
|
else: |
||||
|
return super(Certificado, cls).__new__(cls, *args, **kwargs) |
||||
|
|
||||
|
class CertificadoA1(Certificado): |
||||
|
"""Implementa a entidade do certificado eCNPJ A1, suportado pelo OpenSSL, |
||||
|
e amplamente utilizado.""" |
||||
|
|
||||
|
caminho_arquivo = None |
||||
|
conteudo_x509 = None |
||||
|
|
||||
|
def __init__(self, caminho_arquivo=None, conteudo_x509=None): |
||||
|
self.caminho_arquivo = caminho_arquivo or self.caminho_arquivo |
||||
|
self.conteudo_x509 = conteudo_x509 or self.conteudo_x509 |
||||
|
|
||||
@ -0,0 +1,128 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
from pynfe.excecoes import NenhumObjetoEncontrado, MuitosObjetosEncontrados |
||||
|
|
||||
|
class FonteDados(object): |
||||
|
u"""Classe responsável por ser o repositório dos objetos em memória e que |
||||
|
pode ser extendida para persistir esses objetos. Também tem a função de |
||||
|
memorizar os objetos redundantes como um só e assim otimizar o desempenho.""" |
||||
|
|
||||
|
_objetos = None |
||||
|
|
||||
|
def __init__(self, objetos=None): |
||||
|
# Inicializa variável que armazena os objetos contidos na Fonte de Dados |
||||
|
if objetos: |
||||
|
self._objetos = objetos |
||||
|
else: |
||||
|
self._objetos = [] |
||||
|
|
||||
|
def carregar_objetos(self, **kwargs): |
||||
|
u"""Método responsavel por retornar os objetos que casem com os atributos |
||||
|
informados no argumento **kwargs (argumentos nomeados). |
||||
|
|
||||
|
Um argumento especial é o '_classe', que representa a classe da entidade |
||||
|
desejada. |
||||
|
|
||||
|
FIXME: Este algoritimo pode ser melhorado pra fazer pesquisas melhores, |
||||
|
mas por enquanto vamos nos focar no processo em geral para só depois nos |
||||
|
preocupar com otimizações e desempenho.""" |
||||
|
|
||||
|
# Função de filtro |
||||
|
def filtrar(obj): |
||||
|
ret = True |
||||
|
|
||||
|
for k,v in kwargs.items(): |
||||
|
# Filtra pela classe e pelos atributos |
||||
|
ret = (k == '_classe' and isinstance(obj, v)) or\ |
||||
|
(k != '_classe' and getattr(obj, k, None) == v) |
||||
|
|
||||
|
if not ret: |
||||
|
break |
||||
|
|
||||
|
return ret |
||||
|
|
||||
|
# Filtra a lista de objetos |
||||
|
lista = filter(filtrar, self._objetos) |
||||
|
|
||||
|
return lista |
||||
|
|
||||
|
def adicionar_objeto(self, _objeto): |
||||
|
u"""Método responsável por adicionar o(s) objeto(s) informado(s) ao |
||||
|
repositorio de objetos da fonte de dados.""" |
||||
|
|
||||
|
from base import Entidade |
||||
|
|
||||
|
# Adiciona _objeto como objeto |
||||
|
if isinstance(_objeto, Entidade): |
||||
|
self._objetos.append(_objeto) |
||||
|
|
||||
|
# Adiciona _objeto como lista |
||||
|
elif isinstance(_objeto, (list, tuple)): |
||||
|
self._objetos += _objeto |
||||
|
|
||||
|
else: |
||||
|
raise Exception('Objeto informado e invalido!') |
||||
|
|
||||
|
def remover_objeto(self, _objeto=None, **kwargs): |
||||
|
u"""Método responsavel por remover os objetos que casem com os atributos |
||||
|
informados no argumento **kwargs (argumentos nomeados). |
||||
|
|
||||
|
Um argumento especial é o '_classe', que representa a classe da entidade |
||||
|
desejada. |
||||
|
|
||||
|
Outro argumetno especial é o '_objeto', que representa o objeto a ser |
||||
|
removido. Caso o argumento _objeto seja uma lista de objetos, eles serão |
||||
|
removidos também.""" |
||||
|
|
||||
|
from base import Entidade |
||||
|
|
||||
|
lista = None |
||||
|
|
||||
|
# Remove objetos |
||||
|
if not _objeto: |
||||
|
lista = self.carregar_objetos(**kwargs) |
||||
|
|
||||
|
# Remove _objeto como objeto |
||||
|
elif isinstance(_objeto, Entidade): |
||||
|
lista = [_objeto] |
||||
|
|
||||
|
# Remove _objeto como objeto |
||||
|
elif isinstance(_objeto, (list, tuple)): |
||||
|
lista = _objeto |
||||
|
|
||||
|
else: |
||||
|
raise Exception('Objeto informado e invalido!') |
||||
|
|
||||
|
# Efetiva a remoção |
||||
|
for obj in lista: |
||||
|
self._objetos.remove(obj) |
||||
|
|
||||
|
def obter_objeto(self, **kwargs): |
||||
|
u"""Faz a ponte para o método 'carregar_objetos' mas obriga o retorno de |
||||
|
apenas um objeto, levantando exceção se nenhum for encontrado ou se forem |
||||
|
encontrados mais de um.""" |
||||
|
|
||||
|
lista = self.carregar_objetos(**kwargs) |
||||
|
|
||||
|
if len(lista) == 0: |
||||
|
raise NenhumObjetoEncontrado('Nenhum objeto foi encontrado!') |
||||
|
elif len(lista) > 1: |
||||
|
raise MuitosObjetosEncontrados('Muitos objetos foram encontrados!') |
||||
|
|
||||
|
return lista[0] |
||||
|
|
||||
|
def obter_lista(self, **kwargs): |
||||
|
u"""Método de proxy, que somente repassa a chamada ao metodo 'carregar_objetos'""" |
||||
|
return self.carregar_objetos(**kwargs) |
||||
|
|
||||
|
def contar_objetos(self, **kwargs): |
||||
|
u"""Método que repassa a chamada ao metodo 'carregar_objetos' mas retorna |
||||
|
somente a quantidade de objetos encontrados.""" |
||||
|
|
||||
|
if kwargs: |
||||
|
return len(self.carregar_objetos(**kwargs)) |
||||
|
else: |
||||
|
return len(self._objetos) |
||||
|
|
||||
|
# Instancia da fonte de dados default |
||||
|
_fonte_dados = FonteDados() |
||||
|
|
||||
@ -1,3 +1,36 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
|
||||
class Assinatura(object): |
class Assinatura(object): |
||||
|
"""Classe abstrata responsavel por definir os metodos e logica das classes |
||||
|
de assinatura digital.""" |
||||
|
|
||||
|
def __init__(self, certificado): |
||||
|
self.certificado = certificado |
||||
|
|
||||
|
def assinar_arquivos(self, caminho_raiz): |
||||
|
"""Efetua a assinatura dos arquivos XML informados""" |
||||
pass |
pass |
||||
|
|
||||
|
def assinar_xml(self, xml): |
||||
|
"""Efetua a assinatura numa string contendo XML valido.""" |
||||
|
pass |
||||
|
|
||||
|
def assinar_etree(self, raiz): |
||||
|
u"""Efetua a assinatura numa instancia da biblioteca lxml.etree. |
||||
|
|
||||
|
Este metodo de assinatura será utilizado internamente pelos demais, |
||||
|
sendo que eles convertem para uma instancia lxml.etree para somente |
||||
|
depois efetivar a assinatura. |
||||
|
|
||||
|
TODO: Verificar o funcionamento da PyXMLSec antes de efetivar isso.""" |
||||
|
pass |
||||
|
|
||||
|
def assinar_objetos(self, objetos): |
||||
|
"""Efetua a assinatura em instancias do PyNFe""" |
||||
|
pass |
||||
|
|
||||
|
class AssinaturaA1(Assinatura): |
||||
|
"""Classe abstrata responsavel por efetuar a assinatura do certificado |
||||
|
digital no XML informado.""" |
||||
|
|
||||
|
pass |
||||
@ -0,0 +1,25 @@ |
|||||
|
PROCESSAMENTO - ASSINATURA DE XML |
||||
|
================================= |
||||
|
|
||||
|
Carregando Certificado Digital tipo A1 |
||||
|
-------------------------------------- |
||||
|
|
||||
|
>>> from pynfe.entidades import CertificadoA1 |
||||
|
|
||||
|
Assinando NF-e |
||||
|
-------------- |
||||
|
|
||||
|
>>> from pynfe.processamento import AssinaturaC1 |
||||
|
|
||||
|
Na hora de assinar, selecionar um Certificado Digital |
||||
|
|
||||
|
>>> |
||||
|
|
||||
|
A assinatura deve ser feita em quatro tipos diferentes de origem do XML: |
||||
|
|
||||
|
- Arquivos |
||||
|
- |
||||
|
|
||||
|
- Utilizar pyXMLSec para isso |
||||
|
- verificar qual eh a integracao do PyXMLSec com o lxml.etree |
||||
|
|
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue