feat: primeira versão da produção
Some checks failed
continuous-integration/drone/push Build is failing
Some checks failed
continuous-integration/drone/push Build is failing
This commit is contained in:
132
app/layouts/equatorial_go.py
Executable file
132
app/layouts/equatorial_go.py
Executable file
@@ -0,0 +1,132 @@
|
||||
# app/layouts/equatorial_go.py
|
||||
import re
|
||||
from datetime import datetime
|
||||
import fitz
|
||||
import logging
|
||||
|
||||
def converter_valor(valor_str):
|
||||
try:
|
||||
if not valor_str:
|
||||
return 0.0
|
||||
valor_limpo = str(valor_str).replace('.', '').replace(',', '.')
|
||||
valor_limpo = re.sub(r'[^\d.]', '', valor_limpo)
|
||||
return float(valor_limpo) if valor_limpo else 0.0
|
||||
except (ValueError, TypeError):
|
||||
return 0.0
|
||||
|
||||
def extrair_dados(texto_final):
|
||||
import logging
|
||||
logging.debug("\n========== INÍCIO DO TEXTO EXTRAÍDO ==========\n" + texto_final + "\n========== FIM ==========")
|
||||
|
||||
def extrair_seguro(patterns, texto_busca, flags=re.IGNORECASE | re.MULTILINE):
|
||||
if not isinstance(patterns, list): patterns = [patterns]
|
||||
for pattern in patterns:
|
||||
match = re.search(pattern, texto_busca, flags)
|
||||
if match:
|
||||
for group in match.groups():
|
||||
if group: return group.strip()
|
||||
return ""
|
||||
|
||||
nota_fiscal = extrair_seguro(r'NOTA FISCAL Nº\s*(\d+)', texto_final)
|
||||
|
||||
uc = extrair_seguro([
|
||||
r'(\d{7,10}-\d)',
|
||||
r'UNIDADE\s+CONSUMIDORA\s*[:\-]?\s*(\d{6,})',
|
||||
r'(\d{6,})\s+FAZENDA',
|
||||
r'(\d{6,})\s+AVENIDA',
|
||||
r'(\d{6,})\s+RUA'
|
||||
], texto_final)
|
||||
|
||||
logging.debug("TEXTO PDF:\n" + texto_final)
|
||||
|
||||
referencia = extrair_seguro([
|
||||
r'\b([A-Z]{3}/\d{4})\b',
|
||||
r'\b([A-Z]{3}\s*/\s*\d{4})\b',
|
||||
r'\b([A-Z]{3}\d{4})\b',
|
||||
r'(\d{2}/\d{4})'
|
||||
], texto_final.upper())
|
||||
|
||||
# Limpeza prévia para evitar falhas por quebra de linha ou espaços
|
||||
texto_limpo = texto_final.replace('\n', '').replace('\r', '').replace(' ', '')
|
||||
|
||||
if any(padrao in texto_limpo for padrao in ['R$***********0,00', 'R$*********0,00', 'R$*0,00']):
|
||||
valor_total = 0.0
|
||||
else:
|
||||
match_valor_total = re.search(r'R\$[*\s]*([\d\.\s]*,\d{2})', texto_final)
|
||||
valor_total = converter_valor(match_valor_total.group(1)) if match_valor_total else None
|
||||
|
||||
match_nome = re.search(r'(?<=\n)([A-Z\s]{10,})(?=\s+CNPJ/CPF:)', texto_final)
|
||||
nome = match_nome.group(1).replace('\n', ' ').strip() if match_nome else "NÃO IDENTIFICADO"
|
||||
|
||||
# Remove qualquer excesso após o nome verdadeiro
|
||||
nome = re.split(r'\b(FAZENDA|RUA|AVENIDA|SETOR|CEP|CNPJ|CPF)\b', nome, maxsplit=1, flags=re.IGNORECASE)[0].strip()
|
||||
|
||||
match_cidade_estado = re.search(r'CEP:\s*\d{8}\s+(.*?)\s+([A-Z]{2})\s+BRASIL', texto_final)
|
||||
cidade = match_cidade_estado.group(1).strip() if match_cidade_estado else "NÃO IDENTIFICADA"
|
||||
estado = match_cidade_estado.group(2).strip() if match_cidade_estado else "NÃO IDENTIFICADO"
|
||||
|
||||
match_class = re.search(r'Classificação:\s*(.*)', texto_final, re.IGNORECASE)
|
||||
classificacao = match_class.group(1).strip() if match_class else "NÃO IDENTIFICADA"
|
||||
|
||||
def extrair_tributo_linhas_separadas(nome_tributo):
|
||||
linhas = texto_final.split('\n')
|
||||
for i, linha in enumerate(linhas):
|
||||
if nome_tributo in linha.upper():
|
||||
aliq = base = valor = 0.0
|
||||
if i + 1 < len(linhas):
|
||||
aliq_match = re.search(r'([\d,]+)%', linhas[i + 1])
|
||||
if aliq_match:
|
||||
aliq = converter_valor(aliq_match.group(1)) / 100
|
||||
if i + 2 < len(linhas):
|
||||
base = converter_valor(linhas[i + 2].strip())
|
||||
if i + 3 < len(linhas):
|
||||
valor = converter_valor(linhas[i + 3].strip())
|
||||
return base, aliq, valor
|
||||
return 0.0, 0.0, 0.0
|
||||
|
||||
pis_base, pis_aliq, pis_valor = extrair_tributo_linhas_separadas('PIS/PASEP')
|
||||
icms_base, icms_aliq, icms_valor = extrair_tributo_linhas_separadas('ICMS')
|
||||
cofins_base, cofins_aliq, cofins_valor = extrair_tributo_linhas_separadas('COFINS')
|
||||
|
||||
match_consumo = re.search(r'CONSUMO\s+\d+\s+([\d.,]+)', texto_final)
|
||||
consumo = converter_valor(match_consumo.group(1)) if match_consumo else 0.0
|
||||
|
||||
match_tarifa = re.search(r'CONSUMO KWH \+ ICMS/PIS/COFINS\s+([\d.,]+)', texto_final) \
|
||||
or re.search(r'CUSTO DISP\s+([\d.,]+)', texto_final)
|
||||
tarifa = converter_valor(match_tarifa.group(1)) if match_tarifa else 0.0
|
||||
|
||||
dados = {
|
||||
'classificacao_tarifaria': classificacao,
|
||||
'nome': nome,
|
||||
'unidade_consumidora': uc,
|
||||
'cidade': cidade,
|
||||
'estado': estado,
|
||||
'referencia': referencia,
|
||||
'valor_total': valor_total,
|
||||
'pis_aliq': pis_aliq,
|
||||
'icms_aliq': icms_aliq,
|
||||
'cofins_aliq': cofins_aliq,
|
||||
'pis_valor': pis_valor,
|
||||
'icms_valor': icms_valor,
|
||||
'cofins_valor': cofins_valor,
|
||||
'pis_base': pis_base,
|
||||
'icms_base': icms_base,
|
||||
'cofins_base': cofins_base,
|
||||
'consumo': consumo,
|
||||
'tarifa': tarifa,
|
||||
'nota_fiscal': nota_fiscal,
|
||||
'data_processamento': datetime.now(),
|
||||
'distribuidora': 'Equatorial Goiás'
|
||||
}
|
||||
|
||||
campos_obrigatorios = ['nome', 'unidade_consumidora', 'referencia', 'nota_fiscal']
|
||||
faltantes = [campo for campo in campos_obrigatorios if not dados.get(campo)]
|
||||
if valor_total is None and not any(p in texto_limpo for p in ['R$***********0,00', 'R$*********0,00', 'R$*0,00']):
|
||||
faltantes.append('valor_total')
|
||||
|
||||
if faltantes:
|
||||
raise ValueError(f"Campos obrigatórios faltantes: {', '.join(faltantes)}")
|
||||
|
||||
return dados
|
||||
|
||||
|
||||
Reference in New Issue
Block a user