import configparser import openpyxl import chardet DEFAULT_ENCODING = 'ISO-8859-1' CONFIG_FILE = 'config_motoradio.ini' def detect_encoding(file_path): with open(file_path, 'rb') as file: raw_data = file.read() result = chardet.detect(raw_data) return result['encoding'] def read_file(file_path, encoding=DEFAULT_ENCODING): """ Lê o conteúdo completo de um arquivo de texto. Args: file_path (str): Caminho do arquivo. encoding (str): Codificação do arquivo. Returns: str: Conteúdo do arquivo como string. """ with open(file_path, 'r', encoding=encoding) as f: return f.read() def read_lines(file_path, encoding=DEFAULT_ENCODING): with open(file_path, 'r', encoding=encoding) as f: return f.readlines() def write_file(file_path, content, encoding=DEFAULT_ENCODING): with open(file_path, 'w', encoding=encoding) as f: f.write(content) def write_lines(file_path, lines, encoding=DEFAULT_ENCODING): with open(file_path, 'w', encoding=encoding) as f: f.writelines(lines) class Config: def __init__(self, config_file=CONFIG_FILE): self.config = configparser.ConfigParser() self.config.read(config_file) self.codigo_st = self.config['DEFAULT']['Codigo_ST'] self.codigo_receita = self.config['DEFAULT']['Codigo_Receita'] self.mes_apuracao = self.config['DEFAULT']['Mes_Apuracao'] self.modelo_arquivo = self.config['DEFAULT']['ModeloArquivo'] self.caminho_planilha = self.config['DEFAULT']['CaminhoPlanilha'] def contar_linhas_com_e(linhas): linha_completa = ''.join(linhas) # Junta todas as linhas em uma única string linhas_separadas = linha_completa.split('\n') # Separa novamente por linhas return sum(1 for linha in linhas_separadas if linha.startswith('|E')) def substituir_valor(arquivo, antigo, novo): with open(arquivo, 'r', encoding='ISO-8859-1') as file: linhas = file.readlines() linhas = [linha.replace(antigo, novo) for linha in linhas] with open(arquivo, 'w', encoding='ISO-8859-1') as file: file.writelines(linhas) def find_e200_states(content): lines = content.split('\n') states = set() for line in lines: if line.startswith('|E200|'): parts = line.split('|') if len(parts) > 2: state_code = parts[2] states.add(state_code) return list(states) def count_9900_lines(content): lines = content.split('\n') count = 0 for line in lines: if '|9900|' in line and '|9900|9900|' not in line: count += 1 return count def count_E250_lines(content): lines = content.split('\n') count = 0 for line in lines: if '|E250|' in line: count += 1 return count def count_lines(file_path, encoding='ISO-8859-1'): with open(file_path, 'r', encoding=encoding) as file: return sum(1 for _ in file) def substituir_ultimas_linhas(arquivo, novas_linhas): with open(arquivo, 'r', encoding='ISO-8859-1') as file: linhas = file.readlines() # Remover as três últimas linhas linhas = linhas[:-2] # Remover todas as linhas que contenham "|9900|9900|" linhas = [linha for linha in linhas if '|9900|9900|' not in linha] # Adicionar as novas linhas linhas.extend(novas_linhas) # Escrever de volta no arquivo with open(arquivo, 'w', encoding='ISO-8859-1') as file: file.writelines(linhas) def substituir_linha_e990(arquivo, conteudo): # Abrir o arquivo para leitura with open(arquivo, 'r') as file: lines = file.readlines() # Abrir o arquivo para escrita with open(arquivo, 'w') as file: count = 1 for i, line in enumerate(lines): if '|E990|' in line: if count == 1: file.write(f'|E990|{contar_linhas_com_e(conteudo)}|' + '\n') count += 1 # Pular a próxima linha continue file.write(line) def inserir_linha_apos_9900_e210(arquivo, conteudo): with open(arquivo, 'r', encoding='ISO-8859-1') as file: linhas = file.readlines() # Contar e remover a linha que começa com '|9900|E250|', se existir linhas_removidas = sum(1 for linha in linhas if linha.startswith('|9900|E250|')) linhas = [linha for linha in linhas if not linha.startswith('|9900|E250|')] cont_E250 = count_E250_lines(conteudo) - linhas_removidas nova_linha = f'|9900|E250|{cont_E250}|' for i, linha in enumerate(linhas): if '|9900|E210|' in linha: linhas.insert(i + 1, nova_linha + '\n') break with open(arquivo, 'w', encoding='ISO-8859-1') as file: file.writelines(linhas) def find_e2xx_state_lines(content, state_code): lines = content.split('\n') return [(index, line) for index, line in enumerate(lines) if line.startswith('|E2') and f'|{state_code}' in line] def process_sheet(sheet, codigo_receita, mes_apuracao): new_values = [] for linha in sheet.iter_rows(min_row=2): valor = linha[39].value valor = str(valor).replace(".", ",") vencimento = linha[1].value if vencimento is None: print('Vencimento é None') continue vencimento = vencimento.strftime("%d%m%Y") if valor == '0': print('Valor zerado') continue new_values.append(f'|E250|999|{valor}|{vencimento}|{codigo_receita}|||||{mes_apuracao.replace("/", "")}|') return new_values def process_file(conteudo, config, estado): e2xx_state_lines = find_e2xx_state_lines(conteudo, estado) lines = conteudo.split('\n') workbook = openpyxl.load_workbook(config.caminho_planilha) sheet_estado = workbook[estado] new_values = process_sheet(sheet_estado, config.codigo_receita, config.mes_apuracao) for index, line in e2xx_state_lines: for new_line in new_values: lines.insert(index + 2, new_line) write_file(config.modelo_arquivo, '\n'.join(lines)) def processar_estados(estados, config): conteudo = read_file(config.modelo_arquivo) conteudo = "\n".join([linha for linha in conteudo.split("\n") if "|E250|999|" not in linha]) write_file(config.modelo_arquivo, conteudo) for estado in estados: if estado != 'SP': print(f'Processando estado {estado}') conteudo = read_file(config.modelo_arquivo) process_file(conteudo, config, estado) def main(): import os config = Config() conteudo = read_file(config.modelo_arquivo) estados = find_e200_states(conteudo) processar_estados(estados, config) conteudo = read_file(config.modelo_arquivo) substituir_linha_e990(config.modelo_arquivo, conteudo) inserir_linha_apos_9900_e210(config.modelo_arquivo, conteudo) substituir_valor(config.modelo_arquivo, '|04601|', '|046-2|') # Remova as chamadas antigas de substituir_ultimas_linhas e use apenas: atualizar_registros_finais(config.modelo_arquivo) # Salvar arquivo processado nome_original = config.modelo_arquivo nome_base, ext = os.path.splitext(nome_original) nome_processado = f"{nome_base}_processado{ext}" try: conteudo_final = read_file(nome_original) write_file(nome_processado, conteudo_final) print(f"Arquivo processado salvo como: {nome_processado}") except Exception as e: print(f"Erro ao salvar arquivo processado: {e}") def atualizar_registros_finais(arquivo): """ Atualiza os registros finais do arquivo SPED, removendo duplicidades e recalculando as quantidades dos registros 9900, 9990 e 9999. Args: arquivo (str): Caminho do arquivo a ser processado. Efeito: O arquivo é sobrescrito com os registros finais corrigidos. """ with open(arquivo, 'r', encoding='ISO-8859-1') as file: linhas = file.readlines() # Remove registros finais antigos para evitar duplicidade linhas = [linha for linha in linhas if not ( linha.startswith('|9900|9900|') or linha.startswith('|9900|E990|') or linha.startswith('|9990|') or linha.startswith('|9999|') )] # Contagem correta dos registros 9900 total_9900 = sum(1 for linha in linhas if linha.startswith('|9900|') and not linha.startswith('|9900|9900|')) # Bloco 9: começa em |9001| e termina em |9990| bloco9_indices = [i for i, linha in enumerate(linhas) if linha.startswith('|9001|')] if bloco9_indices: inicio_bloco9 = bloco9_indices[0] # Procura o fim do bloco 9 (último |9990|, se existir) fim_bloco9 = len(linhas) bloco9_linhas = linhas[inicio_bloco9:fim_bloco9] total_bloco9 = len(bloco9_linhas) + 4 # +4 para os registros finais que serão adicionados else: total_bloco9 = 4 # só os finais total_linhas_arquivo = len(linhas) + 4 # +4 para os registros finais # Adiciona os registros finais corretos linhas.extend([ f'|9900|9900|{total_9900 + 2}|\n', # +2 para E990 e 9900 '|9900|E990|1|\n', f'|9990|{total_bloco9}|\n', f'|9999|{total_linhas_arquivo}|\n' ]) with open(arquivo, 'w', encoding='ISO-8859-1') as file: file.writelines(linhas) if __name__ == "__main__": main()