Browse Source

Anotando metodos de validação no teste geral basico

tags/0.1
Marinho Brandão 16 years ago
parent
commit
4a1598a331
  1. 3
      pynfe/entidades/__init__.py
  2. 128
      pynfe/entidades/fontes_dados.py
  3. 2
      pynfe/entidades/notafiscal.py
  4. 43
      pynfe/processamento/serializacao.py
  5. 41
      tests/01-basico.txt
  6. 2
      tests/02-modelo-00-definicoes-gerais.txt
  7. 19
      tests/02-modelo-06-certificado.txt
  8. 5
      tests/03-processamento-00-definicoes-gerais.txt
  9. 5
      tests/03-processamento-01-serializacao-xml.txt
  10. 2
      tests/saida/README

3
pynfe/entidades/__init__.py

@ -4,5 +4,6 @@ from cliente import Cliente
from transportadora import Transportadora
from notafiscal import NotaFiscal
from lotes import LoteNotaFiscal
from fontes_dados import FonteDados
from fonte_dados import _fonte_dados
from certificado import CertificadoA1

128
pynfe/entidades/fontes_dados.py

@ -1,128 +0,0 @@
# -*- 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 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 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()

2
pynfe/entidades/notafiscal.py

@ -291,7 +291,7 @@ class NotaFiscal(Entidade):
super(NotaFiscal, self).__init__(*args, **kwargs)
def __str__(self):
return ' '.join([self.modelo, self.serie, self.numero_nf])
return ' '.join([str(self.modelo), self.serie, self.numero_nf])
def adicionar_nota_fiscal_referenciada(self, **kwargs):
u"""Adiciona uma instancia de Nota Fisca referenciada"""

43
pynfe/processamento/serializacao.py

