Browse Source

Trabalhando no serializador para XML

tags/0.1
Marinho Brandão 16 years ago
parent
commit
dc54d48255
  1. 1
      pynfe/data/MunIBGE/README
  2. 7
      pynfe/entidades/base.py
  3. 4
      pynfe/entidades/cliente.py
  4. 3
      pynfe/entidades/emitente.py
  5. 2
      pynfe/entidades/fontes_dados.py
  6. 6
      pynfe/entidades/notafiscal.py
  7. 105
      pynfe/processamento/serializacao.py
  8. 65
      pynfe/utils/__init__.py
  9. 1
      pynfe/utils/flags.py
  10. 2
      tests/01-basico.txt
  11. 128
      tests/03-processamento-01-serializacao-xml.txt

1
pynfe/data/MunIBGE/README

@ -0,0 +1 @@
Origem e Creditos: set de componentes ACBr (http://acbr.sourceforge.net/drupal/?q=node/36)

7
pynfe/entidades/base.py

@ -10,8 +10,11 @@ class Entidade(object):
setattr(self, k, v) setattr(self, k, v)
# Adiciona o objeto à fonte de dados informada # Adiciona o objeto à fonte de dados informada
if self._fonte_dados:
self._fonte_dados.adicionar_objeto(self)
if not self._fonte_dados:
from fontes_dados import _fonte_dados
self._fonte_dados = _fonte_dados
self._fonte_dados.adicionar_objeto(self)
def __repr__(self): def __repr__(self):
return '<%s %s>'%(self.__class__.__name__, str(self)) return '<%s %s>'%(self.__class__.__name__, str(self))

4
pynfe/entidades/cliente.py

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from base import Entidade from base import Entidade
from pynfe.utils.flags import TIPOS_DOCUMENTO
from pynfe.utils.flags import TIPOS_DOCUMENTO, CODIGO_BRASIL
class Cliente(Entidade): class Cliente(Entidade):
# Dados do Cliente # Dados do Cliente
@ -39,7 +39,7 @@ class Cliente(Entidade):
endereco_cep = str() endereco_cep = str()
# - Pais (seleciona de lista) # - Pais (seleciona de lista)
endereco_pais = 'BRASIL'
endereco_pais = CODIGO_BRASIL
# - UF (obrigatorio) # - UF (obrigatorio)
endereco_uf = str() endereco_uf = str()

3
pynfe/entidades/emitente.py

@ -1,4 +1,5 @@
from base import Entidade from base import Entidade
from pynfe.utils.flags import CODIGO_BRASIL
class Emitente(Entidade): class Emitente(Entidade):
# Dados do Emitente # Dados do Emitente
@ -40,7 +41,7 @@ class Emitente(Entidade):
endereco_cep = str() endereco_cep = str()
# - Pais (aceita somente Brasil) # - Pais (aceita somente Brasil)
endereco_pais = 'BRASIL'
endereco_pais = CODIGO_BRASIL
# - UF (obrigatorio) # - UF (obrigatorio)
endereco_uf = str() endereco_uf = str()

2
pynfe/entidades/fontes_dados.py

@ -123,4 +123,6 @@ class FonteDados(object):
else: else:
return len(self._objetos) return len(self._objetos)
# Instancia da fonte de dados default
_fonte_dados = FonteDados()

6
pynfe/entidades/notafiscal.py

@ -4,7 +4,7 @@ from pynfe.utils.flags import NF_STATUS, NF_TIPOS_DOCUMENTO, NF_TIPOS_IMPRESSAO_
NF_REFERENCIADA_TIPOS, NF_PRODUTOS_ESPECIFICOS, ICMS_TIPOS_TRIBUTACAO,\ NF_REFERENCIADA_TIPOS, NF_PRODUTOS_ESPECIFICOS, ICMS_TIPOS_TRIBUTACAO,\
ICMS_ORIGENS, ICMS_MODALIDADES, IPI_TIPOS_TRIBUTACAO, IPI_TIPOS_CALCULO,\ ICMS_ORIGENS, ICMS_MODALIDADES, IPI_TIPOS_TRIBUTACAO, IPI_TIPOS_CALCULO,\
PIS_TIPOS_TRIBUTACAO, PIS_TIPOS_CALCULO, COFINS_TIPOS_TRIBUTACAO,\ PIS_TIPOS_TRIBUTACAO, PIS_TIPOS_CALCULO, COFINS_TIPOS_TRIBUTACAO,\
COFINS_TIPOS_CALCULO, MODALIDADES_FRETE, ORIGENS_PROCESSO
COFINS_TIPOS_CALCULO, MODALIDADES_FRETE, ORIGENS_PROCESSO, CODIGO_BRASIL
from decimal import Decimal from decimal import Decimal
@ -13,7 +13,7 @@ class NotaFiscal(Entidade):
# Nota Fisca eletronica # Nota Fisca eletronica
# - Modelo (formato: NN) # - Modelo (formato: NN)
modelo = str()
modelo = int()
# - Serie (obrigatorio - formato: NNN) # - Serie (obrigatorio - formato: NNN)
serie = str() serie = str()
@ -96,7 +96,7 @@ class NotaFiscal(Entidade):
destinatario_endereco_cep = str() destinatario_endereco_cep = str()
# - Pais (seleciona de lista) # - Pais (seleciona de lista)
destinatario_endereco_pais = 'BRASIL'
destinatario_endereco_pais = CODIGO_BRASIL
# - UF (obrigatorio) # - UF (obrigatorio)
destinatario_endereco_uf = str() destinatario_endereco_uf = str()

105
pynfe/processamento/serializacao.py

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
try: try:
set set
except: except:
@ -26,6 +27,7 @@ except ImportError:
from pynfe.entidades import Emitente, Cliente, Produto, Transportadora, NotaFiscal from pynfe.entidades import Emitente, Cliente, Produto, Transportadora, NotaFiscal
from pynfe.excecoes import NenhumObjetoEncontrado, MuitosObjetosEncontrados from pynfe.excecoes import NenhumObjetoEncontrado, MuitosObjetosEncontrados
from pynfe.utils import so_numeros, obter_municipio_por_codigo, obter_pais_por_codigo
class Serializacao(object): class Serializacao(object):
"""Classe abstrata responsavel por fornecer as funcionalidades basicas para """Classe abstrata responsavel por fornecer as funcionalidades basicas para
@ -40,12 +42,12 @@ class Serializacao(object):
if cls == Serializacao: if cls == Serializacao:
raise Exception('Esta classe nao pode ser instanciada diretamente!') raise Exception('Esta classe nao pode ser instanciada diretamente!')
else: else:
return cls(*args, **kwargs)
return super(Serializacao, cls).__new__(cls, *args, **kwargs)
def __init__(self, fonte_dados): def __init__(self, fonte_dados):
self._fonte_dados = fonte_dados self._fonte_dados = fonte_dados
def exportar(self, **kwargs):
def exportar(self, destino, **kwargs):
"""Gera o(s) arquivo(s) de exportacao a partir da Nofa Fiscal eletronica """Gera o(s) arquivo(s) de exportacao a partir da Nofa Fiscal eletronica
ou lista delas.""" ou lista delas."""
@ -58,7 +60,7 @@ class Serializacao(object):
raise Exception('Metodo nao implementado') raise Exception('Metodo nao implementado')
class SerializacaoXML(Serializacao): class SerializacaoXML(Serializacao):
def exportar(self, **kwargs):
def exportar(self, destino, **kwargs):
"""Gera o(s) arquivo(s) de Nofa Fiscal eletronica no padrao oficial da SEFAZ """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) e Receita Federal, para ser(em) enviado(s) para o webservice ou para ser(em)
armazenado(s) em cache local.""" armazenado(s) em cache local."""
@ -74,19 +76,16 @@ class SerializacaoXML(Serializacao):
# Certificado Digital? XXX # Certificado Digital? XXX
# Clientes # Clientes
saida.append(self._serializar_clientes(**kwargs))
#saida.append(self._serializar_clientes(**kwargs))
# Transportadoras # Transportadoras
saida.append(self._serializar_transportadoras(**kwargs))
#saida.append(self._serializar_transportadoras(**kwargs))
# Produtos # Produtos
saida.append(self._serializar_produtos(**kwargs))
#saida.append(self._serializar_produtos(**kwargs))
# Lote de Notas Fiscais # Lote de Notas Fiscais
saida.append(self._serializar_notas_fiscais(**kwargs))
# FIXME
return '\n'.join(saida)
#saida.append(self._serializar_notas_fiscais(**kwargs))
def importar(self, origem): def importar(self, origem):
"""Cria as instancias do PyNFe a partir de arquivos XML no formato padrao da """Cria as instancias do PyNFe a partir de arquivos XML no formato padrao da
@ -95,7 +94,7 @@ class SerializacaoXML(Serializacao):
raise Exception('Metodo nao implementado') raise Exception('Metodo nao implementado')
def _obter_emitente_de_notas_fiscais(self, notas_fiscais): def _obter_emitente_de_notas_fiscais(self, notas_fiscais):
lista = set([nf.emitente for nf in notas_fiscais if nf.emitente])
lista = list(set([nf.emitente for nf in notas_fiscais if nf.emitente]))
if len(lista) == 0: if len(lista) == 0:
raise NenhumObjetoEncontrado('Nenhum objeto foi encontrado!') raise NenhumObjetoEncontrado('Nenhum objeto foi encontrado!')
@ -104,18 +103,82 @@ class SerializacaoXML(Serializacao):
return lista[0] return lista[0]
def _serializar_emitente(self, emitente):
return ''
def _serializar_clientes(self, objetos):
return ''
def _serializar_emitente(self, emitente, tag_raiz='emit'):
raiz = etree.Element(tag_raiz)
def _serializar_transportadoras(self, objetos):
return ''
def _serializar_produtos(self, objetos):
# Dados do emitente
etree.SubElement(raiz, 'CNPJ').text = so_numeros(emitente.cnpj)
etree.SubElement(raiz, 'xNome').text = emitente.razao_social
etree.SubElement(raiz, 'xFant').text = emitente.nome_fantasia
etree.SubElement(raiz, 'IE').text = emitente.inscricao_estadual
# Endereço
endereco = etree.SubElement(raiz, 'enderEmit')
etree.SubElement(endereco, 'xLgr').text = emitente.endereco_logradouro
etree.SubElement(endereco, 'nro').text = emitente.endereco_numero
etree.SubElement(endereco, 'xCpl').text = emitente.endereco_complemento
etree.SubElement(endereco, 'xBairro').text = emitente.endereco_bairro
etree.SubElement(endereco, 'cMun').text = emitente.endereco_municipio
etree.SubElement(endereco, 'xMun').text = obter_municipio_por_codigo(
emitente.endereco_municipio, emitente.endereco_uf,
)
etree.SubElement(endereco, 'UF').text = emitente.endereco_uf
etree.SubElement(endereco, 'CEP').text = so_numeros(emitente.endereco_cep)
etree.SubElement(endereco, 'cPais').text = emitente.endereco_pais
etree.SubElement(endereco, 'xPais').text = obter_pais_por_codigo(emitente.endereco_pais)
etree.SubElement(endereco, 'fone').text = emitente.endereco_telefone
return etree.tostring(raiz, pretty_print=True)
def _serializar_cliente(self, cliente, tag_raiz='dest'):
raiz = etree.Element(tag_raiz)
# Dados do cliente
etree.SubElement(raiz, cliente.tipo_documento).text = so_numeros(cliente.numero_documento)
etree.SubElement(raiz, 'xNome').text = cliente.razao_social
etree.SubElement(raiz, 'IE').text = cliente.inscricao_estadual
# Endereço
endereco = etree.SubElement(raiz, 'enderDest')
etree.SubElement(endereco, 'xLgr').text = cliente.endereco_logradouro
etree.SubElement(endereco, 'nro').text = cliente.endereco_numero
etree.SubElement(endereco, 'xCpl').text = cliente.endereco_complemento
etree.SubElement(endereco, 'xBairro').text = cliente.endereco_bairro
etree.SubElement(endereco, 'cMun').text = cliente.endereco_municipio
etree.SubElement(endereco, 'xMun').text = obter_municipio_por_codigo(
cliente.endereco_municipio, cliente.endereco_uf,
)
etree.SubElement(endereco, 'UF').text = cliente.endereco_uf
etree.SubElement(endereco, 'CEP').text = so_numeros(cliente.endereco_cep)
etree.SubElement(endereco, 'cPais').text = cliente.endereco_pais
etree.SubElement(endereco, 'xPais').text = obter_pais_por_codigo(cliente.endereco_pais)
etree.SubElement(endereco, 'fone').text = cliente.endereco_telefone
return etree.tostring(raiz, pretty_print=True)
def _serializar_transportadora(self, transportadora, tag_raiz='transporta'):
raiz = etree.Element(tag_raiz)
# Dados da transportadora
etree.SubElement(raiz, transportadora.tipo_documento).text = so_numeros(transportadora.numero_documento)
etree.SubElement(raiz, 'xNome').text = transportadora.razao_social
etree.SubElement(raiz, 'IE').text = transportadora.inscricao_estadual
# Endereço
etree.SubElement(raiz, 'xEnder').text = transportadora.endereco_logradouro
etree.SubElement(raiz, 'cMun').text = transportadora.endereco_municipio
etree.SubElement(raiz, 'xMun').text = obter_municipio_por_codigo(
transportadora.endereco_municipio, transportadora.endereco_uf,
)
etree.SubElement(raiz, 'UF').text = transportadora.endereco_uf
return etree.tostring(raiz, pretty_print=True)
def _serializar_produto(self, produto, tag_raiz='prod'):
# TODO
return '' return ''
def _serializar_notas_fiscais(self, objetos):
def _serializar_notas_fiscal(self, notas_fiscal, tag_raiz='infNFe'):
# TODO
return '' return ''

