Skip to content

Commit e1e5e7e

Browse files
authored
Serializacao: Adequecao de campos de qCom e qTrib ao TDec_1104v (#419)
* Serializacao: Adequecao de campos de quantidade ao TDec_1104v * Correção de lint * Teste para validar função formatar quantidade
1 parent 53b0762 commit e1e5e7e

2 files changed

Lines changed: 167 additions & 2 deletions

File tree

pynfe/processamento/serializacao.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import warnings
66

77
from datetime import datetime
8+
from decimal import Decimal
89

910
import pynfe.utils.xml_writer as xmlw
1011
from pynfe.entidades import Manifesto, NotaFiscal
@@ -269,6 +270,13 @@ def _serializar_autorizados_baixar_xml(
269270
else:
270271
return raiz
271272

273+
def _formatarQuantidade(self, quantidade: Decimal) -> str:
274+
return (
275+
str(quantidade.quantize(Decimal("1.0000")).normalize())
276+
if quantidade % 1 != 0
277+
else str(int(quantidade))
278+
)
279+
272280
def _serializar_produto_servico(
273281
self, produto_servico, modelo, tag_raiz="det", retorna_string=True
274282
):
@@ -290,7 +298,9 @@ def _serializar_produto_servico(
290298
etree.SubElement(prod, "cBenef").text = produto_servico.cbenef
291299
etree.SubElement(prod, "CFOP").text = produto_servico.cfop
292300
etree.SubElement(prod, "uCom").text = produto_servico.unidade_comercial
293-
etree.SubElement(prod, "qCom").text = str(produto_servico.quantidade_comercial or 0)
301+
etree.SubElement(prod, "qCom").text = self._formatarQuantidade(
302+
produto_servico.quantidade_comercial or 0
303+
)
294304
etree.SubElement(prod, "vUnCom").text = str("{:.10f}").format(
295305
produto_servico.valor_unitario_comercial or 0
296306
)
@@ -308,7 +318,9 @@ def _serializar_produto_servico(
308318
)
309319
etree.SubElement(prod, "cEANTrib").text = produto_servico.ean_tributavel
310320
etree.SubElement(prod, "uTrib").text = produto_servico.unidade_tributavel
311-
etree.SubElement(prod, "qTrib").text = str(produto_servico.quantidade_tributavel)
321+
etree.SubElement(prod, "qTrib").text = self._formatarQuantidade(
322+
produto_servico.quantidade_tributavel
323+
)
312324
etree.SubElement(prod, "vUnTrib").text = "{:.10f}".format(
313325
produto_servico.valor_unitario_tributavel or 0
314326
)
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
#!/usr/bin/env python
2+
# *-* encoding: utf8 *-*
3+
4+
import datetime
5+
import unittest
6+
from decimal import Decimal
7+
8+
from pynfe.entidades.emitente import Emitente
9+
from pynfe.entidades.fonte_dados import _fonte_dados
10+
from pynfe.entidades.notafiscal import NotaFiscal
11+
from pynfe.processamento.assinatura import AssinaturaA1
12+
from pynfe.processamento.serializacao import SerializacaoXML
13+
from pynfe.processamento.validacao import Validacao
14+
from pynfe.utils.flags import (
15+
CODIGO_BRASIL,
16+
NAMESPACE_NFE,
17+
NAMESPACE_SIG,
18+
XSD_FOLDER_NFE,
19+
XSD_NFE,
20+
XSD_NFE_PROCESSADA,
21+
)
22+
23+
24+
class SerializacaoNFeTestCase(unittest.TestCase):
25+
def setUp(self):
26+
self.certificado = "./tests/certificado.pfx"
27+
self.senha = bytes("123456", "utf-8")
28+
self.uf = "pr"
29+
self.homologacao = True
30+
31+
self.ns = {"ns": NAMESPACE_NFE}
32+
self.ns_sig = {"ns": NAMESPACE_SIG}
33+
34+
self.validacao = Validacao()
35+
self.xsd_procNFe = self.validacao.get_xsd(
36+
xsd_file=XSD_NFE_PROCESSADA, xsd_folder=XSD_FOLDER_NFE
37+
)
38+
self.xsd_nfe = self.validacao.get_xsd(xsd_file=XSD_NFE, xsd_folder=XSD_FOLDER_NFE)
39+
40+
def preenche_emitente(self):
41+
self.emitente = Emitente(
42+
razao_social="NF-E EMITIDA EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL",
43+
nome_fantasia="Nome Fantasia da Empresa",
44+
cnpj="99999999000199", # cnpj apenas números
45+
codigo_de_regime_tributario="3", # 1 para simples nacional ou 3 para normal
46+
inscricao_estadual="9999999999", # numero de IE da empresa
47+
inscricao_municipal="12345",
48+
cnae_fiscal="9999999", # cnae apenas números
49+
endereco_logradouro="Rua da Paz",
50+
endereco_numero="666",
51+
endereco_bairro="Sossego",
52+
endereco_municipio="Paranavaí",
53+
endereco_uf="PR",
54+
endereco_cep="87704000",
55+
endereco_pais=CODIGO_BRASIL,
56+
)
57+
return self.emitente
58+
59+
def preenche_notafiscal_produto(self, quantidade):
60+
utc = datetime.timezone.utc
61+
data_emissao = datetime.datetime(2021, 1, 14, 12, 0, 0, tzinfo=utc)
62+
data_saida_entrada = datetime.datetime(2021, 1, 14, 13, 10, 20, tzinfo=utc)
63+
64+
self.notafiscal = NotaFiscal(
65+
emitente=self.emitente,
66+
cliente=None,
67+
uf="PR",
68+
natureza_operacao="VENDA", # venda, compra, transferência, devolução, etc
69+
forma_pagamento=0, # 0=Pagamento à vista; 1=Pagamento a prazo; 2=Outros.
70+
modelo=65, # 55=NF-e; 65=NFC-e
71+
serie="1",
72+
numero_nf="111", # Número do Documento Fiscal.
73+
data_emissao=data_emissao,
74+
data_saida_entrada=data_saida_entrada,
75+
tipo_documento=1, # 0=entrada; 1=saida
76+
municipio="4118402", # Código IBGE do Município
77+
tipo_impressao_danfe=1, # 1=DANFE normal
78+
forma_emissao="1", # 1=Emissão normal (não em contingência);
79+
cliente_final=1, # 0=Normal;1=Consumidor final;
80+
indicador_destino=1,
81+
indicador_presencial=1,
82+
finalidade_emissao="1", # 1=NF-e normal
83+
processo_emissao="0", # 0=Emissão de NF-e com aplicativo do contribuinte;
84+
transporte_modalidade_frete=1,
85+
informacoes_adicionais_interesse_fisco="Mensagem complementar",
86+
totais_tributos_aproximado=Decimal("1.01"),
87+
valor_troco=Decimal("3.00"),
88+
)
89+
90+
self.notafiscal.adicionar_produto_servico(
91+
codigo="000328", # id do produto
92+
descricao="Produto teste",
93+
ncm="99999999",
94+
# cest='0100100', # NT2015/003
95+
ean="1234567890121",
96+
cfop="5102",
97+
unidade_comercial="UN",
98+
quantidade_comercial=Decimal(quantidade),
99+
valor_unitario_comercial=Decimal("0.00"),
100+
valor_total_bruto=Decimal("0.00"),
101+
unidade_tributavel="UN",
102+
quantidade_tributavel=Decimal(quantidade),
103+
valor_unitario_tributavel=Decimal("0.00"),
104+
ean_tributavel="SEM GTIN",
105+
ind_total=1,
106+
icms_modalidade="00",
107+
icms_origem=0,
108+
icms_csosn="",
109+
pis_modalidade="51",
110+
cofins_modalidade="51",
111+
pis_valor_base_calculo=Decimal("0.00"),
112+
pis_aliquota_percentual=Decimal("0.00"),
113+
pis_valor=Decimal("0.00"),
114+
cofins_valor_base_calculo=Decimal("0.00"),
115+
cofins_aliquota_percentual=Decimal("0.00"),
116+
cofins_valor=Decimal("0.00"),
117+
valor_tributos_aprox="1.01",
118+
)
119+
120+
def serializa_nfe(self):
121+
serializador = SerializacaoXML(
122+
fonte_dados=_fonte_dados, homologacao=self.homologacao, so_cpf=True
123+
)
124+
return serializador.exportar()
125+
126+
def assina_xml(self):
127+
a1 = AssinaturaA1(self.certificado, self.senha)
128+
return a1.assinar(self.xml)
129+
130+
def validacao_com_xsd_do_xml_gerado_sem_processar(self):
131+
self.validacao.validar_etree(
132+
xml_doc=self.xml_assinado, xsd_file=self.xsd_nfe, use_assert=True
133+
)
134+
135+
def test_notafiscal_produto_com_vnf_e_vprod_com_duas_casas_decimais(self):
136+
quantidade = 1.123456
137+
# Preenche as classes do pynfe
138+
self.emitente = self.preenche_emitente()
139+
self.notafiscal = self.preenche_notafiscal_produto(quantidade)
140+
141+
# Serializa e assina o XML
142+
self.xml = self.serializa_nfe()
143+
self.xml_assinado = self.assina_xml()
144+
145+
qCom = self.xml_assinado.xpath("//ns:det/ns:prod/ns:qCom", namespaces=self.ns)[0].text
146+
qTrib = self.xml_assinado.xpath("//ns:det/ns:prod/ns:qTrib", namespaces=self.ns)[0].text
147+
148+
self.assertEqual(qCom, "1.1235")
149+
self.assertEqual(qTrib, "1.1235")
150+
151+
152+
if __name__ == "__main__":
153+
unittest.main()

0 commit comments

Comments
 (0)