@ -64,32 +64,24 @@ class Serializacao(object):
raise Exception('Metodo nao implementado')
class SerializacaoXML(Serializacao):
def exportar(self, destino, **kwargs):
def exportar(self, destino=None, retorna_string=False, **kwargs):
"""Gera o(s) arquivo(s) de Nofa Fiscal eletronica no padrao oficial da SEFAZ
e Receita Federal, para ser(em) enviado(s) para o webservice ou para ser(em)
armazenado(s) em cache local."""
# No raiz do XML de saida
raiz = etree.Element('NFe', xmlns="http://www.portalfiscal.inf.br/nfe")
# Carrega lista de Notas Fiscais
notas_fiscais = self._fonte_dados.obter_lista(_classe=NotaFiscal, **kwargs)
saida = []
# Dados do emitente
saida.append(self._serializar_emitente(self._obter_emitente_de_notas_fiscais(notas_fiscais)))
# Certificado Digital? XXX
# Clientes
#saida.append(self._serializar_clientes(**kwargs))
# Transportadoras
#saida.append(self._serializar_transportadoras(**kwargs))
# Produtos
#saida.append(self._serializar_produtos(**kwargs))
for nf in notas_fiscais:
raiz.append(self._serializar_notas_fiscal(nf, retorna_string=False))
# Lote de Notas Fiscais
#saida.append(self._serializar_notas_fiscais(**kwargs))
if retorna_string:
return etree.tostring(raiz, pretty_print=True)
else:
return raiz
def importar(self, origem):
"""Cria as instancias do PyNFe a partir de arquivos XML no formato padrao da
@ -97,16 +89,6 @@ class SerializacaoXML(Serializacao):
raise Exception('Metodo nao implementado')
def _obter_emitente_de_notas_fiscais(self, notas_fiscais):
lista = list(set([nf.emitente for nf in notas_fiscais if nf.emitente]))
if len(lista) == 0:
raise NenhumObjetoEncontrado('Nenhum objeto foi encontrado!')
elif len(lista) > 1:
raise MuitosObjetosEncontrados('Muitos objetos foram encontrados!')
return lista[0]
def _serializar_emitente(self, emitente, tag_raiz='emit', retorna_string=True):
raiz = etree.Element(tag_raiz)
@ -335,7 +317,10 @@ class SerializacaoXML(Serializacao):
etree.SubElement(transp, 'modFrete').text = str(nota_fiscal.transporte_modalidade_frete)
# Transportadora
transp.append(self._serializar_transportadora(nota_fiscal.transporte_transportadora, retorna_string=False))
transp.append(self._serializar_transportadora(
nota_fiscal.transporte_transportadora,
retorna_string=False,
))
# Veículo
veiculo = etree.SubElement(transp, 'veicTransp')

41
tests/01-basico.txt

@ -41,24 +41,26 @@ modelo:
---------------------------------------------------------------------
--------------------------------------------------------------------------
| PROCESSAMENTO |
--------------------------------------------------------------------------
| |
| ------------------- -------------- -------------------------------- |
| | SerializacaoXML | | Assinatura | | Comunicacao | |
| ------------------- -------------- -------------------------------- |
| | exportar() | | assinar() | | transmitir() | |
| | importar() | -------------- | cancelar() | |
| ------------------- | situacao_nfe() | |
| ------------- | status_servico() | |
| -------------- | Validacao | | consultar_cadastro() | |
| | DANFE | ------------- | inutilizar_faixa_numeracao() | |
| -------------- | validar() | -------------------------------- |
| | imprimir() | ------------- |
| -------------- |
| |
--------------------------------------------------------------------------
----------------------------------------------------------------------------
| PROCESSAMENTO |
----------------------------------------------------------------------------
| |
| ------------------- -------------- -------------------------------- |
| | SerializacaoXML | | Assinatura | | Comunicacao | |
| ------------------- -------------- -------------------------------- |
| | exportar() | | assinar() | | transmitir() | |
| | importar() | -------------- | cancelar() | |
| ------------------- | situacao_nfe() | |
| ---------------------- | status_servico() | |
| -------------- | Validacao | | consultar_cadastro() | |
| | DANFE | ---------------------- | inutilizar_faixa_numeracao() | |
| -------------- | validar_arquivos() | -------------------------------- |
| | imprimir() | | validar_xml() | |
| -------------- | validar_etree() | |
| | validar_objetos() | |
| ---------------------- |
| |
----------------------------------------------------------------------------
Os pacotes da biblioteca sao:
@ -75,7 +77,8 @@ Os pacotes da biblioteca sao:
>>> set([attr for attr in dir(entidades) if not attr.startswith('__')]) == set([
... 'Cliente', 'Emitente', 'LoteNotaFiscal', 'NotaFiscal', 'Produto',
... 'Transportadora', 'base', 'cliente', 'emitente', 'lotes', 'notafiscal',
... 'produto', 'transportadora', 'fontes_dados', 'FonteDados'])
... 'produto', 'transportadora', 'fonte_dados', '_fonte_dados','certificado',
... 'CertificadoA1'])
True
- processamento (contem todas as funcionalidades de processamento da

2
tests/02-modelo-00-definicoes-gerais.txt

@ -9,7 +9,7 @@ Todas as entidades devem referenciar uma Fonte de Dados, de forma a evitar
redundancia de dados (com o objetivo de melhorar o desempenho e possibilitar
o uso de cache de persistencia de dados serializados).
>>> from pynfe.entidades import FonteDados
>>> from pynfe.entidades.fonte_dados import FonteDados
>>> fonte_dados = FonteDados()
Nao eh da funcao do PyNFe efetuar a persistencia dos objetos, mas a classe

19
tests/02-modelo-06-certificado.txt

@ -1,9 +1,24 @@
MODELO - CERTIFICADO DIGITAL
============================
Modelo das entidades e como elas se relacionam.
>>> from pynfe.entidades.certificado import Certificado, CertificadoA1
Nenhum dos campos deve permitir acentos e/ou cedilhas.
A classe 'Certificado' eh abstrata e eh responsavel por definir o modelo
padrao para instancias de certificados.
>>> try:
... Certificado()
... except Exception, e:
... print e.message
Esta classe nao pode ser instanciada diretamente!
Num primeiro momento teremos apenas a classe 'CertificadoA1', responsavel
pelo certificado digital do tipo A1, mas num segundo momento teremos
outros modelos de certificados (como o A3, por exemplo) que vao herdar da
classe 'Certificado'.
>>> issubclass(CertificadoA1, Certificado)
True
- Caminho do arquivo
- Windows

5
tests/03-processamento-00-definicoes-gerais.txt

@ -7,11 +7,6 @@ Validar NF-e
- Efetuar validacoes dos XSD no(s) XML(s) gerado(s)
- Validar nota_fiscal.dv_codigo_numerico_aleatorio (ver pagina 85 do manual de integracao)
Assinar NF-e
------------
- Na hora de assinar, selecionar um Certificado Digital
Transmitir NF-e (ou lote de NF-e`s)
-----------------------------------

5
tests/03-processamento-01-serializacao-xml.txt

@ -492,6 +492,11 @@ Serializando por partes
</infNFe>
<BLANKLINE>
Exportacao completa
-------------------
>>> xml = serializador.exportar(modelo=55)
- Quando gerados me lote, apenas o primeiro arquivo deve ter o cabecalho
padrao do XML 1.0
- <?xml version="1.0" encoding="UTF-8"?>

2
tests/saida/README

@ -0,0 +1,2 @@
Esta pasta provavelmente vai permanecer vazia, e foi criada apenas para armazenar alguns
arquivos de saida da exportacao do serializador.
Loading…
Cancel
Save