65
pynfe/utils/__init__.py

@ -1,2 +1,67 @@
import os
import flags import flags
from geraldo.utils import memoize
@memoize
def so_numeros(texto):
"""Retorna o texto informado mas somente os numeros"""
return ''.join(filter(lambda c: ord(c) in range(48,58), texto))
@memoize
def obter_pais_por_codigo(codigo):
# TODO
if codigo == '1058':
return 'Brasil'
ARQUIVOS_ESTADOS = {
'RO': 'MunIBGE-UF11.txt',
'AC': 'MunIBGE-UF12.txt',
'AM': 'MunIBGE-UF13.txt',
'RR': 'MunIBGE-UF14.txt',
'PA': 'MunIBGE-UF15.txt',
'AP': 'MunIBGE-UF16.txt',
'TO': 'MunIBGE-UF17.txt',
'MA': 'MunIBGE-UF21.txt',
'PI': 'MunIBGE-UF22.txt',
'CE': 'MunIBGE-UF23.txt',
'RN': 'MunIBGE-UF24.txt',
'PB': 'MunIBGE-UF25.txt',
'PE': 'MunIBGE-UF26.txt',
'AL': 'MunIBGE-UF27.txt',
'SE': 'MunIBGE-UF28.txt',
'BA': 'MunIBGE-UF29.txt',
'MG': 'MunIBGE-UF31.txt',
'ES': 'MunIBGE-UF32.txt',
'RJ': 'MunIBGE-UF33.txt',
'SP': 'MunIBGE-UF35.txt',
'PR': 'MunIBGE-UF41.txt',
'SC': 'MunIBGE-UF42.txt',
'RS': 'MunIBGE-UF43.txt',
'MS': 'MunIBGE-UF50.txt',
'MT': 'MunIBGE-UF51.txt',
'GO': 'MunIBGE-UF52.txt',
'DF': 'MunIBGE-UF53.txt',
}
CAMINHO_DATA = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'data')
CAMINHO_MUNICIPIOS = os.path.join(CAMINHO_DATA, 'MunIBGE')
@memoize
def carregar_arquivo_municipios(uf):
caminho_arquivo = os.path.join(CAMINHO_MUNICIPIOS, ARQUIVOS_ESTADOS[uf.upper()])
fp = file(caminho_arquivo)
linhas = list(fp.readlines())
fp.close()
return dict([(linha[:7], linha[7:].strip()) for linha in linhas])
@memoize
def obter_municipio_por_codigo(codigo, uf):
# TODO: fazer UF ser opcional
municipios = carregar_arquivo_municipios(uf)
return municipios[codigo]

