diff --git a/pynfe/entidades/notafiscal.py b/pynfe/entidades/notafiscal.py
index c1f9e24..a64df18 100644
--- a/pynfe/entidades/notafiscal.py
+++ b/pynfe/entidades/notafiscal.py
@@ -9,7 +9,7 @@ from pynfe.utils.flags import NF_STATUS, NF_TIPOS_DOCUMENTO, NF_TIPOS_IMPRESSAO_
ICMS_ORIGENS, ICMS_MODALIDADES, IPI_TIPOS_TRIBUTACAO, IPI_TIPOS_CALCULO,\
PIS_TIPOS_TRIBUTACAO, PIS_TIPOS_CALCULO, COFINS_TIPOS_TRIBUTACAO,\
COFINS_TIPOS_CALCULO, MODALIDADES_FRETE, ORIGENS_PROCESSO, CODIGO_BRASIL,\
- NF_PROCESSOS_EMISSAO, CODIGOS_ESTADOS
+ NF_PROCESSOS_EMISSAO, CODIGOS_ESTADOS, TIPOS_DOCUMENTO
from pynfe.utils import so_numeros, memoize
from decimal import Decimal
@@ -97,33 +97,11 @@ class NotaFiscal(Entidade):
# - Identificacao (seleciona de Clientes)
destinatario_remetente = None
- # - Endereco (ver se pode copiar do Cliente)
- # - Logradouro (obrigatorio)
- destinatario_endereco_logradouro = str()
-
- # - Numero (obrigatorio)
- destinatario_endereco_numero = str()
-
- # - Complemento
- destinatario_endereco_complemento = str()
-
- # - Bairro (obrigatorio)
- destinatario_endereco_bairro = str()
-
- # - CEP
- destinatario_endereco_cep = str()
+ # - Entrega (XXX sera possivel ter entrega e retirada ao mesmo tempo na NF?)
+ entrega = None
- # - Pais (seleciona de lista)
- destinatario_endereco_pais = CODIGO_BRASIL
-
- # - UF (obrigatorio)
- destinatario_endereco_uf = str()
-
- # - Municipio (obrigatorio)
- destinatario_endereco_municipio = str()
-
- # - Telefone
- destinatario_endereco_telefone = str()
+ # - Retirada
+ retirada = None
# - Local Retirada/Entrega
# - Local de retirada diferente do emitente (Sim/Nao)
@@ -317,27 +295,39 @@ class NotaFiscal(Entidade):
def adicionar_nota_fiscal_referenciada(self, **kwargs):
u"""Adiciona uma instancia de Nota Fisca referenciada"""
- self.notas_fiscais_referenciadas.append(NotaFiscalReferenciada(**kwargs))
+ obj = NotaFiscalReferenciada(**kwargs)
+ self.notas_fiscais_referenciadas.append(obj)
+ return obj
def adicionar_produto_servico(self, **kwargs):
u"""Adiciona uma instancia de Produto"""
- self.produtos_e_servicos.append(NotaFiscalProduto(**kwargs))
+ obj = NotaFiscalProduto(**kwargs)
+ self.produtos_e_servicos.append(obj)
+ return obj
def adicionar_transporte_volume(self, **kwargs):
u"""Adiciona uma instancia de Volume de Transporte"""
- self.transporte_volumes.append(NotaFiscalTransporteVolume(**kwargs))
+ obj = NotaFiscalTransporteVolume(**kwargs)
+ self.transporte_volumes.append(obj)
+ return obj
def adicionar_duplicata(self, **kwargs):
u"""Adiciona uma instancia de Duplicata"""
- self.duplicatas.append(NotaFiscalCobrancaDuplicata(**kwargs))
+ obj = NotaFiscalCobrancaDuplicata(**kwargs)
+ self.duplicatas.append(obj)
+ return obj
def adicionar_observacao_contribuinte(self, **kwargs):
u"""Adiciona uma instancia de Observacao do Contribuinte"""
- self.observacoes_contribuinte.append(NotaFiscalObservacaoContribuinte(**kwargs))
+ obj = NotaFiscalObservacaoContribuinte(**kwargs)
+ self.observacoes_contribuinte.append(obj)
+ return obj
def adicionar_processo_referenciado(self, **kwargs):
u"""Adiciona uma instancia de Processo Referenciado"""
- self.processos_referenciados.append(NotaFiscalProcessoReferenciado(**kwargs))
+ obj = NotaFiscalProcessoReferenciado(**kwargs)
+ self.processos_referenciados.append(obj)
+ return obj
@property
@memoize
@@ -776,3 +766,38 @@ class NotaFiscalProcessoReferenciado(Entidade):
# - Outros
origem = str()
+class NotaFiscalEntregaRetirada(Entidade):
+ # - Tipo de Documento (obrigatorio) - default CNPJ
+ tipo_documento = 'CNPJ'
+
+ # - Numero do Documento (obrigatorio)
+ numero_documento = str()
+
+ # - Endereco
+ # - Logradouro (obrigatorio)
+ endereco_logradouro = str()
+
+ # - Numero (obrigatorio)
+ endereco_numero = str()
+
+ # - Complemento
+ endereco_complemento = str()
+
+ # - Bairro (obrigatorio)
+ endereco_bairro = str()
+
+ # - CEP
+ endereco_cep = str()
+
+ # - Pais (seleciona de lista)
+ endereco_pais = CODIGO_BRASIL
+
+ # - UF (obrigatorio)
+ endereco_uf = str()
+
+ # - Municipio (obrigatorio)
+ endereco_municipio = str()
+
+ # - Telefone
+ endereco_telefone = str()
+
diff --git a/pynfe/entidades/produto.py b/pynfe/entidades/produto.py
index 3f9a430..17a7bfe 100644
--- a/pynfe/entidades/produto.py
+++ b/pynfe/entidades/produto.py
@@ -5,6 +5,8 @@ from pynfe.utils.flags import ICMS_TIPOS_TRIBUTACAO, ICMS_ORIGENS, ICMS_MODALIDA
from decimal import Decimal
class Produto(Entidade):
+ """XXX: E provavel que esta entidade sera descartada."""
+
# Dados do Produto
# - Descricao (obrigatorio)
descricao = str()
diff --git a/pynfe/processamento/serializacao.py b/pynfe/processamento/serializacao.py
index aea0d42..930e468 100644
--- a/pynfe/processamento/serializacao.py
+++ b/pynfe/processamento/serializacao.py
@@ -187,10 +187,53 @@ class SerializacaoXML(Serializacao):
else:
return raiz
- def _serializar_produto(self, produto, tag_raiz='prod', retorna_string=True):
- # Provavelmente nao vai ser feito desta forma, e sim como serializacao do produto
- # na NF (NotaFiscalProduto)
- return ''
+ def _serializar_entrega_retirada(self, entrega_retirada, tag_raiz='entrega', retorna_string=True):
+ raiz = etree.Element(tag_raiz)
+
+ # Dados da entrega/retirada
+ etree.SubElement(raiz, entrega_retirada.tipo_documento).text = so_numeros(entrega_retirada.numero_documento)
+
+ # Endereço
+ etree.SubElement(raiz, 'xLgr').text = entrega_retirada.endereco_logradouro
+ etree.SubElement(raiz, 'nro').text = entrega_retirada.endereco_numero
+ etree.SubElement(raiz, 'xCpl').text = entrega_retirada.endereco_complemento
+ etree.SubElement(raiz, 'xBairro').text = entrega_retirada.endereco_bairro
+ etree.SubElement(raiz, 'cMun').text = entrega_retirada.endereco_municipio
+ etree.SubElement(raiz, 'xMun').text = obter_municipio_por_codigo(
+ entrega_retirada.endereco_municipio, entrega_retirada.endereco_uf,
+ )
+ etree.SubElement(raiz, 'UF').text = entrega_retirada.endereco_uf
+
+ if retorna_string:
+ return etree.tostring(raiz, pretty_print=True)
+ else:
+ return raiz
+
+ def _serializar_produto_servico(self, produto_servico, tag_raiz='det', retorna_string=True):
+ raiz = etree.Element(tag_raiz)
+
+ # Produto
+ prod = etree.SubElement(raiz, 'prod')
+ etree.SubElement(prod, 'cProd').text = str(produto_servico.codigo)
+ etree.SubElement(prod, 'cEAN').text = produto_servico.ean
+ etree.SubElement(prod, 'xProd').text = produto_servico.descricao
+ etree.SubElement(prod, 'CFOP').text = produto_servico.cfop
+ etree.SubElement(prod, 'uCom').text = produto_servico.unidade_comercial
+ etree.SubElement(prod, 'qCom').text = '%f'%(produto_servico.quantidade_comercial or 0)
+ etree.SubElement(prod, 'vUnCom').text = '%f'%(produto_servico.valor_unitario_comercial or 0)
+ etree.SubElement(prod, 'vProd').text = '%f'%(produto_servico.valor_total_bruto or 0)
+ etree.SubElement(prod, 'cEANTrib').text = produto_servico.ean_tributavel
+ etree.SubElement(prod, 'uTrib').text = produto_servico.unidade_tributavel
+ etree.SubElement(prod, 'qTrib').text = '%f'%(produto_servico.quantidade_tributavel)
+ etree.SubElement(prod, 'vUnTrib').text = '%f'%(produto_servico.valor_unitario_tributavel)
+
+ # Imposto
+ imposto = etree.SubElement(raiz, 'imposto')
+
+ if retorna_string:
+ return etree.tostring(raiz, pretty_print=True)
+ else:
+ return raiz
def _serializar_notas_fiscal(self, nota_fiscal, tag_raiz='infNFe', retorna_string=True):
raiz = etree.Element(tag_raiz, versao="2.00")
@@ -223,6 +266,29 @@ class SerializacaoXML(Serializacao):
# Destinatário
raiz.append(self._serializar_cliente(nota_fiscal.cliente, retorna_string=False))
+ # Retirada
+ if nota_fiscal.retirada:
+ raiz.append(self._serializar_entrega_retirada(
+ nota_fiscal.retirada,
+ retorna_string=False,
+ tag_raiz='retirada',
+ ))
+
+ # Entrega
+ if nota_fiscal.entrega:
+ raiz.append(self._serializar_entrega_retirada(
+ nota_fiscal.entrega,
+ retorna_string=False,
+ tag_raiz='entrega',
+ ))
+
+ # Itens
+ for num, item in enumerate(nota_fiscal.produtos_e_servicos):
+ det = self._serializar_produto_servico(item, retorna_string=False)
+ det.attrib['nItem'] = str(num+1)
+
+ raiz.append(det)
+
# Transporte
transp = etree.SubElement(raiz, 'transp')
transp.append(self._serializar_transportadora(nota_fiscal.transporte_transportadora, retorna_string=False))
diff --git a/tests/02-modelo-05-notafiscal.txt b/tests/02-modelo-05-notafiscal.txt
index ca4b67b..a590739 100644
--- a/tests/02-modelo-05-notafiscal.txt
+++ b/tests/02-modelo-05-notafiscal.txt
@@ -10,7 +10,8 @@ Nenhum dos campos deve permitir acentos e/ou cedilhas.
... NotaFiscalProduto, NotaFiscalDeclaracaoImportacao,\
... NotaFiscalDeclaracaoImportacaoAdicao, NotaFiscalTransporteVolume,\
... NotaFiscalTransporteVolumeLacre, NotaFiscalCobrancaDuplicata,\
- ... NotaFiscalObservacaoContribuinte, NotaFiscalProcessoReferenciado
+ ... NotaFiscalObservacaoContribuinte, NotaFiscalProcessoReferenciado,\
+ ... NotaFiscalEntregaRetirada
- Dados da NF-e
- Status (controle de estados da NF)
@@ -208,61 +209,83 @@ Nenhum dos campos deve permitir acentos e/ou cedilhas.
>>> hasattr(NotaFiscal, 'destinatario_remetente')
True
- - Endereco (ver se pode copiar do Cliente)
+- Entrega/Retirada
+ - Local de retirada diferente do emitente (Sim/Nao)
+
+ >>> hasattr(NotaFiscal, 'local_retirada_diferente_emitente')
+ True
+
+ - Local de entrega diferente do destinatario (Sim/Nao)
+
+ >>> hasattr(NotaFiscal, 'local_entrega_diferente_destinatario')
+ True
+
+ - Entrega
+
+ >>> hasattr(NotaFiscal, 'entrega')
+ True
+
+ - Retirada
+
+ >>> hasattr(NotaFiscal, 'retirada')
+ True
+
+ - Campos de Entrega e Retirada
+ - Tipo de Documento (obrigatorio) - default CNPJ
+ - CNPJ
+ - CPF
+
+ >>> hasattr(NotaFiscalEntregaRetirada, 'tipo_documento')
+ True
+
+ - Numero do Documento (obrigatorio)
+
+ >>> hasattr(NotaFiscalEntregaRetirada, 'numero_documento')
+ True
+
- Logradouro (obrigatorio)
- >>> hasattr(NotaFiscal, 'destinatario_endereco_logradouro')
+ >>> hasattr(NotaFiscalEntregaRetirada, 'endereco_logradouro')
True
- Numero (obrigatorio)
- >>> hasattr(NotaFiscal, 'destinatario_endereco_numero')
+ >>> hasattr(NotaFiscalEntregaRetirada, 'endereco_numero')
True
- Complemento
- >>> hasattr(NotaFiscal, 'destinatario_endereco_complemento')
+ >>> hasattr(NotaFiscalEntregaRetirada, 'endereco_complemento')
True
- Bairro (obrigatorio)
- >>> hasattr(NotaFiscal, 'destinatario_endereco_bairro')
+ >>> hasattr(NotaFiscalEntregaRetirada, 'endereco_bairro')
True
- CEP
- >>> hasattr(NotaFiscal, 'destinatario_endereco_cep')
+ >>> hasattr(NotaFiscalEntregaRetirada, 'endereco_cep')
True
- Pais (seleciona de lista)
- >>> hasattr(NotaFiscal, 'destinatario_endereco_pais')
+ >>> hasattr(NotaFiscalEntregaRetirada, 'endereco_pais')
True
- UF (obrigatorio)
- >>> hasattr(NotaFiscal, 'destinatario_endereco_uf')
+ >>> hasattr(NotaFiscalEntregaRetirada, 'endereco_uf')
True
- Municipio (obrigatorio)
- >>> hasattr(NotaFiscal, 'destinatario_endereco_municipio')
+ >>> hasattr(NotaFiscalEntregaRetirada, 'endereco_municipio')
True
- Telefone
- >>> hasattr(NotaFiscal, 'destinatario_endereco_telefone')
- True
-
- - Local Retirada/Entrega
- - Local de retirada diferente do emitente (Sim/Nao)
-
- >>> hasattr(NotaFiscal, 'local_retirada_diferente_emitente')
- True
-
- - Local de entrega diferente do destinatario (Sim/Nao)
-
- >>> hasattr(NotaFiscal, 'local_entrega_diferente_destinatario')
+ >>> hasattr(NotaFiscalEntregaRetirada, 'endereco_telefone')
True
- Produtos e Servicos (lista 1 para * / ManyToManyField)
diff --git a/tests/03-processamento-01-serializacao-xml.txt b/tests/03-processamento-01-serializacao-xml.txt
index 515e3af..398dd61 100644
--- a/tests/03-processamento-01-serializacao-xml.txt
+++ b/tests/03-processamento-01-serializacao-xml.txt
@@ -4,9 +4,9 @@ PROCESSAMENTO - SERIALIZACAO PARA XML
Populando fonte de dados
------------------------
- >>> import datetime
- >>> from pynfe.entidades import Emitente, Cliente, NotaFiscal, Produto,\
- ... Transportadora
+ >>> import datetime, decimal
+ >>> from pynfe.entidades import Emitente, Cliente, NotaFiscal, Transportadora
+ >>> from pynfe.entidades.notafiscal import NotaFiscalEntregaRetirada
>>> from pynfe.entidades.fontes_dados import _fonte_dados
>>> from pynfe.utils.flags import CODIGO_BRASIL
@@ -43,9 +43,6 @@ Popula dependentes da NF
... 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',
@@ -66,8 +63,8 @@ Instancia a NF
... modelo=55,
... serie='1',
... numero_nf='1',
- ... data_emissao=datetime.date.today(),
- ... data_saida_entrada=datetime.date.today(),
+ ... data_emissao=datetime.date(2010,1,13),
+ ... data_saida_entrada=datetime.date(2010,1,13),
... natureza_operacao='Venda a vista',
... forma_pagamento=0,
... tipo_impressao_danfe=1, # Retrato
@@ -77,9 +74,53 @@ Instancia a NF
... codigo_numerico_aleatorio='51800512',
... dv_codigo_numerico_aleatorio='3',
... )
+ >>> nota_fiscal.retirada = NotaFiscalEntregaRetirada(
+ ... tipo_documento='CNPJ',
+ ... numero_documento='99.171.171/0001-94',
+ ... endereco_logradouro='AV PAULISTA',
+ ... endereco_numero='12345',
+ ... endereco_complemento='TERREO',
+ ... endereco_bairro='CERQUEIRA CESAR',
+ ... endereco_municipio='3304557', # Rio de Janeiro
+ ... endereco_uf='RJ',
+ ... )
+ >>> nota_fiscal.entrega = NotaFiscalEntregaRetirada(
+ ... tipo_documento='CNPJ',
+ ... numero_documento='99.299.299/0001-94',
+ ... endereco_logradouro='AV FARIA LIMA',
+ ... endereco_numero='1500',
+ ... endereco_complemento='15 ANDAR',
+ ... endereco_bairro='PINHEIROS',
+ ... endereco_municipio='3304557', # Rio de Janeiro
+ ... endereco_uf='RJ',
+ ... )
+ >>> nf_prod1 = nota_fiscal.adicionar_produto_servico(
+ ... codigo='00001',
+ ... descricao='Agua Mineral',
+ ... cfop='5101',
+ ... unidade_comercial='dz',
+ ... quantidade_comercial=decimal.Decimal('2'),
+ ... valor_unitario_comercial=decimal.Decimal('10.0'),
+ ... valor_total_bruto=decimal.Decimal('20.0'),
+ ... unidade_tributavel='und',
+ ... quantidade_tributavel=decimal.Decimal('24.0'),
+ ... valor_unitario_tributavel=decimal.Decimal('3.00'),
+ ... )
+ >>> nf_prod2 = nota_fiscal.adicionar_produto_servico(
+ ... codigo='00002',
+ ... descricao='Agua Mineral',
+ ... cfop='5101',
+ ... unidade_comercial='pack',
+ ... quantidade_comercial=decimal.Decimal('5000000'),
+ ... valor_unitario_comercial=decimal.Decimal('2.0'),
+ ... valor_total_bruto=decimal.Decimal('10000000.0'),
+ ... unidade_tributavel='und',
+ ... quantidade_tributavel=decimal.Decimal('3000000.0'),
+ ... valor_unitario_tributavel=decimal.Decimal('0.3333'),
+ ... )
>>> _fonte_dados.contar_objetos()
- 6
+ 8
Gerar arquivos XML
------------------
@@ -218,9 +259,9 @@ Serializando por partes
12345
TERREO
CERQUEIRA CESAR
- 3550308
- SAO PAULO
- SP
+ 3304557
+ Rio de Janeiro
+ RJ
99299299000194
@@ -228,9 +269,9 @@ Serializando por partes
1500
15 ANDAR
PINHEIROS
- 3550308
- SAO PAULO
- SP
+ 3304557
+ Rio de Janeiro
+ RJ
@@ -239,13 +280,13 @@ Serializando por partes
Agua Mineral
5101
dz
- 1000000.0000
- 1
- 10000000.00
+ 2.000000
+ 10.000000
+ 20.000000
und
- 12000000.0000
- 1
+ 24.000000
+ 3.000000
@@ -283,13 +324,13 @@ Serializando por partes
Agua Mineral
5101
pack
- 5000000.0000
- 2
- 10000000.00
+ 5000000.000000
+ 2.000000
+ 10000000.000000
und
- 3000000.0000
- 0.3333
+ 3000000.000000
+ 0.333300