diff --git a/pynfe/entidades/base.py b/pynfe/entidades/base.py index 5ffbd7cb..f6e90a62 100644 --- a/pynfe/entidades/base.py +++ b/pynfe/entidades/base.py @@ -1,8 +1,20 @@ # -*- coding: utf-8 -*- +import warnings +from dataclasses import dataclass +from typing import List + + +@dataclass +class CampoDeprecated(object): + anterior: str + novo: str + motivo: str + class Entidade(object): _fonte_dados = None + campos_deprecados: List[CampoDeprecated] = [] def __init__(self, **kwargs): # Codigo para dinamizar a criacao de instancias de entidade, @@ -24,6 +36,30 @@ def __str__(self): def __repr__(self): return "<%s %s>" % (self.__class__.__name__, str(self)) + def __setattr__(self, name, value): + if hasattr(self, name): + # Verifica se o atributo é um campo deprecado + campo_deprecado = next( + (campo for campo in self.campos_deprecados if campo.anterior == name), + None, + ) + + if campo_deprecado: + if campo_deprecado.novo: + warnings.warn( + f"O campo {campo_deprecado.anterior} foi deprecado e será removido em versões futuras. " + f"Utilize {campo_deprecado.novo} no lugar. Motivo: {campo_deprecado.motivo}", + DeprecationWarning, + ) + setattr(self, campo_deprecado.novo, value) + return + else: + raise AttributeError( + f"O campo {campo_deprecado.anterior} foi deprecado e removido." + f"Motivo: {campo_deprecado.motivo}" + ) + super(Entidade, self).__setattr__(name, value) + class Lote(object): pass diff --git a/pynfe/entidades/manifesto.py b/pynfe/entidades/manifesto.py index ee529bca..de1ab2d5 100644 --- a/pynfe/entidades/manifesto.py +++ b/pynfe/entidades/manifesto.py @@ -1,13 +1,12 @@ # -*- coding: utf-8 -*- import random +from decimal import Decimal -from .base import Entidade from pynfe import get_version -from pynfe.utils.flags import MDFE_STATUS, CODIGOS_ESTADOS - from pynfe.utils import so_numeros +from pynfe.utils.flags import CODIGOS_ESTADOS, MDFE_STATUS -from decimal import Decimal +from .base import Entidade class Manifesto(Entidade): @@ -174,7 +173,10 @@ def _codigo_numerico_aleatorio(self): return self.codigo_numerico_aleatorio def _dv_codigo_numerico(self, key): - assert len(key) == 43 + if not len(key) == 43: + raise ValueError( + f"Chave de acesso deve ter 43 caracteres antes de calcular o DV, chave: {key}" + ) weights = [2, 3, 4, 5, 6, 7, 8, 9] weights_size = len(weights) @@ -199,20 +201,17 @@ def _dv_codigo_numerico(self, key): def identificador_unico(self): # Monta 'Id' da tag raiz # Ex.: MDFe35080599999090910270580010000000011518005123 - key = ( - "%(uf)s%(ano)s%(mes)s%(cnpj)s%(mod)s%(serie)s%(nMDF)s%(tpEmis)s%(cMDF)s" - % { - "uf": CODIGOS_ESTADOS[self.uf], - "ano": self.data_emissao.strftime("%y"), - "mes": self.data_emissao.strftime("%m"), - "cnpj": so_numeros(self.emitente.cpfcnpj).zfill(14), - "mod": self.modelo, - "serie": str(self.serie).zfill(3), - "nMDF": str(self.numero_mdfe).zfill(9), - "tpEmis": str(self.forma_emissao), - "cMDF": self._codigo_numerico_aleatorio(), - } - ) + key = "%(uf)s%(ano)s%(mes)s%(cnpj)s%(mod)s%(serie)s%(nMDF)s%(tpEmis)s%(cMDF)s" % { + "uf": CODIGOS_ESTADOS[self.uf], + "ano": self.data_emissao.strftime("%y"), + "mes": self.data_emissao.strftime("%m"), + "cnpj": so_numeros(self.emitente.cpfcnpj).zfill(14), + "mod": self.modelo, + "serie": str(self.serie).zfill(3), + "nMDF": str(self.numero_mdfe).zfill(9), + "tpEmis": str(self.forma_emissao), + "cMDF": self._codigo_numerico_aleatorio(), + } return ( "MDFe%(uf)s%(ano)s%(mes)s%(cnpj)s%(mod)s%(serie)s%(nMDF)s%(tpEmis)s%(cMDF)s%(cDV)s" % { diff --git a/pynfe/entidades/notafiscal.py b/pynfe/entidades/notafiscal.py index bc45bff9..8fd10fb6 100644 --- a/pynfe/entidades/notafiscal.py +++ b/pynfe/entidades/notafiscal.py @@ -8,7 +8,7 @@ from pynfe.utils import so_numeros from pynfe.utils.flags import CODIGOS_ESTADOS, NF_STATUS -from .base import Entidade +from .base import CampoDeprecated, Entidade class NotaFiscal(Entidade): @@ -497,7 +497,10 @@ def _codigo_numerico_aleatorio(self): return self.codigo_numerico_aleatorio def _dv_codigo_numerico(self, key): - assert len(key) == 43 + if not len(key) == 43: + raise ValueError( + f"Chave de acesso deve ter 43 caracteres antes de calcular o DV, chave: {key}" + ) weights = [2, 3, 4, 5, 6, 7, 8, 9] weights_size = len(weights) @@ -548,6 +551,12 @@ def identificador_unico(self): class NotaFiscalReferenciada(Entidade): + # Campos depreciados + campos_deprecados = [ + CampoDeprecated("fcp_percentual", "fcp_aliquota", "Consistencia de nomes"), + CampoDeprecated("fcp_st_percentual", "fcp_st_aliquota", "Consistencia de nomes"), + ] + # - Tipo (seleciona de lista) - NF_REFERENCIADA_TIPOS tipo = str() @@ -752,16 +761,20 @@ class NotaFiscalProduto(Entidade): # - Fundo de Combate a Pobreza fcp_base_calculo = Decimal() - fcp_percentual = Decimal() + fcp_aliquota = Decimal() fcp_valor = Decimal() + # FCP ST fcp_st_base_calculo = Decimal() - fcp_st_percentual = Decimal() + fcp_st_aliquota = Decimal() fcp_st_valor = Decimal() - fcp_destino_valor = Decimal() - fcp_st_valor = Decimal() + + # FCP ST Retido + fcp_st_ret_base_calculo = Decimal() + fcp_st_ret_aliquota = Decimal() fcp_st_ret_valor = Decimal() + icms_inter_destino_valor = Decimal() icms_inter_remetente_valor = Decimal() diff --git a/pynfe/processamento/serializacao.py b/pynfe/processamento/serializacao.py index ab3f5147..bee0f056 100644 --- a/pynfe/processamento/serializacao.py +++ b/pynfe/processamento/serializacao.py @@ -497,7 +497,7 @@ def _serializar_imposto_icms( if produto_servico.fcp_valor: etree.SubElement(icms_item, "pFCP").text = "{:.2f}".format( - produto_servico.fcp_percentual or 0 + produto_servico.fcp_aliquota or 0 ) # Percentual FCP etree.SubElement(icms_item, "vFCP").text = "{:.2f}".format( produto_servico.fcp_valor or 0 @@ -536,7 +536,7 @@ def _serializar_imposto_icms( produto_servico.fcp_base_calculo or 0 ) # Base de calculo FCP etree.SubElement(icms_item, "pFCP").text = "{:.2f}".format( - produto_servico.fcp_percentual or 0 + produto_servico.fcp_aliquota or 0 ) # Percentual FCP etree.SubElement(icms_item, "vFCP").text = "{:.2f}".format( produto_servico.fcp_valor or 0 @@ -571,7 +571,7 @@ def _serializar_imposto_icms( produto_servico.fcp_st_base_calculo or 0 ) etree.SubElement(icms_item, "pFCPST").text = "{:.2f}".format( - produto_servico.fcp_st_percentual or 0 + produto_servico.fcp_st_aliquota or 0 ) etree.SubElement(icms_item, "vFCPST").text = "{:.2f}".format( produto_servico.fcp_st_valor or 0 @@ -623,7 +623,7 @@ def _serializar_imposto_icms( produto_servico.fcp_base_calculo or 0 ) # Base de calculo FCP etree.SubElement(icms_item, "pFCP").text = "{:.2f}".format( - produto_servico.fcp_percentual or 0 + produto_servico.fcp_aliquota or 0 ) # Percentual FCP etree.SubElement(icms_item, "vFCP").text = "{:.2f}".format( produto_servico.fcp_valor or 0 @@ -666,7 +666,7 @@ def _serializar_imposto_icms( produto_servico.fcp_st_base_calculo or 0 ) etree.SubElement(icms_item, "pFCPST").text = "{:.2f}".format( - produto_servico.fcp_st_percentual or 0 + produto_servico.fcp_st_aliquota or 0 ) etree.SubElement(icms_item, "vFCPST").text = "{:.2f}".format( produto_servico.fcp_st_valor or 0 @@ -710,7 +710,7 @@ def _serializar_imposto_icms( produto_servico.fcp_base_calculo or 0 ) # Base de calculo FCP etree.SubElement(icms_item, "pFCP").text = "{:.2f}".format( - produto_servico.fcp_percentual or 0 + produto_servico.fcp_aliquota or 0 ) # Percentual FCP etree.SubElement(icms_item, "vFCP").text = "{:.2f}".format( produto_servico.fcp_valor or 0 @@ -742,15 +742,15 @@ def _serializar_imposto_icms( icms_item, "vICMSSTRet" ).text = "{:.2f}".format(produto_servico.icms_st_ret_valor or 0) - if produto_servico.fcp_st_valor: + if produto_servico.fcp_st_ret_valor: etree.SubElement(icms_item, "vBCFCPSTRet").text = "{:.2f}".format( - produto_servico.fcp_st_base_calculo or 0 + produto_servico.fcp_st_ret_base_calculo or 0 ) etree.SubElement(icms_item, "pFCPSTRet").text = "{:.2f}".format( - produto_servico.fcp_st_percentual or 0 + produto_servico.fcp_st_ret_aliquota or 0 ) etree.SubElement(icms_item, "vFCPSTRet").text = "{:.2f}".format( - produto_servico.fcp_st_valor or 0 + produto_servico.fcp_st_ret_valor or 0 ) # 61=Tributação monofásica sobre combustíveis cobrada anteriormente @@ -794,7 +794,7 @@ def _serializar_imposto_icms( produto_servico.fcp_base_calculo or 0 ) # Base de calculo FCP etree.SubElement(icms_item, "pFCP").text = "{:.2f}".format( - produto_servico.fcp_percentual or 0 + produto_servico.fcp_aliquota or 0 ) # Percentual FCP etree.SubElement(icms_item, "vFCP").text = "{:.2f}".format( produto_servico.fcp_valor or 0 @@ -824,7 +824,7 @@ def _serializar_imposto_icms( produto_servico.fcp_st_base_calculo or 0 ) etree.SubElement(icms_item, "pFCPST").text = "{:.2f}".format( - produto_servico.fcp_st_percentual or 0 + produto_servico.fcp_st_aliquota or 0 ) etree.SubElement(icms_item, "vFCPST").text = "{:.2f}".format( produto_servico.fcp_st_valor or 0 @@ -868,7 +868,7 @@ def _serializar_imposto_icms( produto_servico.fcp_base_calculo or 0 ) # Base de calculo FCP etree.SubElement(icms_item, "pFCP").text = "{:.2f}".format( - produto_servico.fcp_percentual or 0 + produto_servico.fcp_aliquota or 0 ) # Percentual FCP etree.SubElement(icms_item, "vFCP").text = "{:.2f}".format( produto_servico.fcp_valor or 0 @@ -901,7 +901,7 @@ def _serializar_imposto_icms( produto_servico.fcp_st_base_calculo or 0 ) etree.SubElement(icms_item, "pFCPST").text = "{:.2f}".format( - produto_servico.fcp_st_percentual or 0 + produto_servico.fcp_st_aliquota or 0 ) etree.SubElement(icms_item, "vFCPST").text = "{:.2f}".format( produto_servico.fcp_st_valor or 0 @@ -976,7 +976,7 @@ def _serializar_imposto_icms( produto_servico.fcp_st_base_calculo or 0 ) etree.SubElement(icms_item, "pFCPST").text = "{:.2f}".format( - produto_servico.fcp_st_percentual or 0 + produto_servico.fcp_st_aliquota or 0 ) etree.SubElement(icms_item, "vFCPST").text = "{:.2f}".format( produto_servico.fcp_st_valor or 0 @@ -1053,7 +1053,7 @@ def _serializar_imposto_icms( produto_servico.fcp_st_base_calculo or 0 ) etree.SubElement(icms_item, "pFCPST").text = "{:.2f}".format( - produto_servico.fcp_st_percentual or 0 + produto_servico.fcp_st_aliquota or 0 ) etree.SubElement(icms_item, "vFCPST").text = "{:.2f}".format( produto_servico.fcp_st_valor or 0 @@ -2011,21 +2011,11 @@ def gerar_qrcode(self, token, csc, xml, return_qr=False, online=True): url_chave = NFCE[uf]["HOMOLOGACAO"] + NFCE[uf]["URL"] # adicionta tag infNFeSupl com qrcode info = etree.Element("infNFeSupl") - etree.SubElement(info, "qrCode").text = "" + etree.SubElement(info, "qrCode").text = etree.CDATA(qrcode.strip()) etree.SubElement(info, "urlChave").text = url_chave + nfe.insert(1, info) - # correção da tag qrCode, retira caracteres pois e CDATA - tnfe = ( - etree.tostring(nfe, encoding="unicode") - .replace("\n", "") - .replace("<", "<") - .replace(">", ">") - .replace("amp;", "") - ) - etree.tostring(nfe.find(".//qrCode"), encoding="unicode").replace( - "\n", "" - ).replace("<", "<").replace(">", ">").replace("amp;", "") - nfe = etree.fromstring(tnfe) + # retorna nfe com o qrcode incluido NT2015/002 e qrcode if return_qr: return nfe, qrcode.strip() diff --git a/tests/test_nfe_serializacao_csosn_201.py b/tests/test_nfe_serializacao_csosn_201.py index cd225674..e069d777 100644 --- a/tests/test_nfe_serializacao_csosn_201.py +++ b/tests/test_nfe_serializacao_csosn_201.py @@ -145,7 +145,7 @@ def preenche_notafiscal_produto_csosn201(self): icms_st_aliquota=0, icms_st_valor=0, fcp_st_base_calculo=Decimal("117.00"), - fcp_st_percentual=Decimal("0.01"), + fcp_st_aliquota=Decimal("0.01"), fcp_st_valor=Decimal("1.17"), pis_modalidade="51", cofins_modalidade="51", diff --git a/tests/test_nfe_serializacao_csosn_202.py b/tests/test_nfe_serializacao_csosn_202.py index 40fee4d1..d9f2ea16 100644 --- a/tests/test_nfe_serializacao_csosn_202.py +++ b/tests/test_nfe_serializacao_csosn_202.py @@ -141,7 +141,7 @@ def preenche_notafiscal_produto_csosn202(self): icms_st_aliquota=0, icms_st_valor=0, fcp_st_base_calculo=Decimal("117.00"), - fcp_st_percentual=Decimal("0.01"), + fcp_st_aliquota=Decimal("0.01"), fcp_st_valor=Decimal("1.17"), pis_modalidade="51", cofins_modalidade="51", diff --git a/tests/test_nfe_serializacao_csosn_203.py b/tests/test_nfe_serializacao_csosn_203.py index c0a8f25f..a083dc51 100644 --- a/tests/test_nfe_serializacao_csosn_203.py +++ b/tests/test_nfe_serializacao_csosn_203.py @@ -141,7 +141,7 @@ def preenche_notafiscal_produto_csosn203(self): icms_st_aliquota=0, icms_st_valor=0, fcp_st_base_calculo=Decimal("117.00"), - fcp_st_percentual=Decimal("0.01"), + fcp_st_aliquota=Decimal("0.01"), fcp_st_valor=Decimal("1.17"), pis_modalidade="51", cofins_modalidade="51", diff --git a/tests/test_nfe_serializacao_csosn_900.py b/tests/test_nfe_serializacao_csosn_900.py index a3f4c840..9f5eb1de 100644 --- a/tests/test_nfe_serializacao_csosn_900.py +++ b/tests/test_nfe_serializacao_csosn_900.py @@ -1,14 +1,16 @@ #!/usr/bin/env python # *-* encoding: utf8 *-* +import datetime import unittest +from decimal import Decimal from pynfe.entidades.cliente import Cliente from pynfe.entidades.emitente import Emitente -from pynfe.entidades.notafiscal import NotaFiscal from pynfe.entidades.fonte_dados import _fonte_dados -from pynfe.processamento.serializacao import SerializacaoXML +from pynfe.entidades.notafiscal import NotaFiscal from pynfe.processamento.assinatura import AssinaturaA1 +from pynfe.processamento.serializacao import SerializacaoXML from pynfe.processamento.validacao import Validacao from pynfe.utils.flags import ( CODIGO_BRASIL, @@ -18,8 +20,6 @@ XSD_NFE, XSD_NFE_PROCESSADA, ) -from decimal import Decimal -import datetime class SerializacaoNFeTestCase(unittest.TestCase): @@ -149,7 +149,7 @@ def preenche_notafiscal_produto_csosn900(self): icms_st_aliquota=Decimal("0.10"), icms_st_valor=Decimal("1.17"), fcp_st_base_calculo=Decimal("117.00"), - fcp_st_percentual=Decimal("0.01"), + fcp_st_aliquota=Decimal("0.01"), fcp_st_valor=Decimal("1.17"), pis_modalidade="51", cofins_modalidade="51", diff --git a/tests/test_nfe_serializacao_cst_70.py b/tests/test_nfe_serializacao_cst_70.py index 3320b276..deb600d1 100644 --- a/tests/test_nfe_serializacao_cst_70.py +++ b/tests/test_nfe_serializacao_cst_70.py @@ -1,14 +1,16 @@ #!/usr/bin/env python # *-* encoding: utf8 *-* +import datetime import unittest +from decimal import Decimal from pynfe.entidades.cliente import Cliente from pynfe.entidades.emitente import Emitente -from pynfe.entidades.notafiscal import NotaFiscal from pynfe.entidades.fonte_dados import _fonte_dados -from pynfe.processamento.serializacao import SerializacaoXML +from pynfe.entidades.notafiscal import NotaFiscal from pynfe.processamento.assinatura import AssinaturaA1 +from pynfe.processamento.serializacao import SerializacaoXML from pynfe.processamento.validacao import Validacao from pynfe.utils.flags import ( CODIGO_BRASIL, @@ -18,8 +20,6 @@ XSD_NFE, XSD_NFE_PROCESSADA, ) -from decimal import Decimal -import datetime class SerializacaoNFeTestCase(unittest.TestCase): @@ -147,7 +147,7 @@ def preenche_notafiscal_produto_cst70(self): icms_aliquota=Decimal("18.00"), icms_valor=Decimal("3.36"), fcp_base_calculo=Decimal("3.36"), - fcp_percentual=Decimal("2.00"), + fcp_aliquota=Decimal("2.00"), fcp_valor=Decimal("0.06"), icms_st_modalidade_determinacao_bc=4, icms_st_percentual_adicional=Decimal("0.00"), @@ -156,7 +156,7 @@ def preenche_notafiscal_produto_cst70(self): icms_st_aliquota=Decimal("18.00"), icms_st_valor=Decimal("1.44"), fcp_st_base_calculo=Decimal("1.44"), - fcp_st_percentual=Decimal("2.00"), + fcp_st_aliquota=Decimal("2.00"), fcp_st_valor=Decimal("0.02"), )