1
pynfe/utils/flags.py

@ -147,5 +147,6 @@ ORIGENS_PROCESSO = (
'Outros', 'Outros',
) )
CODIGO_BRASIL = '1058'

2
tests/01-basico.txt

@ -66,7 +66,7 @@ Os pacotes da biblioteca sao:
biblioteca, incluindo flags e funcoes genericas) biblioteca, incluindo flags e funcoes genericas)
>>> from pynfe import utils >>> from pynfe import utils
>>> set([attr for attr in dir(utils) if not attr.startswith('__')]) == set(['flags'])
>>> set([attr for attr in dir(utils) if not attr.startswith('__')]) >= set(['flags'])
True True
- entidades (contem todas as entidades da biblioteca) - entidades (contem todas as entidades da biblioteca)

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

@ -4,17 +4,139 @@ PROCESSAMENTO - SERIALIZACAO PARA XML
Populando fonte de dados Populando fonte de dados
------------------------ ------------------------
>>> from pynfe.entidades import FonteDados
>>> fonte_dados = FonteDados()
>>> import datetime
>>> from pynfe.entidades import Emitente, Cliente, NotaFiscal, Produto,\
... Transportadora
>>> from pynfe.entidades.fontes_dados import _fonte_dados
>>> from pynfe.utils.flags import CODIGO_BRASIL
Inicia uma NF e demais dependentes
Popula dependentes da NF
>>> emitente = Emitente(
... cnpj='12.345.678/0001-90',
... razao_social='Tarsila Calcados Ltda.',
... nome_fantasia='Tarsila Calcados Ltda.',
... inscricao_estadual='123456789012',
... endereco_logradouro='Rua 10',
... endereco_numero='15',
... endereco_complemento='qd 17, lt 10',
... endereco_bairro='Setor Oeste',
... endereco_municipio='5208806', # Goiania
... endereco_uf='GO',
... endereco_cep='75370-000',
... endereco_telefone='6242421212',
... )
>>> cliente = Cliente(
... razao_social='Jose Felipe da Silva',
... tipo_documento='CPF',
... numero_documento='123.456.789-01',
... inscricao_estadual='9876543210',
... endereco_logradouro='AV DAS ROSAS',
... endereco_numero='1777',
... endereco_complemento='10 ANDAR',
... endereco_bairro='PARQUE FONTES',
... endereco_municipio='3304557', # Rio de Janeiro
... endereco_uf='RJ',
... endereco_pais=CODIGO_BRASIL,
... endereco_cep='23950-000',
... endereco_telefone='2132011234',
... )
>>> produto1 = Produto(codigo=1, descricao='Tenis Adidas Cinza')
>>> produto2 = Produto(codigo=2, descricao='Sapato Ferracini Preto')
>>> transportadora = Transportadora(
... razao_social='WS Cargas S/A',
... tipo_documento='CNPJ',
... numero_documento='123.123.123/0001-12',
... inscricao_estadual='171999999119',
... endereco_logradouro='Rua Central 100 - Fundos - Distrito Industrial',
... endereco_municipio='3304557', # Rio de Janeiro
... endereco_uf='RJ',
... )
Instancia a NF
>>> nota_fiscal = NotaFiscal(
... emitente=emitente,
... transporte_transportadora=transportadora,
... modelo=55,
... serie='1',
... numero_nf='1',
... data_emissao=datetime.date.today(),
... natureza_operacao='Venda no Varejo',
... )
>>> _fonte_dados.contar_objetos()
6
Gerar arquivos XML Gerar arquivos XML
------------------ ------------------
>>> import os
>>> CUR_DIR = '.'
>>> CAMINHO_SAIDA = os.path.join(CUR_DIR, 'tests', 'saida')
>>> from pynfe.processamento.serializacao import SerializacaoXML >>> from pynfe.processamento.serializacao import SerializacaoXML
>>> serializador = SerializacaoXML(_fonte_dados)
Serializando por partes
>>> print serializador._serializar_emitente(emitente)
<emit>
<CNPJ>12345678000190</CNPJ>
<xNome>Tarsila Calcados Ltda.</xNome>
<xFant>Tarsila Calcados Ltda.</xFant>
<IE>123456789012</IE>
<enderEmit>
<xLgr>Rua 10</xLgr>
<nro>15</nro>
<xCpl>qd 17, lt 10</xCpl>
<xBairro>Setor Oeste</xBairro>
<cMun>5208806</cMun>
<xMun>Goianira</xMun>
<UF>GO</UF>
<CEP>75370000</CEP>
<cPais>1058</cPais>
<xPais>Brasil</xPais>
<fone>6242421212</fone>
</enderEmit>
</emit>
<BLANKLINE>
>>> print serializador._serializar_cliente(cliente)
<dest>
<CPF>12345678901</CPF>
<xNome>Jose Felipe da Silva</xNome>
<IE>9876543210</IE>
<enderDest>
<xLgr>AV DAS ROSAS</xLgr>
<nro>1777</nro>
<xCpl>10 ANDAR</xCpl>
<xBairro>PARQUE FONTES</xBairro>
<cMun>3304557</cMun>
<xMun>Rio de Janeiro</xMun>
<UF>RJ</UF>
<CEP>23950000</CEP>
<cPais>1058</cPais>
<xPais>Brasil</xPais>
<fone>2132011234</fone>
</enderDest>
</dest>
<BLANKLINE>
>>> print serializador._serializar_transportadora(transportadora)
<transporta>
<CNPJ>123123123000112</CNPJ>
<xNome>WS Cargas S/A</xNome>
<IE>171999999119</IE>
<xEnder>Rua Central 100 - Fundos - Distrito Industrial</xEnder>
<cMun>3304557</cMun>
<xMun>Rio de Janeiro</xMun>
<UF>RJ</UF>
</transporta>
<BLANKLINE>
- Gera os arquivos XML a partir dos dados das instancias da NF-e - Gera os arquivos XML a partir dos dados das instancias da NF-e
- Quando gerados me lote, apenas o primeiro arquivo deve ter o cabecalho - Quando gerados me lote, apenas o primeiro arquivo deve ter o cabecalho

Loading…
Cancel
Save