diff --git a/requeriments.txt b/requeriments.txt index 57c231d..9533696 100644 --- a/requeriments.txt +++ b/requeriments.txt @@ -1,3 +1,4 @@ coveralls==0.5 six==1.9.0 pytest==2.6.4 +cchardet==2.1.5 diff --git a/setup.py b/setup.py index 6a89b0e..223a59d 100644 --- a/setup.py +++ b/setup.py @@ -50,7 +50,7 @@ def run_tests(self): 'Programming Language :: Python :: 3.6', ], keywords='sped fiscal contábil contabilidade receita federal', - install_requires=['six'], + install_requires=['six','cchardet'], tests_require=['pytest'], extras_require={ 'dev': ['pylint>=1.9.1'], diff --git a/sped/__init__.py b/sped/__init__.py index 73aff5c..82dbebe 100644 --- a/sped/__init__.py +++ b/sped/__init__.py @@ -7,4 +7,4 @@ from .escrituracao import Escrituracao -__version__ = '1.0.2' +__version__ = '1.0.3' diff --git a/sped/arquivos.py b/sped/arquivos.py index a6a0ea5..cf3a845 100644 --- a/sped/arquivos.py +++ b/sped/arquivos.py @@ -1,12 +1,11 @@ # -*- coding: utf-8 -*- - +import re from collections import OrderedDict from io import StringIO from .registros import RegistroIndefinido - class ArquivoDigital(object): registros = None blocos = None @@ -19,31 +18,55 @@ def __init__(self): self._registro_encerramento = self.registro_encerramento() self._blocos = OrderedDict() - def readfile(self, filename): - with open(filename) as spedfile: - for line in [line.rstrip('\r\n') for line in spedfile]: - self.read_registro(line.decode('utf8')) + def readfile(self, filename, codificacao='utf-8', verbose=None): + sucesso = False + with open(filename, 'r', encoding=codificacao) as spedfile: # encoding='utf-8', 'latin-1' + for line in [line.strip() for line in spedfile]: + # a simple way to remove multiple spaces in a string + line = re.sub('\s{2,}', ' ', line) + # Em algumas EFDs foram encontrados registros digitados incorretamente em minúsculo. + # Por exemplo, o registro 'c491' deve ser corrigido para 'C491'. + line = line[:6].upper() + line[6:] # line = '|c491|...' --> '|C491|...' + regt = self.read_registro(line) + # Verificar se foi lido o arquivo SPED até a última linha válida que contém o registro '9999'. + if regt.__class__ == self.__class__.registro_encerramento: + sucesso = True + break + if not sucesso: + raise RuntimeError(u"\nOcorreu uma falha ao ler o arquivo: '%s'.\n" % filename) + elif verbose: + print(u"O arquivo SPED '%s' foi lido com sucesso.\n" % filename) def read_registro(self, line): reg_id = line.split('|')[1] - + try: - registro_class = getattr(self.__class__.registros, - 'Registro' + reg_id) + registro_class = getattr(self.__class__.registros, 'Registro' + reg_id) except AttributeError: - raise RuntimeError(u"Arquivo inválido para EFD - PIS/COFINS") + raise RuntimeError(u"Arquivo inválido para EFD - PIS/COFINS. Registro: %s" % reg_id) registro = registro_class(line) + bloco_id = reg_id[0] + bloco = self._blocos[bloco_id] if registro.__class__ == self.__class__.registro_abertura: + # Atualizar o registro de abertura 0000 self._registro_abertura = registro elif registro.__class__ == self.__class__.registro_encerramento: + # Atualizar o registro de encerramento 9999 self._registro_encerramento = registro + elif registro.__class__ == bloco.registro_abertura.__class__: + # Atualizar os registros de abertura: 0001, A001, C001, ... + bloco.registro_abertura = registro + elif registro.__class__ == bloco.registro_encerramento.__class__: + # Atualizar os registros de encerramento: 0990, A990, C990, ... + bloco.registro_encerramento = registro else: - bloco_id = reg_id[0] - bloco = self._blocos[bloco_id] + # Adicionar novos registros a cada linha obtida de filename bloco.add(registro) + return registro + def write_to(self, buff): buff.write(self._registro_abertura.as_line() + u'\r\n') reg_count = 2 diff --git a/sped/blocos.py b/sped/blocos.py index 8f2d497..8da85c3 100644 --- a/sped/blocos.py +++ b/sped/blocos.py @@ -2,7 +2,6 @@ from .registros import Registro - class Bloco(object): def __init__(self, nome=''): self._nome = nome @@ -14,12 +13,10 @@ def __repr__(self): @property def abertura(self): - # Define o indicador de movimento ou dados return self.registro_abertura @property def encerramento(self): - # Define a quantidade de registros return self.registro_encerramento @property @@ -27,7 +24,4 @@ def registros(self): return [self.abertura] + self._registros + [self.encerramento] def add(self, registro): - # Não adiciona o registro de abertura e fechamento - if not registro.__class__ == self.registro_abertura.__class__ and \ - not registro.__class__ == self.registro_encerramento.__class__: - self._registros.append(registro) + self._registros.append(registro) diff --git a/sped/campos.py b/sped/campos.py index 9c9f575..d5341a6 100644 --- a/sped/campos.py +++ b/sped/campos.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- - import re from datetime import date @@ -188,6 +187,8 @@ def get(self, registro): return datetime.strptime(valor, '%d%m%Y').date() def set(self, registro, valor): + # https://stackoverflow.com/questions/19887353/attributeerror-str-object-has-no-attribute-strftime + valor = datetime.strptime(valor, '%d%m%Y') if isinstance(valor, date): super().set(registro, valor.strftime('%d%m%Y')) elif not valor: @@ -275,3 +276,33 @@ def validar(valor): if len(valor) == 11: return CampoCPF.validar(valor) return False + + +# Fonte: 'NFe Manual_de_Orientacao_Contribuinte_v_6.00.pdf', pg 144. +# 5.4 Cálculo do Dígito Verificador da Chave de Acesso da NF-e +class CampoChaveEletronica(Campo): + @staticmethod + def validar(valor): + if not re.search('^\d{44}$', str(valor)): + return False + + chave = [int(digito) for digito in valor] + multiplicadores = [4, 3, 2] + [9, 8, 7, 6, 5, 4, 3, 2] * 5 + [0] + + soma = sum([chave[i] * multiplicadores[i] for i in range(44)]) + + resto_da_divisao = soma % 11 + digito_verificador = 11 - resto_da_divisao + + if digito_verificador >= 10: + digito_verificador = 0 + + if chave[-1] != digito_verificador: + return False + + # dentro da chave eletrônica há o CNPJ do emitente + # que também será verificado + cnpj = str(valor)[6:20] + + return CampoCNPJ.validar(cnpj) + diff --git a/sped/efd/pis_cofins/blocos.py b/sped/efd/pis_cofins/blocos.py index 5246afa..5be42b7 100644 --- a/sped/efd/pis_cofins/blocos.py +++ b/sped/efd/pis_cofins/blocos.py @@ -30,7 +30,6 @@ class Bloco0(Bloco): registro_abertura = Registro0001() registro_encerramento = Registro0990() - class BlocoA(Bloco): """ Documentos Fiscais - Serviços (ISS) @@ -38,7 +37,6 @@ class BlocoA(Bloco): registro_abertura = RegistroA001() registro_encerramento = RegistroA990() - class BlocoC(Bloco): """ Documentos Fiscais I – Mercadorias (ICMS/IPI) @@ -99,5 +97,5 @@ class Bloco9(Bloco): """ Controle e Encerramento do Arquivo Digital """ - registro_abertura = Registro0001() - registro_encerramento = Registro0990() + registro_abertura = Registro9001() + registro_encerramento = Registro9990() diff --git a/sped/efd/pis_cofins/registros.py b/sped/efd/pis_cofins/registros.py index fc1762b..2024254 100644 --- a/sped/efd/pis_cofins/registros.py +++ b/sped/efd/pis_cofins/registros.py @@ -9,7 +9,6 @@ from ...campos import CampoRegex from ...registros import Registro - class Registro0000(Registro): """ Abertura do Arquivo Digital e Identificação da Pessoa Jurídica @@ -39,7 +38,8 @@ class Registro0000(Registro): Campo(13, 'IND_NAT_PJ'), Campo(14, 'IND_ATIV', obrigatorio=True), ] - + + nivel = 0 class Registro0001(Registro): """ @@ -49,7 +49,8 @@ class Registro0001(Registro): CampoFixo(1, 'REG', '0001'), Campo(2, 'IND_MOV', obrigatorio=True) ] - + + nivel = 1 class Registro0035(Registro): """ @@ -61,7 +62,8 @@ class Registro0035(Registro): Campo(3, 'DESC_SCP', obrigatorio=True), Campo(4, 'INF_COMP', obrigatorio=True), ] - + + nivel = 2 class Registro0100(Registro): """ @@ -83,7 +85,8 @@ class Registro0100(Registro): Campo(13, 'EMAIL'), Campo(14, 'COD_MUN'), ] - + + nivel = 2 class Registro0110(Registro): """ @@ -96,7 +99,8 @@ class Registro0110(Registro): Campo(4, 'COD_TIPO_CONT'), Campo(5, 'IND_REG_CUM'), ] - + + nivel = 2 class Registro0111(Registro): """ @@ -110,7 +114,8 @@ class Registro0111(Registro): CampoNumerico(5, 'REC_BRU_CUM', obrigatorio=True), CampoNumerico(6, 'REC_BRU_TOTAL', obrigatorio=True), ] - + + nivel = 3 class Registro0120(Registro): """ @@ -121,7 +126,8 @@ class Registro0120(Registro): Campo(2, 'MES_DISPENSA', obrigatorio=True), Campo(3, 'INF_COMP'), ] - + + nivel = 2 class Registro0140(Registro): """ @@ -138,7 +144,8 @@ class Registro0140(Registro): Campo(8, 'IM'), Campo(9, 'SUFRAMA'), ] - + + nivel = 2 class Registro0145(Registro): """ @@ -152,7 +159,8 @@ class Registro0145(Registro): CampoNumerico(5, 'VL_REC_DEMAIS_ATIV'), Campo(6, 'INFO_COMPL'), ] - + + nivel = 3 class Registro0150(Registro): """ @@ -173,7 +181,8 @@ class Registro0150(Registro): Campo(12, 'COMPL'), Campo(13, 'BAIRRO'), ] - + + nivel = 3 class Registro0190(Registro): """ @@ -184,7 +193,8 @@ class Registro0190(Registro): Campo(2, 'UNID', obrigatorio=True), Campo(3, 'DESCR', obrigatorio=True), ] - + + nivel = 3 class Registro0200(Registro): """ @@ -204,7 +214,8 @@ class Registro0200(Registro): Campo(11, 'COD_LST'), CampoNumerico(12, 'ALIQ_ICMS'), ] - + + nivel = 3 class Registro0205(Registro): """ @@ -217,7 +228,8 @@ class Registro0205(Registro): CampoData(4, 'DT_FIM', obrigatorio=True), Campo(5, 'COD_ANT_ITEM'), ] - + + nivel = 4 class Registro0206(Registro): """ @@ -227,7 +239,8 @@ class Registro0206(Registro): CampoFixo(1, 'REG', '0206'), Campo(2, 'COD_COMB', obrigatorio=True), ] - + + nivel = 4 class Registro0208(Registro): """ @@ -239,7 +252,8 @@ class Registro0208(Registro): Campo(3, 'COD_GRU', obrigatorio=True), Campo(4, 'MARCA_COM', obrigatorio=True), ] - + + nivel = 4 class Registro0400(Registro): """ @@ -250,7 +264,8 @@ class Registro0400(Registro): Campo(2, 'COD_NAT', obrigatorio=True), Campo(3, 'DESCR_NAT', obrigatorio=True), ] - + + nivel = 3 class Registro0450(Registro): """ @@ -261,7 +276,8 @@ class Registro0450(Registro): Campo(2, 'COD_INF', obrigatorio=True), Campo(3, 'TXT', obrigatorio=True), ] - + + nivel = 3 class Registro0500(Registro): """ @@ -278,7 +294,8 @@ class Registro0500(Registro): Campo(8, 'COD_CTA_REF'), CampoCNPJ(9, 'CNPJ_EST'), ] - + + nivel = 2 class Registro0600(Registro): """ @@ -290,7 +307,32 @@ class Registro0600(Registro): Campo(3, 'COD_CCUS', obrigatorio=True), Campo(4, 'CCUS', obrigatorio=True), ] + + nivel = 2 +class Registro0900(Registro): + """ + Composição das Receitas do Período - Receita Bruta e Demais Receitas + """ + campos = [ + CampoFixo(1, 'REG', '0900'), + CampoNumerico(2, 'REC_TOTAL_BLOCO_A', obrigatorio=True), + CampoNumerico(3, 'REC_NRB_BLOCO_A'), + CampoNumerico(4, 'REC_TOTAL_BLOCO_C', obrigatorio=True), + CampoNumerico(5, 'REC_NRB_BLOCO_C'), + CampoNumerico(6, 'REC_TOTAL_BLOCO_D', obrigatorio=True), + CampoNumerico(7, 'REC_NRB_BLOCO_D'), + CampoNumerico(8, 'REC_TOTAL_BLOCO_F', obrigatorio=True), + CampoNumerico(9, 'REC_NRB_BLOCO_F'), + CampoNumerico(10, 'REC_TOTAL_BLOCO_I', obrigatorio=True), + CampoNumerico(11, 'REC_NRB_BLOCO_I'), + CampoNumerico(12, 'REC_TOTAL_BLOCO_1', obrigatorio=True), + CampoNumerico(13, 'REC_NRB_BLOCO_1'), + CampoNumerico(14, 'REC_TOTAL_PERIODO', obrigatorio=True), + CampoNumerico(15, 'REC_TOTAL_NRB_PERÍODO'), + ] + + nivel = 2 class Registro0990(Registro): """ @@ -300,7 +342,9 @@ class Registro0990(Registro): CampoFixo(1, 'REG', '0990'), CampoNumerico(2, 'QTD_LIN_0', obrigatorio=True), ] - + + nivel = 1 + class RegistroA001(Registro): """ @@ -310,7 +354,8 @@ class RegistroA001(Registro): CampoFixo(1, 'REG', 'A001'), Campo(2, 'IND_MOV', obrigatorio=True), ] - + + nivel = 1 class RegistroA010(Registro): """ @@ -320,7 +365,7 @@ class RegistroA010(Registro): CampoFixo(1, 'REG', 'A010'), CampoCNPJ(2, 'CNPJ', obrigatorio=True), ] - + nivel = 2 class RegistroA100(Registro): """ @@ -349,7 +394,7 @@ class RegistroA100(Registro): CampoNumerico(20, 'VL_COFINS_RET'), CampoNumerico(21, 'VL_ISS'), ] - + nivel = 3 class RegistroA110(Registro): """ @@ -360,7 +405,8 @@ class RegistroA110(Registro): Campo(2, 'COD_INF', obrigatorio=True), Campo(3, 'TXT_COMPL'), ] - + + nivel = 4 class RegistroA111(Registro): """ @@ -371,7 +417,8 @@ class RegistroA111(Registro): Campo(2, 'NUM_PROC', obrigatorio=True), Campo(3, 'IND_PROC', obrigatorio=True), ] - + + nivel = 4 class RegistroA120(Registro): """ @@ -388,7 +435,8 @@ class RegistroA120(Registro): CampoData(8, 'DT_PAG_COFINS'), Campo(9, 'LOC_EXE_SERV', obrigatorio=True), ] - + + nivel = 4 class RegistroA170(Registro): """ @@ -414,7 +462,8 @@ class RegistroA170(Registro): Campo(17, 'COD_CTA'), Campo(18, 'COD_CCUS'), ] - + + nivel = 4 class RegistroA990(Registro): """ @@ -424,7 +473,8 @@ class RegistroA990(Registro): CampoFixo(1, 'REG', 'A990'), CampoNumerico(2, 'QTD_LIN_A', obrigatorio=True), ] - + + nivel = 1 class RegistroC001(Registro): """ @@ -434,7 +484,8 @@ class RegistroC001(Registro): CampoFixo(1, 'REG', 'C001'), Campo(2, 'IND_MOV', obrigatorio=True), ] - + + nivel = 1 class RegistroC010(Registro): """ @@ -445,7 +496,8 @@ class RegistroC010(Registro): CampoCNPJ(2, 'CNPJ', obrigatorio=True), Campo(3, 'IND_ESCRI'), ] - + + nivel = 2 class RegistroC100(Registro): """ @@ -457,15 +509,15 @@ class RegistroC100(Registro): Campo(2, 'IND_OPER', obrigatorio=True), Campo(3, 'IND_EMIT', obrigatorio=True), Campo(4, 'COD_PART', obrigatorio=True), - Campo(5, 'COD_MOD', obrigatorio=True), - Campo(6, 'COD_SIT;', obrigatorio=True), + Campo(5, 'COD_MOD', obrigatorio=True), + Campo(6, 'COD_SIT', obrigatorio=True), Campo(7, 'SER'), Campo(8, 'NUM_DOC', obrigatorio=True), Campo(9, 'CHV_NFE'), - Campo(10, 'CHV_NFE', obrigatorio=True), - CampoData(11, 'DT_DOC'), - CampoData(12, 'DT_E_S', obrigatorio=True), - CampoNumerico(13, 'VL_DOC', obrigatorio=True), + CampoData(10, 'DT_DOC', obrigatorio=True), + CampoData(11, 'DT_E_S'), + CampoNumerico(12, 'VL_DOC', obrigatorio=True), + CampoNumerico(13, 'IND_PGTO', obrigatorio=True), CampoNumerico(14, 'VL_DESC'), CampoNumerico(15, 'VL_ABAT_NT'), CampoNumerico(16, 'VL_MERC'), @@ -483,7 +535,8 @@ class RegistroC100(Registro): CampoNumerico(28, 'VL_PIS_ST'), CampoNumerico(29, 'VL_COFINS_ST'), ] - + + nivel = 3 class RegistroC110(Registro): """ @@ -494,7 +547,8 @@ class RegistroC110(Registro): Campo(2, 'COD_INF', obrigatorio=True), Campo(3, 'TXT_COMPL'), ] - + + nivel = 4 class RegistroC111(Registro): """ @@ -505,7 +559,8 @@ class RegistroC111(Registro): Campo(2, 'NUM_PROC', obrigatorio=True), Campo(3, 'IND_PROC', obrigatorio=True), ] - + + nivel = 4 class RegistroC120(Registro): """ @@ -519,7 +574,8 @@ class RegistroC120(Registro): CampoNumerico(5, 'VL_COFINS_IMP'), Campo(6, 'NUM_ACDRAW'), ] - + + nivel = 4 class RegistroC170(Registro): """ @@ -564,7 +620,8 @@ class RegistroC170(Registro): CampoNumerico(36, 'VL_COFINS'), Campo(37, 'COD_CTA'), ] - + + nivel = 4 class RegistroC175(Registro): """ @@ -590,7 +647,8 @@ class RegistroC175(Registro): Campo(17, 'COD_CTA'), Campo(18, 'INFO_COMPL'), ] - + + nivel = 4 class RegistroC180(Registro): """ @@ -606,7 +664,8 @@ class RegistroC180(Registro): Campo(7, 'EX_IPI'), CampoNumerico(8, 'VL_TOT_ITEM', obrigatorio=True), ] - + + nivel = 3 class RegistroC181(Registro): """ @@ -625,7 +684,8 @@ class RegistroC181(Registro): CampoNumerico(10, 'VL_PIS'), Campo(11, 'COD_CTA'), ] - + + nivel = 4 class RegistroC185(Registro): """ @@ -644,7 +704,8 @@ class RegistroC185(Registro): CampoNumerico(10, 'VL_COFINS'), Campo(11, 'COD_CTA'), ] - + + nivel = 4 class RegistroC188(Registro): """ @@ -655,7 +716,8 @@ class RegistroC188(Registro): Campo(2, 'NUM_PROC', obrigatorio=True), Campo(3, 'IND_PROC', obrigatorio=True), ] - + + nivel = 4 class RegistroC190(Registro): """ @@ -672,7 +734,8 @@ class RegistroC190(Registro): Campo(7, 'EX_IPI'), CampoNumerico(8, 'VL_TOT_ITEM', obrigatorio=True), ] - + + nivel = 3 class RegistroC191(Registro): """ @@ -693,7 +756,8 @@ class RegistroC191(Registro): CampoNumerico(11, 'VL_PIS'), Campo(12, 'COD_CTA'), ] - + + nivel = 4 class RegistroC195(Registro): """ @@ -714,7 +778,8 @@ class RegistroC195(Registro): CampoNumerico(11, 'VL_COFINS'), Campo(12, 'COD_CTA'), ] - + + nivel = 4 class RegistroC198(Registro): """ @@ -725,7 +790,8 @@ class RegistroC198(Registro): Campo(2, 'NUM_PROC', obrigatorio=True), Campo(3, 'IND_PROC', obrigatorio=True), ] - + + nivel = 4 class RegistroC199(Registro): """ @@ -739,7 +805,8 @@ class RegistroC199(Registro): CampoNumerico(5, 'VL_COFINS_IMP'), Campo(6, 'NUM_ACDRAW'), ] - + + nivel = 4 class RegistroC380(Registro): """ @@ -755,7 +822,8 @@ class RegistroC380(Registro): CampoNumerico(7, 'VL_DOC', obrigatorio=True), CampoNumerico(8, 'VL_DOC_CANC', obrigatorio=True), ] - + + nivel = 3 class RegistroC381(Registro): """ @@ -773,7 +841,8 @@ class RegistroC381(Registro): CampoNumerico(9, 'VL_PIS', obrigatorio=True), Campo(10, 'COD_CTA'), ] - + + nivel = 4 class RegistroC385(Registro): """ @@ -791,7 +860,8 @@ class RegistroC385(Registro): CampoNumerico(9, 'VL_COFINS', obrigatorio=True), Campo(10, 'COD_CTA'), ] - + + nivel = 4 class RegistroC395(Registro): """ @@ -807,7 +877,8 @@ class RegistroC395(Registro): CampoData(7, 'DT_DOC', obrigatorio=True), CampoNumerico(8, 'VL_DOC', obrigatorio=True), ] - + + nivel = 4 class RegistroC396(Registro): """ @@ -829,7 +900,8 @@ class RegistroC396(Registro): CampoNumerico(13, 'VL_COFINS'), Campo(14, 'COD_CTA'), ] - + + nivel = 4 class RegistroC400(Registro): """ @@ -842,7 +914,8 @@ class RegistroC400(Registro): Campo(4, 'ECF_FAB', obrigatorio=True), Campo(5, 'ECF_CX', obrigatorio=True), ] - + + nivel = 3 class RegistroC405(Registro): """ @@ -857,7 +930,8 @@ class RegistroC405(Registro): Campo(6, 'GT_FIN', obrigatorio=True), CampoNumerico(7, 'VL_BRT', obrigatorio=True), ] - + + nivel = 4 class RegistroC481(Registro): """ @@ -875,7 +949,8 @@ class RegistroC481(Registro): Campo(9, 'COD_ITEM'), Campo(10, 'COD_CTA'), ] - + + nivel = 5 class RegistroC485(Registro): """ @@ -893,7 +968,8 @@ class RegistroC485(Registro): Campo(9, 'COD_ITEM'), Campo(10, 'COD_CTA'), ] - + + nivel = 5 class RegistroC489(Registro): """ @@ -904,7 +980,8 @@ class RegistroC489(Registro): Campo(2, 'NUM_PROC', obrigatorio=True), Campo(3, 'IND_PROC', obrigatorio=True), ] - + + nivel = 4 class RegistroC490(Registro): """ @@ -916,7 +993,8 @@ class RegistroC490(Registro): CampoData(3, 'DT_DOC_FIN', obrigatorio=True), Campo(4, 'COD_MOD', obrigatorio=True), ] - + + nivel = 3 class RegistroC491(Registro): """ @@ -935,7 +1013,8 @@ class RegistroC491(Registro): CampoNumerico(10, 'VL_PIS'), Campo(11, 'COD_CTA'), ] - + + nivel = 4 class RegistroC495(Registro): """ @@ -954,7 +1033,8 @@ class RegistroC495(Registro): CampoNumerico(10, 'VL_COFINS'), Campo(11, 'COD_CTA'), ] - + + nivel = 4 class RegistroC499(Registro): """ @@ -965,7 +1045,8 @@ class RegistroC499(Registro): Campo(2, 'NUM_PROC', obrigatorio=True), Campo(3, 'IND_PROC', obrigatorio=True), ] - + + nivel = 4 class RegistroC500(Registro): """ @@ -988,7 +1069,8 @@ class RegistroC500(Registro): CampoNumerico(13, 'VL_PIS'), CampoNumerico(14, 'VL_COFINS'), ] - + + nivel = 3 class RegistroC501(Registro): """ @@ -1004,7 +1086,8 @@ class RegistroC501(Registro): CampoNumerico(7, 'VL_PIS', obrigatorio=True), Campo(8, 'COD_CTA'), ] - + + nivel = 4 class RegistroC505(Registro): """ @@ -1020,7 +1103,8 @@ class RegistroC505(Registro): CampoNumerico(7, 'VL_COFINS', obrigatorio=True), Campo(8, 'COD_CTA'), ] - + + nivel = 4 class RegistroC509(Registro): """ @@ -1031,7 +1115,8 @@ class RegistroC509(Registro): Campo(2, 'NUM_PROC', obrigatorio=True), Campo(3, 'IND_PROC', obrigatorio=True), ] - + + nivel = 4 class RegistroC600(Registro): """ @@ -1063,7 +1148,8 @@ class RegistroC600(Registro): CampoNumerico(21, 'VL_PIS', obrigatorio=True), CampoNumerico(22, 'VL_COFINS', obrigatorio=True), ] - + + nivel = 3 class RegistroC601(Registro): """ @@ -1078,7 +1164,8 @@ class RegistroC601(Registro): CampoNumerico(6, 'VL_PIS', obrigatorio=True), Campo(7, 'COD_CTA'), ] - + + nivel = 4 class RegistroC605(Registro): """ @@ -1093,7 +1180,8 @@ class RegistroC605(Registro): CampoNumerico(6, 'VL_COFINS', obrigatorio=True), Campo(7, 'COD_CTA'), ] - + + nivel = 4 class RegistroC609(Registro): """ @@ -1104,7 +1192,8 @@ class RegistroC609(Registro): Campo(2, 'NUM_PROC', obrigatorio=True), Campo(3, 'IND_PROC', obrigatorio=True), ] - + + nivel = 4 class RegistroC800(Registro): """ @@ -1129,7 +1218,8 @@ class RegistroC800(Registro): CampoNumerico(16, 'VL_PIS_ST'), CampoNumerico(17, 'VL_COFINS_ST'), ] - + + nivel = 3 class RegistroC810(Registro): """ @@ -1150,7 +1240,8 @@ class RegistroC810(Registro): CampoNumerico(12, 'VL_COFINS'), Campo(13, 'COD_CTA'), ] - + + nivel = 4 class RegistroC820(Registro): """ @@ -1172,7 +1263,8 @@ class RegistroC820(Registro): CampoNumerico(12, 'VL_COFINS'), Campo(13, 'COD_CTA'), ] - + + nivel = 4 class RegistroC830(Registro): """ @@ -1183,7 +1275,8 @@ class RegistroC830(Registro): Campo(2, 'NUM_PROC', obrigatorio=True), Campo(3, 'IND_PROC', obrigatorio=True), ] - + + nivel = 4 class RegistroC860(Registro): """ @@ -1197,7 +1290,8 @@ class RegistroC860(Registro): Campo(5, 'DOC_INI'), Campo(6, 'DOC_FIM'), ] - + + nivel = 3 class RegistroC870(Registro): """ @@ -1218,7 +1312,8 @@ class RegistroC870(Registro): CampoNumerico(12, 'VL_COFINS'), Campo(13, 'COD_CTA'), ] - + + nivel = 4 class RegistroC880(Registro): """ @@ -1239,7 +1334,8 @@ class RegistroC880(Registro): CampoNumerico(12, 'VL_COFINS'), Campo(13, 'COD_CTA'), ] - + + nivel = 4 class RegistroC890(Registro): """ @@ -1250,7 +1346,8 @@ class RegistroC890(Registro): Campo(2, 'NUM_PROC', obrigatorio=True), Campo(3, 'IND_PROC', obrigatorio=True), ] - + + nivel = 4 class RegistroC990(Registro): """ @@ -1260,7 +1357,8 @@ class RegistroC990(Registro): CampoFixo(1, 'REG', 'C990'), CampoNumerico(2, 'QTD_LIN_C', obrigatorio=True), ] - + + nivel = 1 class RegistroD001(Registro): """ @@ -1270,7 +1368,8 @@ class RegistroD001(Registro): CampoFixo(1, 'REG', 'D001'), Campo(2, 'IND_MOV', obrigatorio=True), ] - + + nivel = 1 class RegistroD010(Registro): """ @@ -1280,7 +1379,8 @@ class RegistroD010(Registro): CampoFixo(1, 'REG', 'D010'), CampoCNPJ(2, 'CNPJ', obrigatorio=True), ] - + + nivel = 2 class RegistroD100(Registro): """ @@ -1311,7 +1411,8 @@ class RegistroD100(Registro): Campo(22, 'COD_INF'), Campo(23, 'COD_CTA'), ] - + + nivel = 3 class RegistroD101(Registro): """ @@ -1328,7 +1429,8 @@ class RegistroD101(Registro): CampoNumerico(8, 'VL_PIS'), Campo(9, 'COD_CTA'), ] - + + nivel = 4 class RegistroD105(Registro): """ @@ -1345,7 +1447,8 @@ class RegistroD105(Registro): CampoNumerico(8, 'VL_COFINS'), Campo(9, 'COD_CTA'), ] - + + nivel = 4 class RegistroD111(Registro): """ @@ -1356,7 +1459,8 @@ class RegistroD111(Registro): Campo(2, 'NUM_PROC', obrigatorio=True), Campo(3, 'IND_PROC', obrigatorio=True), ] - + + nivel = 4 class RegistroD200(Registro): """ @@ -1375,7 +1479,8 @@ class RegistroD200(Registro): CampoNumerico(10, 'VL_DOC', obrigatorio=True), CampoNumerico(11, 'VL_DESC'), ] - + + nivel = 3 class RegistroD201(Registro): """ @@ -1390,7 +1495,8 @@ class RegistroD201(Registro): CampoNumerico(6, 'VL_PIS'), Campo(7, 'COD_CTA'), ] - + + nivel = 4 class RegistroD205(Registro): """ @@ -1405,7 +1511,8 @@ class RegistroD205(Registro): CampoNumerico(6, 'VL_COFINS'), Campo(7, 'COD_CTA'), ] - + + nivel = 4 class RegistroD209(Registro): """ @@ -1416,7 +1523,8 @@ class RegistroD209(Registro): Campo(2, 'NUM_PROC', obrigatorio=True), Campo(3, 'IND_PROC', obrigatorio=True), ] - + + nivel = 4 class RegistroD300(Registro): """ @@ -1443,7 +1551,8 @@ class RegistroD300(Registro): CampoNumerico(18, 'VL_COFINS'), Campo(19, 'COD_CTA'), ] - + + nivel = 3 class RegistroD309(Registro): """ @@ -1454,7 +1563,8 @@ class RegistroD309(Registro): Campo(2, 'NUM_PROC', obrigatorio=True), Campo(3, 'IND_PROC', obrigatorio=True), ] - + + nivel = 4 class RegistroD350(Registro): """ @@ -1485,7 +1595,8 @@ class RegistroD350(Registro): CampoNumerico(22, 'VL_COFINS'), Campo(23, 'COD_CTA'), ] - + + nivel = 3 class RegistroD359(Registro): """ @@ -1496,7 +1607,8 @@ class RegistroD359(Registro): Campo(2, 'NUM_PROC', obrigatorio=True), Campo(3, 'IND_PROC', obrigatorio=True), ] - + + nivel = 4 class RegistroD500(Registro): """ @@ -1527,7 +1639,8 @@ class RegistroD500(Registro): CampoNumerico(21, 'VL_PIS'), CampoNumerico(22, 'VL_COFINS'), ] - + + nivel = 3 class RegistroD501(Registro): """ @@ -1543,7 +1656,8 @@ class RegistroD501(Registro): CampoNumerico(7, 'VL_PIS'), Campo(8, 'COD_CTA'), ] - + + nivel = 4 class RegistroD505(Registro): """ @@ -1559,7 +1673,8 @@ class RegistroD505(Registro): CampoNumerico(7, 'VL_COFINS'), Campo(8, 'COD_CTA'), ] - + + nivel = 4 class RegistroD509(Registro): """ @@ -1570,7 +1685,8 @@ class RegistroD509(Registro): Campo(2, 'NUM_PROC', obrigatorio=True), Campo(3, 'IND_PROC', obrigatorio=True), ] - + + nivel = 4 class RegistroD600(Registro): """ @@ -1598,7 +1714,8 @@ class RegistroD600(Registro): CampoNumerico(18, 'VL_PIS'), CampoNumerico(19, 'VL_COFINS'), ] - + + nivel = 3 class RegistroD601(Registro): """ @@ -1615,7 +1732,8 @@ class RegistroD601(Registro): CampoNumerico(8, 'VL_PIS'), Campo(9, 'COD_CTA'), ] - + + nivel = 4 class RegistroD605(Registro): """ @@ -1632,7 +1750,8 @@ class RegistroD605(Registro): CampoNumerico(8, 'VL_COFINS'), Campo(9, 'COD_CTA'), ] - + + nivel = 4 class RegistroD609(Registro): """ @@ -1643,7 +1762,8 @@ class RegistroD609(Registro): Campo(2, 'NUM_PROC', obrigatorio=True), Campo(3, 'IND_PROC', obrigatorio=True), ] - + + nivel = 4 class RegistroD990(Registro): """ @@ -1653,7 +1773,8 @@ class RegistroD990(Registro): CampoFixo(1, 'REG', 'D990'), CampoNumerico(2, 'QTD_LIN_D', obrigatorio=True), ] - + + nivel = 1 class RegistroF001(Registro): """ @@ -1663,7 +1784,8 @@ class RegistroF001(Registro): CampoFixo(1, 'REG', 'F001'), Campo(2, 'IND_MOV', obrigatorio=True), ] - + + nivel = 1 class RegistroF010(Registro): """ @@ -1673,7 +1795,8 @@ class RegistroF010(Registro): CampoFixo(1, 'REG', 'F010'), CampoCNPJ(2, 'CNPJ', obrigatorio=True), ] - + + nivel = 2 class RegistroF100(Registro): """ @@ -1700,7 +1823,8 @@ class RegistroF100(Registro): Campo(18, 'COD_CCUS'), Campo(19, 'DESC_DOC_OPER'), ] - + + nivel = 3 class RegistroF111(Registro): """ @@ -1711,7 +1835,8 @@ class RegistroF111(Registro): Campo(2, 'NUM_PROC', obrigatorio=True), Campo(3, 'IND_PROC', obrigatorio=True), ] - + + nivel = 4 class RegistroF120(Registro): """ @@ -1738,7 +1863,8 @@ class RegistroF120(Registro): Campo(17, 'COD_CCUS'), Campo(18, 'DESC_BEM_IMOB'), ] - + + nivel = 3 class RegistroF129(Registro): """ @@ -1749,7 +1875,8 @@ class RegistroF129(Registro): Campo(2, 'NUM_PROC', obrigatorio=True), Campo(3, 'IND_PROC', obrigatorio=True), ] - + + nivel = 4 class RegistroF130(Registro): """ @@ -1778,7 +1905,8 @@ class RegistroF130(Registro): Campo(20, 'COD_CCUS'), Campo(21, 'DESC_BEM_IMOB'), ] - + + nivel = 3 class RegistroF139(Registro): """ @@ -1789,7 +1917,8 @@ class RegistroF139(Registro): Campo(2, 'NUM_PROC', obrigatorio=True), Campo(3, 'IND_PROC', obrigatorio=True), ] - + + nivel = 4 class RegistroF150(Registro): """ @@ -1811,7 +1940,8 @@ class RegistroF150(Registro): Campo(13, 'DESC_EST'), Campo(14, 'COD_CTA'), ] - + + nivel = 3 class RegistroF200(Registro): """ @@ -1841,7 +1971,8 @@ class RegistroF200(Registro): Campo(21, 'IND_NAT_EMP'), Campo(22, 'INF_COMP'), ] - + + nivel = 3 class RegistroF205(Registro): """ @@ -1867,7 +1998,8 @@ class RegistroF205(Registro): CampoNumerico(17, 'VL_CRED_COFINS_DESC', obrigatorio=True), CampoNumerico(18, 'VL_CRED_COFINS_DESC_FUT', obrigatorio=True), ] - + + nivel = 4 class RegistroF210(Registro): """ @@ -1886,7 +2018,8 @@ class RegistroF210(Registro): CampoNumerico(10, 'ALIQ_COFINS'), CampoNumerico(11, 'VL_CRED_COFINS_UTIL'), ] - + + nivel = 4 class RegistroF211(Registro): """ @@ -1897,7 +2030,8 @@ class RegistroF211(Registro): Campo(2, 'NUM_PROC', obrigatorio=True), Campo(3, 'IND_PROC', obrigatorio=True), ] - + + nivel = 4 class RegistroF500(Registro): """ @@ -1922,7 +2056,8 @@ class RegistroF500(Registro): Campo(15, 'COD_CTA'), Campo(16, 'INFO_COMPL'), ] - + + nivel = 3 class RegistroF509(Registro): """ @@ -1933,7 +2068,8 @@ class RegistroF509(Registro): Campo(2, 'NUM_PROC', obrigatorio=True), Campo(3, 'IND_PROC', obrigatorio=True), ] - + + nivel = 4 class RegistroF510(Registro): """ @@ -1958,7 +2094,8 @@ class RegistroF510(Registro): Campo(15, 'COD_CTA'), Campo(16, 'INFO_COMPL'), ] - + + nivel = 3 class RegistroF519(Registro): """ @@ -1969,7 +2106,8 @@ class RegistroF519(Registro): Campo(2, 'NUM_PROC', obrigatorio=True), Campo(3, 'IND_PROC', obrigatorio=True), ] - + + nivel = 4 class RegistroF525(Registro): """ @@ -1988,7 +2126,8 @@ class RegistroF525(Registro): Campo(10, 'INFO_COMPL'), Campo(11, 'COD_CTA'), ] - + + nivel = 3 class RegistroF550(Registro): """ @@ -2013,7 +2152,8 @@ class RegistroF550(Registro): Campo(15, 'COD_CTA'), Campo(16, 'INFO_COMPL'), ] - + + nivel = 3 class RegistroF559(Registro): """ @@ -2024,7 +2164,8 @@ class RegistroF559(Registro): Campo(2, 'NUM_PROC', obrigatorio=True), Campo(3, 'IND_PROC', obrigatorio=True), ] - + + nivel = 4 class RegistroF560(Registro): """ @@ -2050,7 +2191,8 @@ class RegistroF560(Registro): Campo(15, 'COD_CTA'), Campo(16, 'INFO_COMPL'), ] - + + nivel = 3 class RegistroF569(Registro): """ @@ -2061,7 +2203,8 @@ class RegistroF569(Registro): Campo(2, 'NUM_PROC', obrigatorio=True), Campo(3, 'IND_PROC', obrigatorio=True), ] - + + nivel = 4 class RegistroF600(Registro): """ @@ -2080,7 +2223,8 @@ class RegistroF600(Registro): CampoNumerico(10, 'VL_RET_COFINS', obrigatorio=True), Campo(11, 'IND_DEC', obrigatorio=True), ] - + + nivel = 3 class RegistroF700(Registro): """ @@ -2096,7 +2240,8 @@ class RegistroF700(Registro): CampoCNPJ(7, 'CNPJ'), Campo(8, 'INF_COMP'), ] - + + nivel = 3 class RegistroF800(Registro): """ @@ -2113,7 +2258,8 @@ class RegistroF800(Registro): CampoNumerico(8, 'VL_CRED_COFINS', obrigatorio=True), Campo(9, 'PER_CRED_CIS'), ] - + + nivel = 3 class RegistroF990(Registro): """ @@ -2123,7 +2269,8 @@ class RegistroF990(Registro): CampoFixo(1, 'REG', 'F990'), CampoNumerico(2, 'QTD_LIN_F', obrigatorio=True), ] - + + nivel = 1 class RegistroI001(Registro): """ @@ -2133,7 +2280,8 @@ class RegistroI001(Registro): CampoFixo(1, 'REG', 'I001'), Campo(2, 'IND_MOV', obrigatorio=True), ] - + + nivel = 1 class RegistroI010(Registro): """ @@ -2145,7 +2293,8 @@ class RegistroI010(Registro): Campo(3, 'IND_ATIV', obrigatorio=True), Campo(4, 'INFO_COMPL'), ] - + + nivel = 2 class RegistroI100(Registro): """ @@ -2165,7 +2314,8 @@ class RegistroI100(Registro): CampoNumerico(11, 'VL_COFINS', obrigatorio=True), Campo(12, 'INFO_COMPL', obrigatorio=True), ] - + + nivel = 3 class RegistroI199(Registro): """ @@ -2176,7 +2326,8 @@ class RegistroI199(Registro): Campo(2, 'NUM_PROC', obrigatorio=True), Campo(3, 'IND_PROC', obrigatorio=True), ] - + + nivel = 4 class RegistroI200(Registro): """ @@ -2190,7 +2341,8 @@ class RegistroI200(Registro): Campo(5, 'COD_CTA', obrigatorio=True), Campo(6, 'INFO_COMPL', obrigatorio=True), ] - + + nivel = 4 class RegistroI299(Registro): """ @@ -2201,7 +2353,8 @@ class RegistroI299(Registro): Campo(2, 'NUM_PROC', obrigatorio=True), Campo(3, 'IND_PROC', obrigatorio=True), ] - + + nivel = 5 class RegistroI300(Registro): """ @@ -2214,7 +2367,8 @@ class RegistroI300(Registro): Campo(4, 'COD_CTA', obrigatorio=True), Campo(5, 'INFO_COMPL', obrigatorio=True), ] - + + nivel = 5 class RegistroI399(Registro): """ @@ -2225,7 +2379,8 @@ class RegistroI399(Registro): Campo(2, 'NUM_PROC', obrigatorio=True), Campo(3, 'IND_PROC', obrigatorio=True), ] - + + nivel = 6 class RegistroI990(Registro): """ @@ -2235,7 +2390,8 @@ class RegistroI990(Registro): CampoFixo(1, 'REG', 'I990'), CampoNumerico(2, 'QTD_LIN_I', obrigatorio=True), ] - + + nivel = 1 class RegistroM001(Registro): """ @@ -2245,7 +2401,7 @@ class RegistroM001(Registro): CampoFixo(1, 'REG', 'M001'), Campo(2, 'IND_MOV', obrigatorio=True), ] - + nivel = 1 class RegistroM100(Registro): """ @@ -2268,7 +2424,8 @@ class RegistroM100(Registro): CampoNumerico(14, 'VL_CRED_DESC'), Campo(15, 'SLD_CRED', obrigatorio=True), ] - + + nivel = 2 class RegistroM105(Registro): """ @@ -2286,7 +2443,8 @@ class RegistroM105(Registro): Campo(9, 'QUANT_BC_PIS'), Campo(10, 'DESC_CRED'), ] - + + nivel = 3 class RegistroM110(Registro): """ @@ -2301,7 +2459,8 @@ class RegistroM110(Registro): Campo(6, 'DESCR_AJ'), CampoData(7, 'DT_REF'), ] - + + nivel = 3 class RegistroM115(Registro): """ @@ -2318,7 +2477,8 @@ class RegistroM115(Registro): Campo(8, 'COD_CTA'), Campo(9, 'INFO_COMPL'), ] - + + nivel = 4 class RegistroM200(Registro): """ @@ -2339,7 +2499,8 @@ class RegistroM200(Registro): CampoNumerico(12, 'VL_CONT_CUM_REC', obrigatorio=True), CampoNumerico(13, 'VL_TOT_CONT_REC', obrigatorio=True), ] - + + nivel = 2 class RegistroM205(Registro): """ @@ -2351,7 +2512,8 @@ class RegistroM205(Registro): Campo(3, 'COD_REC', obrigatorio=True), CampoNumerico(4, 'VL_DEBITO', obrigatorio=True), ] - + + nivel = 3 class RegistroM210(Registro): """ @@ -2372,7 +2534,8 @@ class RegistroM210(Registro): CampoNumerico(12, 'VL_CONT_DIFER_ANT'), CampoNumerico(13, 'VL_CONT_PER', obrigatorio=True), ] - + + nivel = 3 class RegistroM211(Registro): """ @@ -2386,7 +2549,8 @@ class RegistroM211(Registro): CampoNumerico(5, 'VL_EXC_ESP_COOP'), CampoNumerico(6, 'VL_BC_CONT', obrigatorio=True), ] - + + nivel = 4 class RegistroM220(Registro): """ @@ -2401,7 +2565,8 @@ class RegistroM220(Registro): Campo(6, 'DESCR_AJ'), CampoData(7, 'DT_REF'), ] - + + nivel = 4 class RegistroM225(Registro): """ @@ -2418,7 +2583,8 @@ class RegistroM225(Registro): Campo(8, 'COD_CTA'), Campo(9, 'INFO_COMPL'), ] - + + nivel = 5 class RegistroM230(Registro): """ @@ -2433,7 +2599,8 @@ class RegistroM230(Registro): CampoNumerico(6, 'VL_CRED_DIF'), Campo(7, 'COD_CRED'), ] - + + nivel = 4 class RegistroM300(Registro): """ @@ -2449,7 +2616,8 @@ class RegistroM300(Registro): Campo(7, 'PER_APUR', obrigatorio=True), CampoData(8, 'DT_RECEB'), ] - + + nivel = 2 class RegistroM350(Registro): """ @@ -2463,7 +2631,8 @@ class RegistroM350(Registro): CampoNumerico(5, 'ALIQ_PIS_FOL', obrigatorio=True), CampoNumerico(6, 'VL_TOT_CONT_FOL', obrigatorio=True), ] - + + nivel = 2 class RegistroM400(Registro): """ @@ -2477,7 +2646,8 @@ class RegistroM400(Registro): Campo(4, 'COD_CTA'), Campo(5, 'DESC_COMPL'), ] - + + nivel = 2 class RegistroM410(Registro): """ @@ -2491,7 +2661,8 @@ class RegistroM410(Registro): Campo(4, 'COD_CTA', obrigatorio=True), Campo(5, 'DESC_COMPL', obrigatorio=True), ] - + + nivel = 3 class RegistroM500(Registro): """ @@ -2514,7 +2685,8 @@ class RegistroM500(Registro): CampoNumerico(14, 'VL_CRED_DESC'), Campo(15, 'SLD_CRED', obrigatorio=True), ] - + + nivel = 2 class RegistroM505(Registro): """ @@ -2532,7 +2704,8 @@ class RegistroM505(Registro): Campo(9, 'QUANT_BC_COFINS'), Campo(10, 'DESC_CRED'), ] - + + nivel = 3 class RegistroM510(Registro): """ @@ -2547,7 +2720,8 @@ class RegistroM510(Registro): Campo(6, 'DESCR_AJ'), CampoData(7, 'DT_REF'), ] - + + nivel = 3 class RegistroM515(Registro): """ @@ -2564,7 +2738,8 @@ class RegistroM515(Registro): Campo(8, 'COD_CTA'), Campo(9, 'INFO_COMPL'), ] - + + nivel = 4 class RegistroM600(Registro): """ @@ -2585,7 +2760,8 @@ class RegistroM600(Registro): CampoNumerico(12, 'VL_CONT_CUM_REC', obrigatorio=True), CampoNumerico(13, 'VL_TOT_CONT_REC', obrigatorio=True), ] - + + nivel = 2 class RegistroM605(Registro): """ @@ -2597,7 +2773,8 @@ class RegistroM605(Registro): Campo(3, 'COD_REC', obrigatorio=True), CampoNumerico(4, 'VL_DEBITO', obrigatorio=True), ] - + + nivel = 3 class RegistroM610(Registro): """ @@ -2618,7 +2795,8 @@ class RegistroM610(Registro): CampoNumerico(12, 'VL_CONT_DIFER_ANT'), CampoNumerico(13, 'VL_CONT_PER', obrigatorio=True), ] - + + nivel = 3 class RegistroM611(Registro): """ @@ -2632,7 +2810,8 @@ class RegistroM611(Registro): CampoNumerico(5, 'VL_EXC_ESP_COOP'), CampoNumerico(6, 'VL_BC_CONT', obrigatorio=True), ] - + + nivel = 4 class RegistroM620(Registro): """ @@ -2647,7 +2826,8 @@ class RegistroM620(Registro): Campo(6, 'DESCR_AJ'), CampoData(7, 'DT_REF'), ] - + + nivel = 4 class RegistroM625(Registro): """ @@ -2664,7 +2844,8 @@ class RegistroM625(Registro): Campo(8, 'COD_CTA'), Campo(9, 'INFO_COMPL'), ] - + + nivel = 5 class RegistroM630(Registro): """ @@ -2679,7 +2860,8 @@ class RegistroM630(Registro): CampoNumerico(6, 'VL_CRED_DIF'), Campo(7, 'COD_CRED'), ] - + + nivel = 4 class RegistroM700(Registro): """ @@ -2695,7 +2877,8 @@ class RegistroM700(Registro): Campo(7, 'PER_APUR', obrigatorio=True), CampoData(8, 'DT_RECEB'), ] - + + nivel = 2 class RegistroM800(Registro): """ @@ -2709,7 +2892,8 @@ class RegistroM800(Registro): Campo(4, 'COD_CTA'), Campo(5, 'DESC_COMPL'), ] - + + nivel = 2 class RegistroM810(Registro): """ @@ -2723,7 +2907,8 @@ class RegistroM810(Registro): Campo(4, 'COD_CTA'), Campo(5, 'DESC_COMPL'), ] - + + nivel = 3 class RegistroM990(Registro): """ @@ -2733,7 +2918,8 @@ class RegistroM990(Registro): CampoFixo(1, 'REG', 'M990'), CampoNumerico(2, 'QTD_LIN_M', obrigatorio=True), ] - + + nivel = 1 class RegistroP001(Registro): """ @@ -2743,7 +2929,8 @@ class RegistroP001(Registro): CampoFixo(1, 'REG', 'P001'), Campo(2, 'IND_MOV', obrigatorio=True), ] - + + nivel = 1 class RegistroP010(Registro): """ @@ -2753,7 +2940,8 @@ class RegistroP010(Registro): CampoFixo(1, 'REG', 'P010'), CampoCNPJ(2, 'CNPJ', obrigatorio=True), ] - + + nivel = 2 class RegistroP100(Registro): """ @@ -2773,7 +2961,8 @@ class RegistroP100(Registro): Campo(11, 'COD_CTA'), Campo(12, 'INFO_COMPL'), ] - + + nivel = 3 class RegistroP110(Registro): """ @@ -2786,7 +2975,8 @@ class RegistroP110(Registro): Campo(4, 'DET_VALOR', obrigatorio=True), Campo(5, 'INF_COMPL'), ] - + + nivel = 4 class RegistroP199(Registro): """ @@ -2797,7 +2987,8 @@ class RegistroP199(Registro): Campo(2, 'NUM_PROC', obrigatorio=True), Campo(3, 'IND_PROC', obrigatorio=True), ] - + + nivel = 4 class RegistroP200(Registro): """ @@ -2812,7 +3003,8 @@ class RegistroP200(Registro): CampoNumerico(6, 'VL_TOT_CONT_DEV', obrigatorio=True), Campo(7, 'COD_REC', obrigatorio=True), ] - + + nivel = 2 class RegistroP210(Registro): """ @@ -2827,7 +3019,8 @@ class RegistroP210(Registro): Campo(6, 'DESCR_AJ'), CampoData(7, 'DT_REF'), ] - + + nivel = 3 class RegistroP990(Registro): """ @@ -2837,7 +3030,8 @@ class RegistroP990(Registro): CampoFixo(1, 'REG', 'P990'), CampoNumerico(2, 'QTD_LIN_P', obrigatorio=True), ] - + + nivel = 1 class Registro1001(Registro): """ @@ -2847,7 +3041,8 @@ class Registro1001(Registro): CampoFixo(1, 'REG', '1001'), Campo(2, 'IND_MOV', obrigatorio=True), ] - + + nivel = 1 class Registro1010(Registro): """ @@ -2862,7 +3057,43 @@ class Registro1010(Registro): Campo(6, 'DESC_DEC_JUD'), CampoData(7, 'DT_SENT_JUD'), ] + + nivel = 2 +class Registro1011(Registro): + """ + Apuração de Crédito Extemporâneo - Documentos e Operações de Períodos Anteriores – PIS/PASEP + """ + campos = [ + CampoFixo(1, 'REG', '1011'), + Campo(2, 'REG_REF'), + Campo(3, 'CHAVE_DOC'), + Campo(4, 'COD_PART'), + Campo(5, 'COD_ITEM'), + CampoData(6, 'DT_OPER', obrigatorio=True), + CampoNumerico(7, 'VL_OPER', obrigatorio=True), + Campo(8, 'CST_PIS', obrigatorio=True), + CampoNumerico(9, 'VL_BC_PIS'), + CampoNumerico(10, 'ALIQ_PIS'), + CampoNumerico(11, 'VL_PIS'), + Campo(12, 'CST_COFINS', obrigatorio=True), + CampoNumerico(13, 'VL_BC_COFINS'), + CampoNumerico(14, 'ALIQ_COFINS'), + CampoNumerico(15, 'VL_COFINS'), + Campo(16, 'CST_PIS_SUSP', obrigatorio=True), + CampoNumerico(17, 'VL_BC_PIS_SUSP'), + CampoNumerico(18, 'ALIQ_PIS_SUSP'), + CampoNumerico(19, 'VL_PIS_SUSP'), + Campo(20, 'CST_COFINS_SUSP', obrigatorio=True), + CampoNumerico(21, 'VL_BC_COFINS_SUSP'), + CampoNumerico(22, 'ALIQ_COFINS_SUSP'), + CampoNumerico(23, 'VL_COFINS_SUSP'), + Campo(24, 'COD_CTA'), + Campo(25, 'COD_CCUS'), + Campo(26, 'DESC_DOC_OPER'), + ] + + nivel = 3 class Registro1020(Registro): """ @@ -2874,7 +3105,8 @@ class Registro1020(Registro): Campo(3, 'IND_NAT_ACAO', obrigatorio=True), CampoData(4, 'DT_DEC_ADM', obrigatorio=True), ] - + + nivel = 2 class Registro1100(Registro): """ @@ -2900,7 +3132,8 @@ class Registro1100(Registro): CampoNumerico(17, 'VL_CRED_OUT'), Campo(18, 'SLD_CRED_FIM'), ] - + + nivel = 2 class Registro1101(Registro): """ @@ -2930,7 +3163,8 @@ class Registro1101(Registro): Campo(21, 'PER_ESCRIT'), CampoCNPJ(22, 'CNPJ', obrigatorio=True), ] - + + nivel = 3 class Registro1102(Registro): """ @@ -2942,7 +3176,8 @@ class Registro1102(Registro): CampoNumerico(3, 'VL_CRED_PIS_NT_MI'), CampoNumerico(4, 'VL_CRED_PIS_EXP'), ] - + + nivel = 4 class Registro1200(Registro): """ @@ -2961,7 +3196,8 @@ class Registro1200(Registro): CampoNumerico(10, 'VL_JUR'), CampoData(11, 'DT_RECOL'), ] - + + nivel = 2 class Registro1210(Registro): """ @@ -2980,7 +3216,8 @@ class Registro1210(Registro): Campo(10, 'COD_CTA'), Campo(11, 'DESC_COMPL'), ] - + + nivel = 3 class Registro1220(Registro): """ @@ -2993,7 +3230,8 @@ class Registro1220(Registro): Campo(4, 'COD_CRED', obrigatorio=True), CampoNumerico(5, 'VL_CRED', obrigatorio=True), ] - + + nivel = 3 class Registro1300(Registro): """ @@ -3009,7 +3247,8 @@ class Registro1300(Registro): CampoNumerico(7, 'VL_RET_DCOMP', obrigatorio=True), Campo(8, 'SLD_RET', obrigatorio=True), ] - + + nivel = 2 class Registro1500(Registro): """ @@ -3035,7 +3274,8 @@ class Registro1500(Registro): CampoNumerico(17, 'VL_CRED_OUT'), Campo(18, 'SLD_CRED_FIM', obrigatorio=True), ] - + + nivel = 2 class Registro1501(Registro): """ @@ -3065,7 +3305,8 @@ class Registro1501(Registro): Campo(21, 'PER_ESCRIT'), CampoCNPJ(22, 'CNPJ', obrigatorio=True), ] - + + nivel = 3 class Registro1502(Registro): """ @@ -3077,7 +3318,8 @@ class Registro1502(Registro): CampoNumerico(3, 'VL_CRED_COFINS_NT_MI'), CampoNumerico(4, 'VL_CRED_COFINS_EXP'), ] - + + nivel = 4 class Registro1600(Registro): """ @@ -3096,7 +3338,8 @@ class Registro1600(Registro): CampoNumerico(10, 'VL_JUR'), CampoData(11, 'DT_RECOL'), ] - + + nivel = 2 class Registro1610(Registro): """ @@ -3115,7 +3358,8 @@ class Registro1610(Registro): Campo(10, 'COD_CTA'), Campo(11, 'DESC_COMPL'), ] - + + nivel = 3 class Registro1620(Registro): """ @@ -3128,7 +3372,8 @@ class Registro1620(Registro): Campo(4, 'COD_CRED', obrigatorio=True), CampoNumerico(5, 'VL_CRED', obrigatorio=True), ] - + + nivel = 3 class Registro1700(Registro): """ @@ -3144,7 +3389,8 @@ class Registro1700(Registro): CampoNumerico(7, 'VL_RET_DCOMP', obrigatorio=True), Campo(8, 'SLD_RET', obrigatorio=True), ] - + + nivel = 2 class Registro1800(Registro): """ @@ -3161,7 +3407,8 @@ class Registro1800(Registro): CampoData(8, 'DT_REC_UNI'), Campo(9, 'COD_REC'), ] - + + nivel = 2 class Registro1809(Registro): """ @@ -3172,7 +3419,8 @@ class Registro1809(Registro): Campo(2, 'NUM_PROC', obrigatorio=True), Campo(3, 'IND_PROC', obrigatorio=True), ] - + + nivel = 3 class Registro1900(Registro): """ @@ -3194,7 +3442,8 @@ class Registro1900(Registro): Campo(12, 'INF_COMPL'), Campo(13, 'COD_CTA'), ] - + + nivel = 2 class Registro1990(Registro): """ @@ -3204,7 +3453,8 @@ class Registro1990(Registro): CampoFixo(1, 'REG', '1990'), CampoNumerico(2, 'QTD_LIN_1', obrigatorio=True), ] - + + nivel = 1 class Registro9001(Registro): """ @@ -3214,7 +3464,8 @@ class Registro9001(Registro): CampoFixo(1, 'REG', '9001'), Campo(2, 'IND_MOV', obrigatorio=True), ] - + + nivel = 1 class Registro9900(Registro): """ @@ -3225,7 +3476,8 @@ class Registro9900(Registro): Campo(2, 'REG_BLC', obrigatorio=True), CampoNumerico(3, 'QTD_REG_BLC', obrigatorio=True), ] - + + nivel = 2 class Registro9990(Registro): """ @@ -3235,7 +3487,8 @@ class Registro9990(Registro): CampoFixo(1, 'REG', '9990'), CampoNumerico(2, 'QTD_LIN_9', obrigatorio=True), ] - + + nivel = 1 class Registro9999(Registro): """ @@ -3245,3 +3498,5 @@ class Registro9999(Registro): CampoFixo(1, 'REG', '9999'), CampoNumerico(2, 'QTD_LIN', obrigatorio=True), ] + nivel = 0 + diff --git a/sped/registros.py b/sped/registros.py index 8a60e45..bcf47bc 100644 --- a/sped/registros.py +++ b/sped/registros.py @@ -6,7 +6,7 @@ from .erros import CampoError from .erros import CampoInexistenteError from .erros import RegistroError - +import itertools class Registro(object): """ @@ -71,16 +71,23 @@ def __init__(self, line=None): for c in self.campos: if isinstance(c, CampoFixo): self._valores[c.indice] = c.valor + self._numero_da_linha = None else: - self._valores = line.split('|') + #self._valores = line.split('|') + self._valores = [valor.strip() for valor in line.split('|')] for c in self.campos: if isinstance(c, CampoFixo): if self._valores[c.indice] != c.valor: raise CampoError(self, c.nome) + # Inicializar contador na leitura do registro de abertura '0000' + if self._valores[1] == '0000': + __class__.contador_de_linhas = itertools.count(1) + # Informação do número da linha do arquivo sped + self._numero_da_linha = next(__class__.contador_de_linhas) @property - def campos(self): - return self.__class__.campos + def numero_da_linha(self): + return self._numero_da_linha @property def valores(self): @@ -122,7 +129,6 @@ def __str__(self): def __repr__(self): return '<%s.%s>' % (self.__class__.__module__, self.__class__.__name__) - class RegistroIndefinido(Registro): def __init__(self): super(RegistroIndefinido, self).__init__() diff --git a/sped/relatorios/__init__.py b/sped/relatorios/__init__.py new file mode 100644 index 0000000..40a96af --- /dev/null +++ b/sped/relatorios/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/sped/relatorios/efd_contribuicoes.py b/sped/relatorios/efd_contribuicoes.py new file mode 100755 index 0000000..b4cc32f --- /dev/null +++ b/sped/relatorios/efd_contribuicoes.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python3 + +Autor = 'Claudio Fernandes de Souza Rodrigues (claudiofsr@yahoo.com)' +Data = '07 de Fevereiro de 2020 (início: 29 de Janeiro de 2020)' + +# Fonte: https://github.com/Trust-Code/python-sped + +# Instruções (no Linux): + +# Digite em seu web brawser o endereço abaixo: +# https://github.com/claudiofsr/python-sped +# Clique em 'Clone or download'. +# Em seguida, descompacte o arquivo copiado: +# > unzip python-sped-master.zip +# > cd python-sped-master +# Para instalar o módulo do sped em seu sistema execute, como superusuário: +# python setup.py install +# Copie o arquivo 'efd_contribuicoes.py' de python-sped-master/relatorios +# para o diretório que contenha os arquivos de EFD Contribuições. +# Em seguida, execute no terminal o camando: +# > python efd_contribuicoes.py + +import sys, os +from time import time, sleep +from sped.relatorios.efd_read_dir import ReadFiles, Total_Execution_Time +from sped.relatorios.efd_print_info import EFD_Contrib_Info + +# Versão mínima exigida: python 3.6.0 +python_version = sys.version_info +if python_version < (3,6,0): + print('versão mínima exigida do python é 3.6.0') + print('versão atual', "%s.%s.%s" % (python_version[0],python_version[1],python_version[2])) + exit() + +if __name__ == '__main__': + + dir_path = os.getcwd() # CurrentDirectory + extensao = 'txt' + + lista_de_arquivos = ReadFiles(root_path = dir_path, extension = extensao) + + arquivos_efd_contrib = list(lista_de_arquivos.find_all_efd_contrib) # SPED EFD Contrib + + for index,file_path in enumerate(arquivos_efd_contrib,1): + print( f"{index:>6}: {file_path}") + for attribute, value in lista_de_arquivos.get_file_info(file_path).items(): + print(f'{attribute:>25}: {value}') + + indice_do_arquivo = None + + if len(arquivos_efd_contrib) > 1: + while indice_do_arquivo is None: + my_input = input(f"\nFavor, digite o número do arquivo da EFD Contribuições (1 a {len(arquivos_efd_contrib)}): ") + try: + my_input = int(my_input) + if 1 <= my_input <= len(arquivos_efd_contrib): + indice_do_arquivo = my_input - 1 + except: + print(f"-->Opção incorreta: '{my_input}'.") + print(f"-->Digite um número inteiro entre 1 e {len(arquivos_efd_contrib)}.") + elif len(arquivos_efd_contrib) == 1: + indice_do_arquivo = 0 + else: + dir_path_exemplo = '/home/claudio/Documentos/' + print(f"\nA lista de arquivos de SPED Contribuições é obtida a partir de:\n") + print(f"\tlista_de_arquivos = ReadFiles(root_path = dir_path, extension = extensao).\n") + print(f"tal que:\n") + print(f"\tdir_path = '{dir_path}' e extensao = '{extensao}'.\n") + print(f"Nenhum arquivo de EFD Contribuções foi encontrado no diretório definido acima.") + print(f"Se as EFDs estão localizadas, por exemplo, no diretório '{dir_path_exemplo}',") + print(f"então altere a variável 'dir_path' para o diretório que contenha as EFDs:") + print(f"\n\tdir_path = '{dir_path_exemplo}'\n") + print(f"Outra alternativa é copiar este arquivo '{__file__}' para o diretório que contenha as EFDs.") + print(f"Em seguida, executar no terminal:\n") + print(f"\t python {__file__} \n") + exit() + + # arquivo EFD Contribuições + file_path = arquivos_efd_contrib[indice_do_arquivo] + codif = lista_de_arquivos.informations[file_path]['codificação'] + + print(f"\nFoi selecionado o arquivo {indice_do_arquivo + 1}: '{file_path}'\n") + input("Tecle Enter para gerar arquivo .csv com informações da EFD ") + print() + + start = time() + + efd = EFD_Contrib_Info(file_path, encoding=codif, verbose=False) + + efd.imprimir_informacoes + + end = time() + print(f'\nTotal Execution Time: {Total_Execution_Time(start,end)} \n') + diff --git a/sped/relatorios/efd_print_info.py b/sped/relatorios/efd_print_info.py new file mode 100755 index 0000000..f23db0d --- /dev/null +++ b/sped/relatorios/efd_print_info.py @@ -0,0 +1,735 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +Autor = 'Claudio Fernandes de Souza Rodrigues (claudiofsr@yahoo.com)' +Data = '06 de Fevereiro de 2020 (início: 10 de Janeiro de 2020)' + +import os, csv, re, sys, itertools +from datetime import datetime +from time import time, sleep +from sped.efd.pis_cofins.arquivos import ArquivoDigital +from sped.relatorios.efd_tabelas import EFD_Tabelas +from sped.campos import CampoCNPJ, CampoCPF, CampoCPFouCNPJ, CampoChaveEletronica + +# Versão mínima exigida: python 3.6.0 +python_version = sys.version_info +if python_version < (3,6,0): + print('versão mínima exigida do python é 3.6.0') + print('versão atual', "%s.%s.%s" % (python_version[0],python_version[1],python_version[2])) + exit() + +# Python OOP: Atributos e Métodos (def, funções) +class EFD_Contrib_Info(ArquivoDigital): + """ + Imprimir informações da SPED EFD Contribuições em um arquivo.csv + tal que em uma linha contenha todas as informações suficientes para + verificar a correção dos lançamentos das contribuições de PIS/COFINS. + """ + + # class or static variable + + # Python 3 Deep Dive (Part 4 - OOP)/03. Project 1/03. Project Solution - Transaction Numbers + contador_de_linhas = itertools.count(2) # 2 é o valor inicial do contador + + ### --- registros e colunas --- ### + + registros_de_data_emissao = ['DT_DOC', 'DT_DOC_INI', 'DT_REF_INI', 'DT_OPER'] # 'Data da Emissão do Documento Fiscal' + + registros_de_data_execucao = ['DT_EXE_SERV', 'DT_E_S', 'DT_ENT', 'DT_A_P', 'DT_DOC_FIN', 'DT_REF_FIN'] # 'Data da Entrada/Aquisição/Execução ou da Saída/Prestação/Conclusão' + + registros_de_data = ['DT_INI', 'DT_FIN'] + registros_de_data_emissao + registros_de_data_execucao # merge/concatenating two lists in Python + + registros_de_identificacao_do_item = ['DESCR_ITEM', 'TIPO_ITEM', 'COD_NCM'] + + registros_de_cadastro_do_participante = ['NOME_participante', 'CNPJ_participante', 'CPF_participante'] + + registros_de_plano_de_contas = ['COD_NAT_CC', 'NOME_CTA'] + + registros_de_codigo_cst = ['CST_PIS', 'CST_COFINS'] + + registros_de_chave_eletronica = ['CHV_NFE', 'CHV_CTE', 'CHV_NFSE', 'CHV_DOCe', 'CHV_CFE', 'CHV_NFE_CTE'] + + registros_de_base_de_calculo = ['VL_BC_PIS', 'VL_BC_COFINS'] + + registros_de_valor = ['VL_DOC', 'VL_BRT', 'VL_OPER', 'VL_OPR', 'VL_OPER_DEP', 'VL_BC_CRED', 'VL_BC_EST', 'VL_TOT_REC', 'VL_REC_CAIXA', 'VL_REC_COMP', 'VL_REC', 'VL_ITEM'] # adicionado 'VL_OPR' para EFD ICMS_IPI + + registros_totais = [*registros_de_data, *registros_de_identificacao_do_item, *registros_de_plano_de_contas, *registros_de_codigo_cst, *registros_de_chave_eletronica, *registros_de_base_de_calculo, *registros_de_valor] + + # Imprimir as informações desta coluna, nesta ordem + colunas = ['Linhas', 'Arquivo da SPED EFD', 'Nº da Linha da EFD', 'CNPJ', 'NOME', 'Mês do Período de Apuração', 'Ano do Período de Apuração', 'Tipo de Operação', 'IND_ORIG_CRED', 'REG', 'CST Código da Situação Tributária', + 'NAT_BC_CRED', 'CFOP', 'COD_PART', *registros_de_cadastro_do_participante, 'CNPJ_CPF_PART', 'Data de Emissão', 'Data de Execução', 'COD_ITEM', *registros_de_identificacao_do_item, + 'Chave Eletrônica', 'COD_MOD', 'NUM_DOC', 'NUM_ITEM', 'COD_CTA', *registros_de_plano_de_contas, 'Valor do Item', 'Valor da Base de Cálculo', 'ALIQ_PIS', 'ALIQ_COFINS'] + + # initialize the attributes of the class + + def __init__(self, file_path=None, encoding=None, verbose=False): + + if file_path is None or not os.path.isfile(file_path): + raise ValueError(f'O arquivo file_path = {file_path} não é válido!') + else: + self.file_path = file_path + + if encoding is None: + self.encoding = 'UTF-8' + else: + self.encoding = encoding + + if not isinstance(verbose, bool): + raise ValueError(f'verbose deve ser uma variável boolean (True or False). verbose = {verbose} é inválido!') + else: + self.verbose = verbose + + self.basename = os.path.basename(self.file_path) + + @property + def imprimir_informacoes(self): + + self.objeto_sped = ArquivoDigital() # instanciar objeto sped_efd + self.objeto_sped.readfile(self.file_path, codificacao=self.encoding, verbose=self.verbose) + + self.info_do_participante = self.cadastro_do_participante(self.objeto_sped) + + if self.verbose: + print(f'self.info_do_participante = {self.info_do_participante} ; len(self.info_do_participante) = {len(self.info_do_participante)}\n') + + self.info_do_item = self.identificacao_do_item(self.objeto_sped) + + if self.verbose: + print(f'self.info_do_item = {self.info_do_item} ; len(self.info_do_item) = {len(self.info_do_item)}\n') + + self.info_da_conta = self.plano_de_contas_contabeis(self.objeto_sped) + + if self.verbose: + print(f'self.info_da_conta = {self.info_da_conta} ; len(self.info_da_conta) = {len(self.info_da_conta)}\n') + + self.info_de_abertura = self.obter_info_de_abertura(self.objeto_sped) + + filename, file_extension = os.path.splitext(self.file_path) + arquivo_csv = filename + '.csv' + + # Função desabilitada + # Funçao utilizada apenas para teste + # self.imprimir_informacoes_linha_a_linha(self.objeto_sped, output_filename=arquivo_csv) + + self.imprimir_informacoes_da_efd(self.objeto_sped, output_filename=arquivo_csv) + + def __repr__(self): + return f'{self.__class__.__name__}(file_path = {self.file_path!r}, encoding = {self.encoding!r}, verbose = {self.verbose!r})' + + # https://radek.io/2011/07/21/static-variables-and-methods-in-python/ + @staticmethod + def natureza_da_bc_dos_creditos(cfop): + """ + http://sped.rfb.gov.br/arquivo/show/1681 + Tabela CFOP - Operações Geradoras de Créditos - Versão 1.0.0 + """ + natureza_bc = None + + # Código 01 - CFOP de 'Aquisição de Bens para Revenda' + if cfop in ['1102','1113','1117','1118','1121','1251','1403','1652','2102','2113','2117','2118','2121','2251','2403','2652','3102','3251','3652']: + natureza_bc = 1 + # Código 02 - CFOP de 'Aquisição de Bens Utilizados como Insumo' + elif cfop in ['1101','1111','1116','1120','1122','1126','1128','1401','1407','1556','1651','1653','2101','2111','2116','2120','2122','2126','2128','2401','2407','2556','2651','2653','3101','3126','3128','3556','3651','3653']: + natureza_bc = 2 + # Código 03 - CFOP de 'Aquisição de Serviços Utilizados como Insumos' + elif cfop in ['1124','1125','1933','2124','2125','2933']: + natureza_bc = 3 + # Código 12 - CFOP de 'Devolução de Vendas Sujeitas à Incidência Não-Cumulativa' + elif cfop in ['1201','1202','1203','1204','1410','1411','1660','1661','1662','2201','2202','2410','2411','2660','2661','2662']: + natureza_bc = 12 + # Código 13 - CFOP de 'Outras Operações com Direito a Crédito' + elif cfop in ['1922','2922']: + natureza_bc = 13 + + return natureza_bc + + def identidade(chave): + return chave + + def formatar_linhas(numero): + return f'{int(numero):09d}' + + def formatar_data(data_in): + dt = datetime.strptime(data_in, "%d%m%Y") # ddmmaaaa + #data_out = dt.isoformat('T') + #data_out = dt.strftime('%x %X') # excel date format + data_out = dt.strftime("%d/%m/%Y") + return data_out + + def formatar_chave(chave): + chave_copia = chave + if len(chave) == 44: + chave_copia = "%s.%s.%s.%s.%s.%s.%s.%s-%s" % (chave[0:2],chave[2:6],chave[6:20],chave[20:22],chave[22:25],chave[25:34],chave[34:35],chave[35:43],chave[43:44]) + if len(chave) >= 1 and not CampoChaveEletronica.validar(chave): + chave_copia = chave_copia + ' : o dígito verificador da chave é inválido!' + return chave_copia + + def formatar_ncm(ncm): + if len(ncm) == 8: + ncm = "%s.%s.%s" % (ncm[0:4],ncm[4:6],ncm[6:8]) + return ncm + + def formatar_cnpj(cnpj): + cnpj_copia = cnpj + if len(cnpj) == 14: + cnpj_copia = "%s.%s.%s/%s-%s" % (cnpj[0:2],cnpj[2:5],cnpj[5:8],cnpj[8:12],cnpj[12:14]) + if len(cnpj) >= 1 and not CampoCNPJ.validar(cnpj): + cnpj_copia = cnpj_copia + ' : o dígito verificador do cnpj é inválido!' + return cnpj_copia + + def formatar_cpf(cpf): + cpf_copia = cpf + if len(cpf) == 11: + cpf_copia = "%s.%s.%s-%s" % (cpf[0:3],cpf[3:6],cpf[6:9],cpf[9:11]) + if len(cpf) >= 1 and not CampoCPF.validar(cpf): + cpf_copia = cpf_copia + ' : o dígito verificador do cpf é inválido!' + return cpf_copia + + def formatar_cnpj_cpf(pjpf): + if len(pjpf) == 11: + pjpf = "CPF %s.%s.%s-%s" % (pjpf[0:3],pjpf[3:6],pjpf[6:9],pjpf[9:11]) + elif len(pjpf) == 14: + pjpf = "CNPJ %s.%s.%s/%s-%s" % (pjpf[0:2],pjpf[2:5],pjpf[5:8],pjpf[8:12],pjpf[12:14]) + return pjpf + + def formatar_cst(codigo_cst): + try: + codigo_cst = f'{int(codigo_cst):02d}' + return f'{codigo_cst} - {EFD_Tabelas.tabela_cst[codigo_cst]}' + except: + return codigo_cst + + def formatar_nbc(natureza_bc): + try: + natureza_bc = f'{int(natureza_bc):02d}' + return f'{natureza_bc} - {EFD_Tabelas.tabela_bc_do_credito[natureza_bc]}' + except: + return natureza_bc + + def formatar_tipo(tipo_do_item): + try: + tipo_do_item = f'{int(tipo_do_item):02d}' + return f'{tipo_do_item} - {EFD_Tabelas.tabela_tipo_do_item[tipo_do_item]}' + except: + return tipo_do_item + + def formatar_mod(doc_fiscal): + try: + return f'{doc_fiscal} - {EFD_Tabelas.tabela_modelos_documentos_fiscais[doc_fiscal]}' + except: + return doc_fiscal + + # dict[key] = funtion_key(value) ; Definido apenas uma vez ; + # Ao escolher uma chave, o dicionário aplica uma função (que depende da chave) sobre o valor ; Ordem O(1) + # Caso fossem utilizados vários if/then/else (ou select) a Ordem seria O(N), que é mais lento! + myDict = {} + for col in sorted(set(registros_totais + colunas)): + + match_linha = re.search('^Linhas', col, flags=re.IGNORECASE) + match_data = re.search('^DT_|Data', col, flags=re.IGNORECASE) + match_chave = re.search('^CHV_|Chave Eletrônica', col, flags=re.IGNORECASE) + match_ncm = re.search('COD_NCM', col, flags=re.IGNORECASE) + match_cnpj = re.search('CNPJ', col, flags=re.IGNORECASE) + match_cpf = re.search('CPF', col, flags=re.IGNORECASE) + match_cst = re.search('^CST|CST Código da Situação Tributária', col, flags=re.IGNORECASE) + match_nbc = re.search('NAT_BC_CRED', col, flags=re.IGNORECASE) + match_tipo = re.search('TIPO_ITEM', col, flags=re.IGNORECASE) + match_mod = re.search('COD_MOD', col, flags=re.IGNORECASE) + + myDict[col] = identidade + + # Estes vários if/elif/elif/... são executados apenas uma vez na definição da classe. + if match_linha: + myDict[col] = formatar_linhas + elif match_data: + myDict[col] = formatar_data + elif match_chave: + myDict[col] = formatar_chave + elif match_ncm: + myDict[col] = formatar_ncm + elif match_cst: + myDict[col] = formatar_cst + elif match_nbc: + myDict[col] = formatar_nbc + elif match_tipo: + myDict[col] = formatar_tipo + elif match_mod: + myDict[col] = formatar_mod + + if match_cnpj and match_cpf: + myDict[col] = formatar_cnpj_cpf + elif match_cnpj: + myDict[col] = formatar_cnpj + elif match_cpf: + myDict[col] = formatar_cpf + + if False: + for idx, key in enumerate(myDict.keys(),1): + print(f'{key:>40}: [{idx:>2}] {myDict[key]}') + + @staticmethod + def formatar_valor(nome,val): + ''' + Evitar n repetições de 'if condicao_j then A_j else B_j' tal que 1 <= j <= n, usar dicionário: myDict[key] = funtion_key(value) + Better optimization technique using if/else or dictionary + A series of if/else statement which receives the 'string' returns the appropriate function for it. (Around 40-50 if/else statements). + A dictionary maintaining the key-value pair. key as strings, and values as the function objects, and one main function to search and return the function object. + ''' + # https://stackoverflow.com/questions/11445226/better-optimization-technique-using-if-else-or-dictionary + # https://softwareengineering.stackexchange.com/questions/182093/why-store-a-function-inside-a-python-dictionary/182095 + # https://stackoverflow.com/questions/9168340/using-a-dictionary-to-select-function-to-execute + try: + val_formated = __class__.myDict[nome](val) + except: + val_formated = val + #print(f'nome = {nome} ; val = {val} ; val_formated = {val_formated}') + return val_formated + + def cadastro_do_participante(self,sped_efd): + """ + Registro 0150: Tabela de Cadastro do Participante + Retorno desta função: + info_do_participante[codigo_do_participante][campo] = descricao + """ + blocoZero = sped_efd._blocos['0'] # Ler apenas o bloco 0. + info = {} + for registro in blocoZero.registros: + REG = registro.valores[1] + if REG != '0150': + continue + codigo_do_participante = None + for campo in registro.campos: + valor = registro.valores[campo.indice] + # Fazer distinção entre 'NOME' do Registro0000 e 'NOME' do Registro0150 + nome = campo.nome + '_participante' + if campo.nome == 'COD_PART': + codigo_do_participante = valor + info[codigo_do_participante] = {} + if nome in __class__.registros_de_cadastro_do_participante and codigo_do_participante is not None: + info[codigo_do_participante][nome] = valor + return info + + def identificacao_do_item(self,sped_efd): + """ + Registro 0200: Tabela de Identificação do Item (Produtos e Serviços) + Retorno desta função: + info_do_item[codigo_do_item][campo] = descricao + """ + blocoZero = sped_efd._blocos['0'] # Ler apenas o bloco 0. + info = {} + for registro in blocoZero.registros: + REG = registro.valores[1] + if REG != '0200': + continue + codigo_do_item = None + for campo in registro.campos: + valor = registro.valores[campo.indice] + if campo.nome == 'COD_ITEM': + codigo_do_item = valor + info[codigo_do_item] = {} + if campo.nome in __class__.registros_de_identificacao_do_item and codigo_do_item is not None: + info[codigo_do_item][campo.nome] = valor + return info + + def plano_de_contas_contabeis(self,sped_efd): + """ + Registro 0500: Plano de Contas Contábeis + Retorno desta função: + info_do_item[codigo_do_item][campo] = descricao + """ + blocoZero = sped_efd._blocos['0'] # Ler apenas o bloco 0. + info = {} + for registro in blocoZero.registros: + REG = registro.valores[1] + if REG != '0500': + continue + codigo_do_item = None + for campo in registro.campos: + valor = registro.valores[campo.indice] + if campo.nome == 'COD_CTA': + codigo_do_item = valor + info[codigo_do_item] = {} + for campo in registro.campos: + valor = registro.valores[campo.indice] + if campo.nome in __class__.registros_de_plano_de_contas and codigo_do_item is not None: + info[codigo_do_item][campo.nome] = valor + return info + + + def obter_info_de_abertura(self,sped_efd): + registro = sped_efd._registro_abertura + REG = registro.valores[1] + nivel = registro.nivel + codigo_cst = '' + valor_item = '' + valor_base = '' + + info_de_abertura = {} + + # https://www.geeksforgeeks.org/python-creating-multidimensional-dictionary/ + info_de_abertura.setdefault(nivel, {}).setdefault(codigo_cst, {}).setdefault(valor_item, {}).setdefault(valor_base, {})['Nível Hierárquico'] = nivel + + if self.verbose: + print(f'registro.as_line() = {registro.as_line()} ; REG = {REG} ; nivel = {nivel}') + print(f'info_de_abertura = {info_de_abertura}') + + for campo in registro.campos: + + valor = registro.valores[campo.indice] + + if campo.nome in __class__.colunas: + info_de_abertura[nivel][codigo_cst][valor_item][valor_base][campo.nome] = valor + if campo.nome == 'DT_INI': + ddmmaaaa = registro.valores[campo.indice] + info_de_abertura[nivel][codigo_cst][valor_item][valor_base]['Data de Emissão'] = valor + info_de_abertura[nivel][codigo_cst][valor_item][valor_base]['Mês do Período de Apuração'] = ddmmaaaa[2:4] + info_de_abertura[nivel][codigo_cst][valor_item][valor_base]['Ano do Período de Apuração'] = ddmmaaaa[4:8] + if campo.nome == 'DT_FIN': + info_de_abertura[nivel][codigo_cst][valor_item][valor_base]['Data de Execução'] = valor + if self.verbose: + valor_formatado = __class__.formatar_valor(nome=campo.nome, val=valor) + print(f'campo.indice = {campo.indice:>2} ; campo.nome = {campo.nome:>22} ; registro.valores[{campo.indice:>2}] = {valor:<50} ; valor_formatado = {valor_formatado}') + + print() if self.verbose else 0 + + return info_de_abertura + + def imprimir_informacoes_linha_a_linha(self,sped_efd,output_filename): + ''' + Imprimir a título de aprendizagem + Observar as sequências de informações dos registros dos blocos + ''' + my_regex = "^[ABCDFI]" # Ler apenas os blocos A, B, C, D, F e I. + # https://docs.python.org/3/library/csv.html + with open(output_filename, 'w', newline='') as csvfile: + writer = csv.writer(csvfile, delimiter=';') + writer.writerow(__class__.colunas) # imprimir nomes das colunas + + for key in sped_efd._blocos.keys(): + + match_bloco = re.search(my_regex, key, flags=re.IGNORECASE) + if not match_bloco: + continue + + bloco = sped_efd._blocos[key] + count = 1 + + for registro in bloco.registros: + + REG = registro.valores[1] + nivel = registro.nivel + + info = {} + for coluna in __class__.colunas: + info[coluna] = '' + if coluna in self.info_de_abertura[0]['']['']: + info[coluna] = self.info_de_abertura[0][''][''][coluna] + + for campo in registro.campos: + + valor = registro.valores[campo.indice] + + if campo.nome in __class__.colunas: + info[campo.nome] = valor + if campo.nome in __class__.registros_de_valor: + info['Valor do Item'] = valor + if campo.nome in __class__.registros_de_chave_eletronica: + info['Chave Eletrônica'] = valor + if campo.nome in __class__.registros_de_base_de_calculo: + info['Valor da Base de Cálculo'] = valor + + writer.writerow( info.values() ) + + # limitar tamanho do arquivo impresso + # Imprimir apenas os 100 primeiros registro de cada Bloco + count += 1 + if self.verbose and count > 100: + break + + def adicionar_informacoes(self,dict_info): + ''' + Adicionar informações em dict_info + Formatar alguns de seus campos com o uso de tabelas ou funções + ''' + dict_info['Arquivo da SPED EFD'] = self.basename + dict_info['Linhas'] = next(__class__.contador_de_linhas) + + # re.search: find something anywhere in the string and return a match object. + if re.search('\d{1,2}', dict_info['CST Código da Situação Tributária']): # em perl: if (cst =~ /\d{1,2}/) + cst = int(dict_info['CST Código da Situação Tributária']) + if 1 <= cst <= 49: + dict_info['Tipo de Operação'] = 'Saída' + elif 50 <= cst <= 99: + dict_info['Tipo de Operação'] = 'Entrada' + + # adicionar informação de NAT_BC_CRED para os créditos (50 <= cst <= 66) quando houver informação do CFOP e NAT_BC_CRED estiver vazio. + if (len(dict_info['NAT_BC_CRED']) == 0 and re.search('\d{4}', dict_info['CFOP']) + #and ( re.search('[1-9]', dict_info['ALIQ_PIS']) or re.search('[1-9]', dict_info['ALIQ_COFINS']) ) # aliq_cofins > 0 + and re.search('\d{1,2}', dict_info['CST Código da Situação Tributária'])): + cfop = str(dict_info['CFOP']) + cst = int(dict_info['CST Código da Situação Tributária']) + if 50 <= cst <= 66: + dict_info['NAT_BC_CRED'] = __class__.natureza_da_bc_dos_creditos(cfop) + + # Índice de Origem do Crédito: Leia os comentários do 'Registro M100: Crédito de PIS/Pasep Relativo ao Período'. + # Os códigos vinculados à importação (108, 208 e 308) são obtidos através da informação de CFOP iniciado em 3 (quando existente) ou pelo campo IND_ORIG_CRED nos demais casos. + # O registro C100 possui o campo IND_OPER. IND_OPER igual a "0" (zero) indica operação de entrada. Veja os comentários do Registro C120. + indicador_de_origem = 'Mercado Interno' # Default Value: 0 - Mercado Interno ; 1 - Mercado Externo (Importação). + if len(dict_info['IND_ORIG_CRED']) == 0 and re.search('^3\d{3}', dict_info['CFOP']): + indicador_de_origem = 'Mercado Externo (Importação)' + dict_info['IND_ORIG_CRED'] = indicador_de_origem + + # adicionar informação de cadastro do participante obtido do Registro 0150 + # info_do_participante[codigo_do_participante][campo] = descricao + codigo_do_participante = dict_info['COD_PART'] + if codigo_do_participante != '': + for campo in self.info_do_participante[codigo_do_participante]: + dict_info[campo] = self.info_do_participante[codigo_do_participante][campo] + + # adicionar informação de identificação do item obtido do Registro 0200 + # info_do_item[codigo_do_item][campo] = descricao + codigo_do_item = dict_info['COD_ITEM'] + if codigo_do_item != '': + for campo in self.info_do_item[codigo_do_item]: + dict_info[campo] = self.info_do_item[codigo_do_item][campo] + + # adicionar informação do plano de contas obtido do Registro 0500 + codigo_da_conta = dict_info['COD_CTA'] + # info_da_conta[codigo_da_conta][campo] = descricao + if codigo_da_conta != '': + for campo in self.info_da_conta[codigo_da_conta]: + val = str(self.info_da_conta[codigo_da_conta][campo]) + if campo == 'COD_NAT_CC' and re.search('\d{1,2}', val): + val = val.zfill(2) # val = f'{int(val):02d}' + val = val + ' - ' + EFD_Tabelas.tabela_natureza_da_conta[val] + dict_info[campo] = val + + # Ao final, formatar alguns valores dos campos + for campo in dict_info.copy(): + valor_formatado = __class__.formatar_valor(nome=campo, val=dict_info[campo]) + dict_info[campo] = valor_formatado + + return dict_info + + def imprimir_informacoes_da_efd(self,sped_efd,output_filename): + + my_regex = "^[ABCDFI]" # Ler apenas os blocos A, B, C, D, F e I. + + campos_necessarios = ['CST_PIS', 'CST_COFINS', 'VL_BC_PIS', 'VL_BC_COFINS'] + # Bastam os seguintes campos, desde que os registros de PIS/PASEP ocorram sempre anteriores aos registros de COFINS: + # campos_necessarios = ['CST_COFINS', 'VL_BC_COFINS'] + + # https://docs.python.org/3/library/csv.html + with open(output_filename, 'w', newline='') as csvfile: + writer = csv.writer(csvfile, delimiter=';') + writer.writerow(__class__.colunas) # imprimir nomes das colunas + + for key in sped_efd._blocos.keys(): + + match_bloco = re.search(my_regex, key, flags=re.IGNORECASE) + if not match_bloco: + continue + + bloco = sped_efd._blocos[key] + count = 1 + + info = self.info_de_abertura + + for registro in bloco.registros: + + REG = registro.valores[1] + + #print(f'efd_print_info: {registro = } ; {registro.nivel = } ; {registro.__dict__ = }') + #sleep(0.1) + + try: + nivel_anterior = nivel + num_de_campos_anterior = num_de_campos + except: + nivel_anterior = registro.nivel + 1 + num_de_campos_anterior = len(registro.campos) + 1 + + nivel = registro.nivel # nível atual + num_de_campos = len(registro.campos) + + codigo_cst = '' + valor_item = '' + valor_base = '' + + for campo in registro.campos: + if campo.nome in __class__.registros_de_codigo_cst: + codigo_cst = registro.valores[campo.indice] + if campo.nome in __class__.registros_de_base_de_calculo: + valor_base = registro.valores[campo.indice] + if campo.nome in __class__.registros_de_valor: + valor_item = registro.valores[campo.indice] + + if self.verbose: + print(f'\ncount = {count:>2} ; key = {key} ; bloco = {bloco} ; REG = {REG} ; nivel_anterior = {nivel_anterior} ; nivel = {nivel} ; num_de_campos_anterior = {num_de_campos_anterior} ; num_de_campos = {num_de_campos} ; codigo_cst = {codigo_cst}') + print(f'registro.as_line() = {registro.as_line()}') + + # As informações do pai e respectivos filhos devem ser apagadas quando o nivel hierárquico regride dos filhos para pais diferentes. + if nivel < nivel_anterior or (nivel == nivel_anterior and num_de_campos < num_de_campos_anterior): + if self.verbose and nivel < nivel_anterior: + print(f'\n nivel atual: nivel = {nivel} < nivel_anterior = {nivel_anterior}; deletar informações em info a partir do nível {nivel} em diante:') + if self.verbose and nivel == nivel_anterior and num_de_campos < num_de_campos_anterior: + print(f'\n numero de campos atual: num_de_campos = {num_de_campos} < num_de_campos_anterior = {num_de_campos_anterior}; deletar informações em info a partir do nível {nivel} em diante:') + # Delete items from dictionary while iterating: https://www.geeksforgeeks.org/python-delete-items-from-dictionary-while-iterating/ + for nv in list(info): + if nv >= nivel: + del info[nv] + if self.verbose: + print(f'\t *** deletar informações do nível {nv}: del info[{nv}] ***') + print() if self.verbose else 0 + + info.setdefault(nivel, {}).setdefault(codigo_cst, {}).setdefault(valor_item, {}).setdefault(valor_base, {})['Valor do Item'] = valor_item + + for campo in registro.campos: + + valor = registro.valores[campo.indice] + + if self.verbose: + valor_formatado = __class__.formatar_valor(nome=campo.nome, val=valor) + print(f'campo.indice = {campo.indice:>2} ; campo.nome = {campo.nome:>22} ; registro.valores[{campo.indice:>2}] = {valor:<50} ; valor_formatado = {valor_formatado}') + + if campo.nome in __class__.colunas: + info[nivel][codigo_cst][valor_item][valor_base][campo.nome] = valor + + # Informar os campos em registros_de_data_emissao na coluna 'Data de Emissão'. + if campo.nome in __class__.registros_de_data_emissao: + info[nivel][codigo_cst][valor_item][valor_base]['Data de Emissão'] = valor + # Informar os campos em registros_de_data_execucao na coluna 'Data de Execução'. + if campo.nome in __class__.registros_de_data_execucao: + info[nivel][codigo_cst][valor_item][valor_base]['Data de Execução'] = valor + # Informar os campos de chave eletrônica de 44 dígitos na coluna 'Chave Eletrônica'. + if campo.nome in __class__.registros_de_chave_eletronica: + info[nivel][codigo_cst][valor_item][valor_base]['Chave Eletrônica'] = valor + # Informar os campos CST_PIS e CST_COFINS na coluna 'CST Código da Situação Tributária'. + if campo.nome in __class__.registros_de_codigo_cst: + info[nivel][codigo_cst][valor_item][valor_base][campo.nome] = valor + info[nivel][codigo_cst][valor_item][valor_base]['CST Código da Situação Tributária'] = valor + # Informar os campos VL_BC_PIS e VL_BC_COFINS na coluna 'Valor da Base de Cálculo'. + if campo.nome in __class__.registros_de_base_de_calculo: + info[nivel][codigo_cst][valor_item][valor_base][campo.nome] = valor + info[nivel][codigo_cst][valor_item][valor_base]['Valor da Base de Cálculo'] = valor + + if self.verbose: + print(f'\n-->info[nivel][codigo_cst][valor_item][valor_base] = info[{nivel}][{codigo_cst}][{valor_item}][{valor_base}] = {info[nivel][codigo_cst][valor_item][valor_base]}\n') + + #https://stackoverflow.com/questions/3931541/how-to-check-if-all-of-the-following-items-are-in-a-list + # set(['a', 'c']).issubset(['a', 'b', 'c', 'd']) or set(lista1).issubset(lista2) + + if set(campos_necessarios).issubset( info[nivel][codigo_cst][valor_item][valor_base] ): + + # Zen of Python: Flat is better than nested. + flattened_info = {} # eliminar os diversos niveis e trazer todas as informações para apenas uma dimensão. + + for coluna in __class__.colunas: + flattened_info[coluna] = '' # atribuir valor inicial para todas as colunas + + if coluna in info[nivel][codigo_cst][valor_item][valor_base]: + flattened_info[coluna] = info[nivel][codigo_cst][valor_item][valor_base][coluna] + if self.verbose: + print(f'nivel = {nivel:<10} ; codigo_cst = {codigo_cst:<2} ; valor_item = {valor_item:<15} ; coluna = {coluna:>35} = {info[nivel][codigo_cst][valor_item][valor_base][coluna]:<35} ; info[nivel][codigo_cst][valor_item][valor_base] = {info[nivel][codigo_cst][valor_item][valor_base]}') + continue + + for nv in sorted(info,reverse=True): # nível em ordem decrescente + if nv >= nivel: # informação já obtida acima + continue + if flattened_info[coluna] != '': # as informações obtidas do nível mais alto prevalecerá + break + for cst in info[nv]: + for vl_item in info[nv][cst]: + for vl_base in info[nv][cst][vl_item]: + if coluna in info[nv][cst][vl_item][vl_base]: + flattened_info[coluna] = info[nv][cst][vl_item][vl_base][coluna] + if self.verbose: + print(f'nivel = {nivel} ; nv = {nv} ; codigo_cst = {cst:<2} ; valor_item = {vl_item:<15} ; coluna = {coluna:>35} = {info[nv][cst][vl_item][vl_base][coluna]:<35} ; info[nv][cst][vl_item][vl_base] = {info[nv][cst][vl_item][vl_base]}') + + print() if self.verbose else 0 + + flattened_info['Nº da Linha da EFD'] = registro.numero_da_linha + + # Adicionar informações em flattened_info ou formatar alguns de seus campos com o uso de tabelas ou funções + flattened_info = self.adicionar_informacoes(flattened_info) + + writer.writerow( flattened_info.values() ) + + # Limitar tamanho do arquivo impresso + # Imprimir apenas os 20 primeiros registro de cada Bloco + count += 1 + if self.verbose and count > 20: + break + + print(f"Gerado o arquivo csv: '{output_filename}'.") + +if __name__ == '__main__': + + from efd_read_dir import ReadFiles, Total_Execution_Time + + dir_path = os.getcwd() # CurrentDirectory + extensao = 'txt' + + lista_de_arquivos = ReadFiles(root_path = dir_path, extension = extensao) + + #print(f'{lista_de_arquivos.find_all_files = }') + + arquivos_efd = list(lista_de_arquivos.find_all_efd_contrib) # SPED EFD Contrib: + + for index,file_path in enumerate(arquivos_efd,1): + print( f"{index:>6}: {file_path}") + for attribute, value in lista_de_arquivos.get_file_info(file_path).items(): + print(f'{attribute:>25}: {value}') + + indice_do_arquivo = None + + if len(arquivos_efd) > 1: + while indice_do_arquivo is None: + my_input = input(f"\nFavor, digite o número do arquivo da EFD Contribuições (1 a {len(arquivos_efd)}): ") + try: + my_input = int(my_input) + if 1 <= my_input <= len(arquivos_efd): + indice_do_arquivo = my_input - 1 + except: + print(f"-->Opção incorreta: '{my_input}'.") + print(f"-->Digite um número inteiro entre 1 e {len(arquivos_efd)}.") + elif len(arquivos_efd) == 1: + indice_do_arquivo = 0 + else: + dir_path_exemplo = '/home/claudio/Documentos/' + print(f"\nA lista de arquivos de SPED Contribuições é obtida a partir de:\n") + print(f"\tlista_de_arquivos = ReadFiles(root_path = dir_path, extension = extensao).\n") + print(f"tal que:\n") + print(f"\tdir_path = '{dir_path}' e extensao = '{extensao}'.\n") + print(f"Nenhum arquivo de EFD Contribuções foi encontrado no diretório definido acima.") + print(f"Se as EFDs estão localizadas, por exemplo, no diretório '{dir_path_exemplo}',") + print(f"então altere a variável 'dir_path' para o diretório que contenha as EFDs:") + print(f"\n\tdir_path = '{dir_path_exemplo}'\n") + print(f"Outra alternativa é copiar este arquivo '{__file__}' para o diretório que contenha as EFDs.") + print(f"Em seguida, executar no terminal:\n") + print(f"\t python {__file__} \n") + exit() + + # arquivo EFD Contribuições + file_path = arquivos_efd[indice_do_arquivo] + codif = lista_de_arquivos.informations[file_path]['codificação'] + + print(f"\nFoi selecionado o arquivo {indice_do_arquivo + 1}: '{file_path}'\n") + input("Tecle Enter para gerar arquivo .csv com informações da EFD ") + print() + + start = time() + + efd = EFD_Contrib_Info(file_path, encoding=codif, verbose=False) + + #print(f'\n efd.basename = {efd.basename}') + #print(f'\n efd.colunas = {efd.colunas}') + #print(f'\n efd.natureza_da_bc_dos_creditos("1556") = {efd.natureza_da_bc_dos_creditos("1556")}') + #print(f'\n efd.formatar_valor("CNPJ","12345678000199") = {efd.formatar_valor("CNPJ","12345678000199")}\n') + + efd.imprimir_informacoes + + end = time() + print(f'\nTotal Execution Time: {Total_Execution_Time(start,end)} \n') + + diff --git a/sped/relatorios/efd_read_dir.py b/sped/relatorios/efd_read_dir.py new file mode 100755 index 0000000..9649c87 --- /dev/null +++ b/sped/relatorios/efd_read_dir.py @@ -0,0 +1,212 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import re, os, glob, sys +import cchardet as chardet # pip3 install cchardet + +Autor = 'Claudio Fernandes de Souza Rodrigues (claudiofsr@yahoo.com)' +Data = '06 de Fevereiro de 2020 (início: 15 de Dezembro de 2020)' + +# Versão mínima exigida: python 3.6.0 +python_version = sys.version_info +if python_version < (3,6,0): + print('versão mínima exigida do python é 3.6.0') + print('versão atual', "%s.%s.%s" % (python_version[0],python_version[1],python_version[2])) + exit() + +# Python OOP: Atributos e Métodos (def, funções) +class ReadFiles: + """Retorna um dicionário com informações dos arquivos encontrados no diretório""" + recursive = True + current_dir = os.getcwd() + seen_file = set() # evitar duplicidade: Is there a more Pythonic way to prevent adding a duplicate to a list? + + def __init__(self, root_path = None, extension = None, pattern = None): + if root_path is None: + self.root_path = self.current_dir + else: + self.root_path = root_path + if extension is None: + self.extension = '*' + else: + self.extension = extension.strip('.') + if pattern is None: + self.pattern = '.' + else: + self.pattern = pattern + self.informations = {} # instance attributes, dict + + def __repr__(self): + return f'{self.__class__.__name__}(root_path = {self.root_path!r}, extension = {self.extension!r}, pattern = {self.pattern!r})' + + def get_file_extension(self,file_path): + # https://stackoverflow.com/questions/541390/extracting-extension-from-filename-in-python + filename, file_extension = os.path.splitext(file_path) + return file_extension + + def get_filename(self,file_path): + # https://stackoverflow.com/questions/541390/extracting-extension-from-filename-in-python + filename = os.path.basename(file_path) + return filename + + # https://stackoverflow.com/questions/9629179/python-counting-lines-in-a-huge-10gb-file-as-fast-as-possible + def count_number_of_lines(self,file_path): + lines = 0 + try: + with open(file_path, mode='r', encoding="utf-8", errors='ignore') as filename: + for line in filename: + lines += 1 + except FileNotFoundError: + print('O Arquivo não existe!') + return f'{lines:,}'.replace(',','.') + + # https://stackoverflow.com/questions/436220/how-to-determine-the-encoding-of-text + # https://chardet.readthedocs.io/en/latest/usage.html + # https://github.com/PyYoshi/cChardet + # iconv -f WINDOWS-1252 -t UTF-8 filename.txt # iconv - convert text from one character encoding to another + def predict_encoding(self,file_path): + '''Predict a file's encoding using cchardet''' + # import cchardet as chardet + lines = [] + my_regex = b"^\|9999\|\d+\|" # descartar informações após a linha que contém |9999| seguido por dígitos ; b'': binary mode + with open(file_path, mode='rb') as filename: # mode 'rb': open the file as binary data + for line in filename: + match_object = re.search(my_regex, line, flags=re.IGNORECASE) + if len(lines) > 2**8 or match_object: + #print(f'{file_path = } ; {line = }') + break + lines.append(line) + rawdata = b''.join(lines) + info = chardet.detect(rawdata) # {'encoding': 'UTF-8', 'confidence': 0.9900000095367432} + encoding = info['encoding'] + if encoding is None or not re.search('UTF-8', encoding, flags=re.IGNORECASE): + encoding = 'Latin-1' + return encoding + + @property + def find_all_files(self): + # https://www.mkyong.com/python/python-how-to-list-all-files-in-a-directory/ + # https://docs.python.org/3/library/glob.html + files = [f for f in glob.glob(self.root_path + '/' + f"**/*.{self.extension}", recursive = self.recursive)] + # How to use a variable inside a regular expression? + my_regex = f"{self.pattern}" # Literal String Interpolation, "f-strings". + for file_path in files: + # path.isfile: The easiest way to check if both a file exists and if it is a file. + # seen_file: Is there a more Pythonic way to prevent adding a duplicate to a list? + if not os.path.isfile(file_path) or file_path.casefold() in self.seen_file: + continue + match_pattern = re.search(my_regex, str(file_path), flags=re.IGNORECASE) + if match_pattern: + self.seen_file.add(file_path.casefold()) + self.informations.setdefault(file_path, {})['tipo'] = 'Arquivo' + return self.informations + + @property + def find_all_efd_contrib(self): + indice = 0 + # Calling one method from another within same class in Python + return self.find_all_efd(indice) + + @property + def find_all_efd_icmsipi(self): + indice = 1 + # Calling one method from another within same class in Python + return self.find_all_efd(indice) + + def find_all_efd(self,indice=0): + if not self.informations: # How do I check if a list/dict is empty? + self.find_all_files # Calling one method from another within same class in Python + regex_efd = ["PISCOFINS", "SPED-EFD"] + efd_type = ['EFD Contribuições', 'EFD ICMS_IPI'] + idx_nome = [8,6] + idx_data = [6,4] + idx_cnpj = [9,7] + for file_path in self.informations: + encode_info = None + if not re.search(regex_efd[indice], file_path, flags=re.IGNORECASE): + continue + # Ler apenas a primeira linha para obter encode_info + with open(file_path, mode='rb') as filename: # mode 'rb': open the file as binary data + for line in filename: + info = chardet.detect(line) # {'encoding': 'UTF-8', 'confidence': 0.9900000095367432} + encode_info = info['encoding'] + #print(f'{file_path = } ; {encode_info = } ; {info = }') + break + # Ler apenas a primeira linha para obter informações do registro de abertura '0000'. + with open(file_path, mode='r', encoding=encode_info, errors='ignore') as filename: # encoding='latin-1','UTF-8' + for line in filename: + campos = line.strip().split('|') + if len(campos) <= 10: + break + campo_registro = campos[1] # A primeira linha deve conter o registro '0000' + campo_nome = campos[ idx_nome[indice] ] + campo_data = campos[ idx_data[indice] ] + campo_cnpj = campos[ idx_cnpj[indice] ] # o campo CNPJ deve conter 14 dígitos + match_regi = re.search( '0000', campo_registro) + match_cnpj = re.search('(\D|^)\d{14}(\D|$)', campo_cnpj) + match_data = re.search( '(\D|^)\d{8}(\D|$)', campo_data) + if match_regi and match_cnpj and match_data: + self.informations[file_path]['tipo'] = efd_type[indice] + self.informations[file_path]['NOME'] = campo_nome + self.informations[file_path]['CNPJ'] = "%s.%s.%s/%s-%s" % (campo_cnpj[0:2],campo_cnpj[2:5],campo_cnpj[5:8],campo_cnpj[8:12],campo_cnpj[12:14]) + self.informations[file_path]['DT_INI'] = "%s/%s/%s" % (campo_data[0:2],campo_data[2:4],campo_data[4:8]) + break + # Filter a Dictionary by values in Python using dict comprehension + return {key: value for (key, value) in sorted(self.informations.items()) if value['tipo'] == efd_type[indice]} + + def get_file_info(self,file_path): + self.informations[file_path]['extensão' ] = self.get_file_extension(file_path) + self.informations[file_path]['codificação' ] = self.predict_encoding(file_path) + self.informations[file_path]['número de linhas'] = self.count_number_of_lines(file_path) + return self.informations[file_path] + +from time import time, sleep + +def Total_Execution_Time(start,end=None): + ''' + How to format elapsed time from seconds to hours, minutes, seconds and milliseconds in Python? + https://stackoverflow.com/questions/27779677/how-to-format-elapsed-time-from-seconds-to-hours-minutes-seconds-and-milliseco + https://stackoverflow.com/questions/3620943/measuring-elapsed-time-with-the-time-module/46544199 + https://blog.softhints.com/python-test-performance-and-measure-time-elapsed-in-seconds/ + ''' + if end is None: + end = time() + hours, rem = divmod(end-start, 3600) + minutes, seconds = divmod(rem, 60) + return f"{int(hours):02d}h:{int(minutes):02d}m:{seconds:07.4f}s" + + +if __name__ == '__main__': + + start = time() + + #print(f'\n{ReadFiles.__dict__ = } \n') + + lista_de_arquivos0 = ReadFiles(root_path = '.') + lista_de_arquivos1 = ReadFiles(root_path = 'EFD Contribuicoes', extension = 'txt', pattern = 'PISCOFINS') + lista_de_arquivos2 = ReadFiles(root_path = 'EFD ICMS_IPI-ADM', extension = 'txt', pattern = 'SPED-EFD' ) + + arquivos_da_efd = [lista_de_arquivos0, lista_de_arquivos1, lista_de_arquivos2] + + for lista_de_arquivos in arquivos_da_efd: + print(f'\nlista_de_arquivos = {lista_de_arquivos}:\n') + + print(f'lista_de_arquivos.find_all_files = {lista_de_arquivos.find_all_files} ; len(lista_de_arquivos.informations) = {len(lista_de_arquivos.informations)}\n') + + # SPED EFD Contrib: + for index,file_path in enumerate(lista_de_arquivos.find_all_efd_contrib,1): + print( f"{index:>6}: {file_path} ; tipo = {lista_de_arquivos.informations[file_path]['tipo']}" ) + #continue + for attribute, value in lista_de_arquivos.get_file_info(file_path).items(): + print(f'{attribute:>30}: {value}') + + # SPED EFD ICMS_IPI: + for index,file_path in enumerate(lista_de_arquivos.find_all_efd_icmsipi,1): + print( f"{index:>6}: {file_path} ; tipo = {lista_de_arquivos.informations[file_path]['tipo']}" ) + #continue + for attribute, value in lista_de_arquivos.get_file_info(file_path).items(): + print(f'{attribute:>30}: {value}') + + end = time() + print(f'\nTotal Execution Time: {Total_Execution_Time(start,end)} \n') + diff --git a/sped/relatorios/efd_tabelas.py b/sped/relatorios/efd_tabelas.py new file mode 100755 index 0000000..520233c --- /dev/null +++ b/sped/relatorios/efd_tabelas.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +Autor = 'Claudio Fernandes de Souza Rodrigues (claudiofsr@yahoo.com)' +Data = '06 de Fevereiro de 2020 (início: 10 de Janeiro de 2020)' + +class EFD_Tabelas: + """ + Tabelas utilizadas na EFD + """ + + tabela_natureza_da_conta = { + '01': 'Contas de ativo', + '02': 'Contas de passivo', + '03': 'Patrimônio líquido', + '04': 'Contas de resultado', + '05': 'Contas de compensação', + '09': 'Outras', + } + + # Tabela 4.3.7 - Tabela Código de Base de Cálculo do Crédito + tabela_bc_do_credito = { + '01': 'Aquisição de bens para revenda', + '02': 'Aquisição de bens utilizados como insumo', + '03': 'Aquisição de serviços utilizados como insumo', + '04': 'Energia elétrica e térmica, inclusive sob a forma de vapor', + '05': 'Aluguéis de prédios', + '06': 'Aluguéis de máquinas e equipamentos', + '07': 'Armazenagem de mercadoria e frete na operação de venda', + '08': 'Contraprestações de arrendamento mercantil', + '09': 'Máquinas, equipamentos e outros bens incorporados ao ativo imobilizado (crédito sobre encargos de depreciação)', + '10': 'Máquinas, equipamentos e outros bens incorporados ao ativo imobilizado (crédito com base no valor de aquisição)', + '11': 'Amortização e Depreciação de edificações e benfeitorias em imóveis', + '12': 'Devolução de Vendas Sujeitas à Incidência Não-Cumulativa', + '13': 'Outras Operações com Direito a Crédito', + '14': 'Atividade de Transporte de Cargas - Subcontratação', + '15': 'Atividade Imobiliária - Custo Incorrido de Unidade Imobiliária', + '16': 'Atividade Imobiliária - Custo Orçado de unidade não concluída', + '17': 'Atividade de Prestação de Serviços de Limpeza, Conservação e Manutenção - vale-transporte, valerefeição ou vale-alimentação, fardamento ou uniforme', + '18': 'Estoque de abertura de bens', + } + + # 4.3.3 - CÓDIGO DA SITUAÇÃO TRIBUTÁRIA REFERENTE AO PIS/PASEP e COFINS + tabela_cst = { + '01': 'Operação Tributável com Alíquota Básica', + '02': 'Operação Tributável com Alíquota Diferenciada', + '03': 'Operação Tributável com Alíquota por Unidade de Medida de Produto', + '04': 'Operação Tributável Monofásica - Revenda a Alíquota Zero', + '05': 'Operação Tributável por Substituição Tributária', + '06': 'Operação Tributável a Alíquota Zero', + '07': 'Operação Isenta da Contribuição', + '08': 'Operação sem Incidência da Contribuição', + '09': 'Operação com Suspensão da Contribuição', + '49': 'Outras Operações de Saída', + '50': 'Operação com Direito a Crédito - Vinculada Exclusivamente a Receita Tributada no Mercado Interno', + '51': 'Operação com Direito a Crédito - Vinculada Exclusivamente a Receita Não Tributada no Mercado Interno', + '52': 'Operação com Direito a Crédito - Vinculada Exclusivamente a Receita de Exportação', + '53': 'Operação com Direito a Crédito - Vinculada a Receitas Tributadas e Não-Tributadas no Mercado Interno', + '54': 'Operação com Direito a Crédito - Vinculada a Receitas Tributadas no Mercado Interno e de Exportação', + '55': 'Operação com Direito a Crédito - Vinculada a Receitas Não-Tributadas no Mercado Interno e de Exportação', + '56': 'Operação com Direito a Crédito - Vinculada a Receitas Tributadas e Não-Tributadas no Mercado Interno, e de Exportação', + '60': 'Crédito Presumido - Operação de Aquisição Vinculada Exclusivamente a Receita Tributada no Mercado Interno', + '61': 'Crédito Presumido - Operação de Aquisição Vinculada Exclusivamente a Receita Não-Tributada no Mercado Interno', + '62': 'Crédito Presumido - Operação de Aquisição Vinculada Exclusivamente a Receita de Exportação', + '63': 'Crédito Presumido - Operação de Aquisição Vinculada a Receitas Tributadas e Não-Tributadas no Mercado Interno', + '64': 'Crédito Presumido - Operação de Aquisição Vinculada a Receitas Tributadas no Mercado Interno e de Exportação', + '65': 'Crédito Presumido - Operação de Aquisição Vinculada a Receitas Não-Tributadas no Mercado Interno e de Exportação', + '66': 'Crédito Presumido - Operação de Aquisição Vinculada a Receitas Tributadas e Não-Tributadas no Mercado Interno, e de Exportação', + '67': 'Crédito Presumido - Outras Operações', + '70': 'Operação de Aquisição sem Direito a Crédito', + '71': 'Operação de Aquisição com Isenção', + '72': 'Operação de Aquisição com Suspensão', + '73': 'Operação de Aquisição a Alíquota Zero', + '74': 'Operação de Aquisição sem Incidência da Contribuição', + '75': 'Operação de Aquisição por Substituição Tributária', + '98': 'Outras Operações de Entrada', + '99': 'Outras Operações', + } + + tabela_tipo_do_item = { + '00': 'Mercadoria para Revenda', + '01': 'Matéria-Prima', + '02': 'Embalagem', + '03': 'Produto em Processo', + '04': 'Produto Acabado', + '05': 'Subproduto', + '06': 'Produto Intermediário', + '07': 'Material de Uso e Consumo', + '08': 'Ativo Imobilizado', + '09': 'Serviços', + '10': 'Outros insumos', + '99': 'Outras', + } + + # 4.1.1- Tabela Modelos de Documentos Fiscais + tabela_modelos_documentos_fiscais = { + '01': 'Nota Fiscal', + '1B': 'Nota Fiscal Avulsa', + '02': 'Nota Fiscal de Venda a Consumidor', + '2D': 'Cupom Fiscal emitido por ECF', + '2E': 'Bilhete de Passagem emitido por ECF', + '04': 'Nota Fiscal de Produtor', + '06': 'Nota Fiscal / Conta de Energia Elétrica', + '07': 'Nota Fiscal de Serviço de Transporte', + '08': 'Conhecimento de Transporte Rodoviário de Cargas', + '8B': 'Conhecimento de Transporte de Cargas Avulso', + '09': 'Conhecimento de Transporte Aquaviário de Cargas', + '10': 'Conhecimento Aéreo', + '11': 'Conhecimento de Transporte Ferroviário de Cargas', + '13': 'Bilhete de Passagem Rodoviário', + '14': 'Bilhete de Passagem Aquaviário', + '15': 'Bilhete de Passagem e Nota de Bagagem', + '17': 'Despacho de Transporte', + '16': 'Bilhete de Passagem Ferroviário', + '18': 'Resumo de Movimento Diário', + '20': 'Ordem de Coleta de Cargas', + '21': 'Nota Fiscal de Serviço de Comunicação', + '22': 'Nota Fiscal de Serviço de Telecomunicação', + '23': 'GNRE', + '24': 'Autorização de Carregamento e Transporte', + '25': 'Manifesto de Carga', + '26': 'Conhecimento de Transporte Multimodal de Cargas', + '27': 'Nota Fiscal de Transporte Ferroviário de Cargas', + '28': 'Nota Fiscal / Conta de Fornecimento de Gás Canalizado', + '29': 'Nota Fiscal / Conta de Fornecimento de Água Canalizada', + '30': 'Bilhete / Recibo do Passageiro', + '55': 'Nota Fiscal Eletrônica: NF-e', + '57': 'Conhecimento de Transporte Eletrônico: CT-e', + '59': 'Cupom Fiscal Eletrônico: CF-e (CF-e-SAT)', + '60': 'Cupom Fiscal Eletrônico: CF-e-ECF', + '63': 'Bilhete de Passagem Eletrônico: BP-e', + '65': 'Nota Fiscal Eletrônica ao Consumidor Final: NFC-e', + '66': 'Nota Fiscal de Energia Elétrica Eletrônica: NF3e', + '67': 'Conhecimento de Transporte Eletrônico para Outros Serviços: CT-e OS', + }