FASE 1: AUTOMAÇÃO DAS AÇÕES COLETIVAS
Objetivo: Calcular valores de pelo menos 25 funcionários por dia ou 3 por hora
Método: Criar sistemas e automações que aumentem a velocidade de produção em 12 vezes
Fluxograma:
Colocar contracheques (PDFs) na pasta pdfs_trabalhador (C:\= LEO RAYZER SSD\= RAYZER DEV\Ação Coletiva – Automação\pdfs_trabalhador)
Gerar planilhas pelo terminal do VS Code:
python gerar_csvs_pjecalc.py
Importar planilhas no PJe-Calc
FASE 1: AUTOMAÇÃO DAS AÇÕES COLETIVAS
Objetivo: Calcular valores de pelo menos 25 funcionários por dia ou 3 por hora
Método: Criar sistemas e automações que aumentem a velocidade de produção em 12 vezes
Fluxograma:
Colocar contracheques (PDFs) na pasta pdfs_trabalhador (C:\= LEO RAYZER SSD\= RAYZER DEV\Ação Coletiva – Automação\pdfs_trabalhador)
Gerar planilhas pelo terminal do VS Code:
python gerar_csvs_pjecalc.py
Importar planilhas no PJe-Calc
Método XML - A Forma CORRETA e Profissional
A DESCOBERTA QUE MUDA TUDO
PJe-Calc possui função NATIVA de importação de XML!
Isso significa:
- ❌ NÃO precisa de Selenium/Playwright
- ❌ NÃO precisa de automação de navegador
- ❌ NÃO precisa de “Calc Machine” ou ferramentas pagas
- ✅ SIM: Python gera XML → PJe-Calc importa pronto!
📊 COMPARAÇÃO: MÉTODOS DE AUTOMAÇÃO
| Método | Confiabilidade | Complexidade | Velocidade | Recomendado? |
|---|---|---|---|---|
| XML Nativo | ⭐⭐⭐⭐⭐ | ⭐⭐ Média | ⭐⭐⭐⭐⭐ | ✅ IDEAL |
| Playwright/Selenium | ⭐⭐ Baixa | ⭐⭐⭐⭐⭐ Alta | ⭐⭐⭐ | ❌ Desnecessário |
| Manual Total | ⭐⭐⭐⭐ | ⭐ Fácil | ⭐ Lento | ❌ Ineficiente |
VEREDICTO: Use XML! É o método oficial e profissional.
🛠️ FERRAMENTAS DEFINITIVAS (Só as MELHORES e GRÁTIS)
1. Python 3.12 (Linguagem de programação)
- Por quê: Mais recente e estável
- Alternativas: Nenhuma melhor para automação
- 📥 Download: https://python.org
- ✅ Melhor escolha absoluta
2. uv (Gerenciador de pacotes Python)
- Por quê: 10-100x mais rápido que pip
- Alternativa ao pip tradicional
- 📥 Instalar:
pip install uv - ✅ Revolucionário e gratuito
3. pdfplumber (Extração de PDF)
- Por quê: Melhor para PDFs com texto
- Alternativas analisadas:
- PyPDF2: Menos confiável
- PyMuPDF: Mais complexo
- pdfminer.six: Mais lento
- ✅ pdfplumber vence
4. polars (Manipulação de dados)
- Por quê: 5-10x mais rápido que pandas
- Alternativa ao pandas
- Sintaxe mais moderna
- ✅ Melhor que pandas
5. lxml (Geração de XML)
- Por quê: Mais rápido e confiável
- Alternativas:
- xml.etree: Mais lento
- xmltodict: Menos control
e
- ✅ lxml é superior
6. Rye (Gerenciador de projeto Python)
- Por quê: Gerencia tudo automaticamente
- Alternativas:
- poetry: Mais lento
- pipenv: Menos features
- ✅ Rye é o futuro
7. Ruff (Linter/Formatter)
- Por quê: 10-100x mais rápido que pylint/black
- Substitui: pylint + black + isort
- ✅ Ferramenta obrigatória
8. VS Code (Editor)
- Por quê: Mais completo e gratuito
- Alternativas:
- PyCharm Community: Mais pesado
- Sublime: Menos features
- Vim/Neovim: Curva de aprendizado
- ✅ VS Code é ideal para iniciantes
FASES (1-4)
FASE 1: Fundamentos (Dias 1-3)
- Python + uv + Ambiente
- Extração básica de PDF
- Conceitos de XML
FASE 2: Extração Avançada (Dias 4-7)
- Sistema robusto de extração
- Validações
- Polars para dados
FASE 3: Geração de XML (Dias 8-12)
- Estrutura XML do PJe-Calc
- Gerador automático
- Testes com PJe-Calc real
FASE 4: Sistema Completo (Dias 13-15)
- Pipeline completo
- Testes finais
- Documentação
🚀 DIA 1: SETUP MODERNO
Instalação Correta
# 1. Instalar Python 3.12
# Download: https://python.org (marcar "Add to PATH")
# 2. Verificar instalação
python --version # Deve mostrar 3.12.x
# 3. Instalar uv (substituir pip)
pip install uv
# 4. Instalar ferramentas modernas
uv pip install ruff
# 5. Instalar bibliotecas do projeto
uv pip install pdfplumber polars lxml openpyxl
Configurar VS Code
Extensões essenciais:
- Python (Microsoft)
- Ruff (Charliermarsh)
- Even Better TOML
- XML Tools
Primeiro Script: Teste de Ambiente
# Arquivo: teste_ambiente.py
import sys
print(f"✓ Python {sys.version}")
import pdfplumber
print("✓ pdfplumber instalado")
import polars as pl
print("✓ polars instalado")
import lxml
print("✓ lxml instalado")
print("\n🎉 Ambiente configurado com sucesso!")
Executar: python teste_ambiente.py
📚 DIAS 2-3: PYTHON ESSENCIAL
Conceitos mínimos necessários
# 1. VARIÁVEIS E TIPOS
nome = "Willian" # string
matricula = 2452583 # int
salario = 3019.40 # float
tem_periculosidade = True # bool
# 2. DICIONÁRIOS (essencial para XML)
trabalhador = {
"nome": "Willian Aguiar",
"matricula": "2452583",
"salario_basico": 3019.40,
"adicional_periculosidade": 905.82
}
print(trabalhador["nome"]) # Willian Aguiar
# 3. LISTAS
rubricas = ["Salário Básico", "Periculosidade", "INSS"]
# 4. LOOPS
for rubrica in rubricas:
print(rubrica)
# 5. FUNÇÕES
def calcular_inss(salario, aliquota=11):
return salario * (aliquota / 100)
inss = calcular_inss(3019.40)
print(f"INSS: R$ {inss:.2f}")
# 6. CONDICIONAIS
if salario > 3000:
print("Salário acima de R$ 3.000")
else:
print("Salário até R$ 3.000")
Exercício Prático: Extrator Simples
# Arquivo: extrator_basico.py
import pdfplumber
import re
def extrair_contracheque(pdf_path):
"""Extrai dados básicos do contracheque"""
with pdfplumber.open(pdf_path) as pdf:
texto = pdf.pages[0].extract_text()
# Extrair matrícula
match = re.search(r'(\d{7})\s+Matrícula', texto)
matricula = match.group(1) if match else None
# Extrair nome
match = re.search(r'Nome\s+\d+\s+([A-ZÀ-Ú\s]+)\s+Matrícula', texto)
nome = match.group(1).strip() if match else None
# Extrair salário
match = re.search(r'Salário Básico\s+\d+\s+R\$\s+([\d.,]+)', texto)
salario = match.group(1) if match else None
return {
"matricula": matricula,
"nome": nome,
"salario_basico": salario
}
# Testar
dados = extrair_contracheque("seu_contracheque.pdf")
print(dados)
🔍 DIAS 4-7: EXTRAÇÃO PROFISSIONAL COM POLARS
Por que Polars em vez de Pandas?
# PANDAS (antigo, lento)
import pandas as pd
df = pd.read_csv("dados.csv")
resultado = df.groupby("categoria").agg({"valor": "sum"})
# POLARS (moderno, 10x mais rápido)
import polars as pl
df = pl.read_csv("dados.csv")
resultado = df.group_by("categoria").agg(pl.col("valor").sum())
Polars é:
- ✅ 5-10x mais rápido
- ✅ Usa menos memória
- ✅ Sintaxe mais clara
- ✅ Paralelização automática
Sistema Completo de Extração
# Arquivo: extrator_completo.py
import pdfplumber
import polars as pl
import re
from pathlib import Path
from dataclasses import dataclass
@dataclass
class DadosContracheque:
"""Estrutura de dados do contracheque"""
arquivo: str
matricula: str | None = None
nome: str | None = None
mes_ano: str | None = None
salario_basico: float = 0.0
adicional_periculosidade: float = 0.0
adicional_noturno: float = 0.0
adicional_hra: float = 0.0
total_proventos: float = 0.0
inss: float = 0.0
ir: float = 0.0
total_descontos: float = 0.0
total_liquido: float = 0.0
status: str = "pendente"
def converter_valor_br(texto: str) -> float:
"""Converte R$ 1.234,56 para 1234.56"""
if not texto:
return 0.0
try:
return float(texto.replace('.', '').replace(',', '.'))
except:
return 0.0
PADROES_REGEX = {
'matricula': r'(\d{7})\s+Matrícula',
'nome': r'Nome\s+\d+\s+([A-ZÀ-Ú\s]+)\s+Matrícula',
'mes_ano': r'(\d{2}/\d{4})\s+Mês/Ano',
'salario_basico': r'0001\s+Salário Básico\s+\d+\s+R\$\s+([\d.,]+)',
'adicional_periculosidade': r'0201\s+Adicional Periculosidade\s+\d+\s+R\$\s+([\d.,]+)',
'adicional_noturno': r'1061\s+Adicional Trab\.?\s*Noturno\s+\d+\s+R\$\s+([\d.,]+)',
'adicional_hra': r'1062\s+Adicional HRA\s+\d+\s+R\$\s+([\d.,]+)',
'total_proventos': r'Total de Proventos\s+\.+\s+R\$\s+([\d.,]+)',
'inss': r'0923\s+Contribuição INSS\s+[\d,]+\s+R\$\s+([\d.,]+)',
'ir': r'0927\s+Imposto de Renda\s+[\d,]+\s+R\$\s+([\d.,]+)',
'total_descontos': r'Total de Descontos\s+\.+\s+R\$\s+([\d.,]+)',
'total_liquido': r'Total Líquido\s+R\$\s+([\d.,]+)',
}
def extrair_contracheque(pdf_path: Path) -> DadosContracheque:
"""Extrai dados completos de um contracheque"""
dados = DadosContracheque(arquivo=pdf_path.name)
try:
with pdfplumber.open(pdf_path) as pdf:
texto = pdf.pages[0].extract_text()
if not texto:
dados.status = "erro: PDF vazio"
return dados
# Extrair cada campo
for campo, padrao in PADROES_REGEX.items():
match = re.search(padrao, texto)
if match:
valor = match.group(1)
# Converter valores monetários
if campo in ['salario_basico', 'adicional_periculosidade',
'adicional_noturno', 'adicional_hra',
'total_proventos', 'inss', 'ir',
'total_descontos', 'total_liquido']:
valor = converter_valor_br(valor)
setattr(dados, campo, valor)
# Validar
if dados.matricula and dados.total_liquido > 0:
dados.status = "sucesso"
else:
dados.status = "dados incompletos"
except Exception as e:
dados.status = f"erro: {str(e)}"
return dados
def processar_pasta(pasta_pdfs: str) -> pl.DataFrame:
"""Processa todos os PDFs de uma pasta"""
pasta = Path(pasta_pdfs)
pdfs = list(pasta.glob("*.pdf"))
print(f"📄 Encontrados {len(pdfs)} PDFs\n")
resultados = []
for i, pdf in enumerate(pdfs, 1):
print(f"[{i}/{len(pdfs)}] {pdf.name[:50]}", end=" ... ")
dados = extrair_contracheque(pdf)
# Converter para dict
resultados.append(dados.__dict__)
status_symbol = "✓" if dados.status == "sucesso" else "✗"
print(f"{status_symbol} {dados.status}")
# Criar DataFrame com Polars
df = pl.DataFrame(resultados)
# Salvar
df.write_excel("contracheques_extraidos.xlsx")
# Estatísticas
total = len(df)
sucessos = df.filter(pl.col("status") == "sucesso").height
print(f"\n📊 RESULTADO:")
print(f" Total: {total}")
print(f" ✓ Sucessos: {sucessos} ({sucessos/total*100:.1f}%)")
print(f" ✗ Erros: {total - sucessos}")
return df
# Executar
if __name__ == "__main__":
df = processar_pasta(".")
🎯 DIAS 8-12: GERAÇÃO DE XML PARA PJe-CALC
DESCOBERTA CRÍTICA: Estrutura XML do PJe-Calc
O PJe-Calc aceita importação de XML com estrutura específica.
Primeiro, precisamos descobrir o formato correto:
- Abra o PJe-Calc Cidadão
- Crie um cálculo manualmente (simples)
- Exporte como XML (menu Cálculo → Exportar)
- Abra o XML no VS Code
- Analise a estrutura
Exemplo típico da estrutura:
<?xml version="1.0" encoding="UTF-8"?>
<calculo>
<dadosProcesso>
<numeroProcesso>0000000-00.0000.0.00.0000</numeroProcesso>
<reclamante>
<nome>WILLIAN AGUIAR DOS SANTOS</nome>
<cpf>000.000.000-00</cpf>
</reclamante>
<reclamado>
<nome>PETRÓLEO BRASILEIRO S.A.</nome>
<cnpj>00.000.000/0000-00</cnpj>
</reclamado>
</dadosProcesso>
<dadosContratuais>
<dataAdmissao>2010-01-01</dataAdmissao>
<dataDispensa>2019-12-31</dataDispensa>
<cargaHoraria>168</cargaHoraria>
</dadosContratuais>
<verbas>
<verba>
<codigo>SALARIO_BASICO</codigo>
<descricao>Salário Básico</descricao>
<valor>3019.40</valor>
<tipo>PROVENTO</tipo>
</verba>
<verba>
<codigo>ADICIONAL_PERICULOSIDADE</codigo>
<descricao>Adicional de Periculosidade</descricao>
<valor>905.82</valor>
<tipo>PROVENTO</tipo>
</verba>
<verba>
<codigo>INSS</codigo>
<descricao>Contribuição INSS</descricao>
<valor>642.34</valor>
<tipo>DESCONTO</tipo>
</verba>
</verbas>
</calculo>
Gerador de XML com lxml
# Arquivo: gerador_xml.py
from lxml import etree
from datetime import datetime
import polars as pl
class GeradorXMLPJeCalc:
"""Gera XML compatível com PJe-Calc"""
def __init__(self):
self.namespace = None # Ajustar se PJe-Calc usar namespace
def gerar_xml(self, dados: dict, dados_adicionais: dict) -> str:
"""
Gera XML de um trabalhador
dados: dict com dados extraídos do PDF
dados_adicionais: dict com info manual (carga horária, datas, etc)
"""
# Criar elemento raiz
root = etree.Element("calculo")
root.set("versao", "2.13.2") # Versão do PJe-Calc
# Dados do processo
processo = etree.SubElement(root, "dadosProcesso")
numero_processo = etree.SubElement(processo, "numeroProcesso")
numero_processo.text = dados_adicionais.get("numero_processo", "")
# Reclamante
reclamante = etree.SubElement(processo, "reclamante")
nome_rec = etree.SubElement(reclamante, "nome")
nome_rec.text = dados.get("nome", "").upper()
cpf_rec = etree.SubElement(reclamante, "cpf")
cpf_rec.text = dados_adicionais.get("cpf", "")
matricula = etree.SubElement(reclamante, "matricula")
matricula.text = str(dados.get("matricula", ""))
# Reclamado
reclamado = etree.SubElement(processo, "reclamado")
nome_reclamado = etree.SubElement(reclamado, "nome")
nome_reclamado.text = "PETRÓLEO BRASILEIRO S.A."
cnpj = etree.SubElement(reclamado, "cnpj")
cnpj.text = "33.000.167/0001-01" # CNPJ Petrobras
# Dados contratuais
contratuais = etree.SubElement(root, "dadosContratuais")
data_admissao = etree.SubElement(contratuais, "dataAdmissao")
data_admissao.text = dados_adicionais.get("data_admissao", "")
data_dispensa = etree.SubElement(contratuais, "dataDispensa")
data_dispensa.text = dados_adicionais.get("data_dispensa", "")
carga_horaria = etree.SubElement(contratuais, "cargaHoraria")
carga_horaria.text = str(dados_adicionais.get("carga_horaria", 168))
cargo = etree.SubElement(contratuais, "cargo")
cargo.text = dados_adicionais.get("cargo", "")
# Verbas
verbas = etree.SubElement(root, "verbas")
# Mapear rubricas do contracheque para verbas do PJe-Calc
mapeamento_verbas = {
'salario_basico': {
'codigo': 'SALARIO_BASICO',
'descricao': 'Salário Básico',
'tipo': 'PROVENTO'
},
'adicional_periculosidade': {
'codigo': 'ADICIONAL_PERICULOSIDADE',
'descricao': 'Adicional de Periculosidade',
'tipo': 'PROVENTO'
},
'adicional_noturno': {
'codigo': 'ADICIONAL_NOTURNO',
'descricao': 'Adicional Noturno',
'tipo': 'PROVENTO'
},
'adicional_hra': {
'codigo': 'ADICIONAL_HRA',
'descricao': 'Adicional HRA',
'tipo': 'PROVENTO'
},
'inss': {
'codigo': 'INSS',
'descricao': 'Contribuição INSS',
'tipo': 'DESCONTO'
},
'ir': {
'codigo': 'IRRF',
'descricao': 'Imposto de Renda',
'tipo': 'DESCONTO'
}
}
# Adicionar cada verba
for campo, config in mapeamento_verbas.items():
valor = dados.get(campo, 0)
if valor and valor > 0:
verba = etree.SubElement(verbas, "verba")
codigo = etree.SubElement(verba, "codigo")
codigo.text = config['codigo']
descricao = etree.SubElement(verba, "descricao")
descricao.text = config['descricao']
valor_elem = etree.SubElement(verba, "valor")
valor_elem.text = f"{valor:.2f}"
tipo = etree.SubElement(verba, "tipo")
tipo.text = config['tipo']
# Converter para string XML
xml_string = etree.tostring(
root,
pretty_print=True,
xml_declaration=True,
encoding='UTF-8'
).decode('utf-8')
return xml_string
def gerar_lote(self, df: pl.DataFrame, dados_adicionais_df: pl.DataFrame,
pasta_saida: str = "xmls_pjecalc"):
"""
Gera XMLs para múltiplos trabalhadores
df: DataFrame com dados extraídos
dados_adicionais_df: DataFrame com dados manuais (CPF, datas, etc)
"""
from pathlib import Path
pasta = Path(pasta_saida)
pasta.mkdir(exist_ok=True)
print(f"\n🔄 Gerando XMLs...\n")
# Join dos dataframes
df_completo = df.join(
dados_adicionais_df,
on="matricula",
how="left"
)
gerados = 0
erros = 0
for row in df_completo.iter_rows(named=True):
try:
matricula = row['matricula']
# Gerar XML
xml = self.gerar_xml(row, row)
# Salvar arquivo
arquivo_xml = pasta / f"calculo_{matricula}.xml"
with open(arquivo_xml, 'w', encoding='utf-8') as f:
f.write(xml)
print(f"✓ {arquivo_xml.name}")
gerados += 1
except Exception as e:
print(f"✗ Erro na matrícula {row.get('matricula', 'N/A')}: {e}")
erros += 1
print(f"\n📊 RESULTADO:")
print(f" ✓ XMLs gerados: {gerados}")
print(f" ✗ Erros: {erros}")
print(f" 📁 Pasta: {pasta.absolute()}")
return gerados
# Exemplo de uso
if __name__ == "__main__":
# Carregar dados extraídos
df = pl.read_excel("contracheques_extraidos.xlsx")
# Carregar dados adicionais (você preenche manualmente)
dados_adicionais = pl.read_csv("dados_adicionais.csv")
# Gerar XMLs
gerador = GeradorXMLPJeCalc()
gerador.gerar_lote(df, dados_adicionais)
Arquivo de Dados Adicionais (você preenche)
# dados_adicionais.csv
matricula,cpf,numero_processo,data_admissao,data_dispensa,carga_horaria,cargo
2452583,000.000.000-00,0000000-00.2020.5.01.0000,2010-01-01,2019-12-31,168,Técnico
gerar_csvs_pjecalc.py 2025-11-23
import pdfplumber
import polars as pl
import re
from pathlib import Path
def converter_valor_br(texto):
"""Converte R$ 1.234,56 para float 1234.56"""
if not texto:
return 0.0
return float(texto.replace('.', '').replace(',', '.'))
# Dicionário com TODOS os padrões de rubricas
PADROES_RUBRICAS = {
'Salario_Basico': r'0001\s+Salário Básico\s+\d+\s+R\$\s+([\d.,]+)',
'Adicional_Periculosidade': r'0201\s+Adicional Periculosidade\s+\d+\s+R\$\s+([\d.,]+)',
'Adicional_Noturno': r'1061\s+Adicional Trab\.\s+Noturno\s+\d+\s+R\$\s+([\d.,]+)',
'Adicional_HRA': r'1062\s+Adicional HRA\s+\d+\s+R\$\s+([\d.,]+)',
'Complemento_RMNR': r'0192\s+Complemento da RMNR\s+R\$\s+([\d.,]+)',
'Anuenio': r'0015\s+Anuênio\s+[\d,]+\s+R\$\s+([\d.,]+)',
'Ferias_Petros': r'1079\s+Férias C/Petros\s+\d+\s+R\$\s+([\d.,]+)',
'Gratificacao_Ferias': r'0403\s+Gratificação de Ferias\s+\d+\s+R\$\s+([\d.,]+)',
'Media_Hora_Extra': r'0395\s+Média Hora Extra\s+\d+\s+R\$\s+([\d.,]+)',
'Media_Rep_Sem': r'0392\s+Média Rep\.\s+Sem\.\s+Rem HE\s+\d+\s+R\$\s+([\d.,]+)',
'Compl_Cand_Interno': r'0075\s+Compl Re Cand Interno C/P\s+\d+\s+R\$\s+([\d.,]+)',
'HE_Troca_Turno': r'0528\s+HE Troca de Turno\s+\d+\s+R\$\s+([\d.,]+)',
'RSR_HE_Troca': r'0532\s+RSR-HE Troca de Turno\s+R\$\s+([\d.,]+)',
'Imposto_Renda': r'0927\s+Imposto de Renda\s+[\d,]+\s+R\$\s+([\d.,]+)',
'IR_Ferias': r'0926\s+Imp Renda Férias\s+[\d,]+\s+R\$\s+([\d.,]+)',
'INSS': r'0923\s+Contribuição INSS\s+[\d,]+\s+R\$\s+([\d.,]+)',
'Petros': r'0985\s+Petros II Contr Regular\s+\d+\s+R\$\s+([\d.,]+)',
'Desc_Adiant_Sal': r'5636\s+Desc Adiant sal sem IR\s+R\$\s+([\d.,]+)',
'Desc_Adto_Ferias': r'5118\s+Desc Adto Férias\s+\d+\s+R\$\s+([\d.,]+)',
'Desc_Adto_GFE': r'5119\s+Desc Adto GFE\s+\d+\s+R\$\s+([\d.,]+)',
'AMS_Grande_Risco': r'0980\s+AMS Grande Risco\s+[\d,]+\s+R\$\s+([\d.,]+)',
'CA_Sindipetro': r'1408\s+CA Sindipetro Caxias\s+R\$\s+([\d.,]+)',
'Sindipetro': r'0827\s+Sindipetro Caxias\s+\d+\s+R\$\s+([\d.,]+)',
'Dif_AMS': r'2980\s+Dif AMS Grande Risco\s+[\d,]+\s+R\$\s+([\d.,]+)',
'Falta_Nao_Just': r'0446\s+Falta Não Just C/Pet\s+[\d,]+\s+R\$\s+([\d.,]+)',
'Beneficio_Farmacia': r'0569\s+Novo Benefício Farmácia\s+[\d,]+\s+R\$\s+([\d.,]+)',
}
def extrair_mes_ano_e_rubricas(pdf_path):
"""Extrai mês/ano e todas as rubricas de um PDF"""
try:
with pdfplumber.open(pdf_path) as pdf:
texto = pdf.pages[0].extract_text()
if not texto:
return None, {}
# Extrair Mês/Ano
match = re.search(r'(\d{2}/\d{4})', texto)
mes_ano = match.group(1) if match else None
if not mes_ano:
return None, {}
# Extrair todas as rubricas
rubricas_encontradas = {}
for nome_rubrica, padrao in PADROES_RUBRICAS.items():
match = re.search(padrao, texto)
if match:
valor = converter_valor_br(match.group(1))
if valor > 0:
rubricas_encontradas[nome_rubrica] = valor
return mes_ano, rubricas_encontradas
except Exception as e:
print(f" ✗ Erro: {e}")
return None, {}
def processar_trabalhador():
"""Processa todos os PDFs e gera CSVs para o PJe-Calc"""
# Caminhos
pasta_pdfs = Path("pdfs_trabalhador")
pasta_saida = Path("csvs_gerados")
# Verificar se pasta de PDFs existe
if not pasta_pdfs.exists():
print(f"❌ ERRO: Pasta '{pasta_pdfs}' não encontrada!")
print(f"📁 Crie a pasta e coloque os PDFs dentro.")
return
# Buscar PDFs
pdfs = sorted(list(pasta_pdfs.glob("*.pdf")))
if not pdfs:
print(f"⚠️ Nenhum PDF encontrado em: {pasta_pdfs}")
print(f"📁 Coloque os PDFs na pasta e tente novamente.")
return
print(f"\n{'='*70}")
print(f" GERADOR DE CSVS PARA PJe-CALC")
print(f"{'='*70}")
print(f"\n📄 Encontrados {len(pdfs)} PDFs")
print(f"🔄 Extraindo rubricas...\n")
# Dicionário: {rubrica: [(mes_ano, valor), ...]}
dados_por_rubrica = {}
# Processar cada PDF
for i, pdf in enumerate(pdfs, 1):
print(f"[{i:3d}/{len(pdfs)}] {pdf.name[:50]:<50}", end=" ")
mes_ano, rubricas = extrair_mes_ano_e_rubricas(pdf)
if mes_ano:
print(f"✓ {mes_ano} - {len(rubricas)} rubricas")
# Adicionar ao dicionário
for nome_rubrica, valor in rubricas.items():
if nome_rubrica not in dados_por_rubrica:
dados_por_rubrica[nome_rubrica] = []
dados_por_rubrica[nome_rubrica].append({
'MES_ANO': mes_ano,
'VALOR': valor
})
else:
print("✗ Mês/Ano não encontrado")
# Criar pasta de saída
pasta_saida.mkdir(exist_ok=True)
print(f"\n{'='*70}")
print(f"📊 GERANDO CSVS...")
print(f"{'='*70}\n")
# Gerar 1 CSV por rubrica
total_csvs = 0
for nome_rubrica, dados in dados_por_rubrica.items():
# Ordenar por mês/ano
dados_ordenados = sorted(dados, key=lambda x: x['MES_ANO'])
# Adicionar colunas fixas
for item in dados_ordenados:
item['FGTS'] = 'N'
item['FGTS_REC'] = 'N'
item['CONTRIBUICAO_SOCIAL'] = 'N'
item['CONTRIBUICAO_SOCIAL_REC'] = 'N'
# Criar DataFrame
df = pl.DataFrame(dados_ordenados)
# Reordenar colunas
df = df.select(['MES_ANO', 'VALOR', 'FGTS', 'FGTS_REC', 'CONTRIBUICAO_SOCIAL', 'CONTRIBUICAO_SOCIAL_REC'])
# Formatar valores com 2 casas decimais
df = df.with_columns([
pl.col('VALOR').round(2)
])
# Nome do arquivo
nome_arquivo = f"{nome_rubrica}.csv"
caminho_arquivo = pasta_saida / nome_arquivo
# Salvar CSV manualmente para garantir formato correto
with open(caminho_arquivo, 'w', encoding='utf-8') as f:
# Escrever cabeçalho
f.write('MES_ANO;VALOR;FGTS;FGTS_REC;CONTRIBUICAO_SOCIAL;CONTRIBUICAO_SOCIAL_REC\n')
# Escrever dados
for row in df.iter_rows():
mes_ano, valor, fgts, fgts_rec, contrib_social, contrib_social_rec = row
# Formatar valor com vírgula (padrão brasileiro)
valor_formatado = f"{valor:.2f}".replace('.', ',')
f.write(f'{mes_ano};{valor_formatado};{fgts};{fgts_rec};{contrib_social};{contrib_social_rec}\n')
print(f"✓ {nome_arquivo:<40} ({len(dados_ordenados)} meses)")
total_csvs += 1
print(f"\n{'='*70}")
print(f"✅ CONCLUÍDO!")
print(f"{'='*70}")
print(f"\n📊 Estatísticas:")
print(f" PDFs processados: {len(pdfs)}")
print(f" Rubricas encontradas: {len(dados_por_rubrica)}")
print(f" CSVs gerados: {total_csvs}")
print(f"\n📁 Arquivos salvos em: {pasta_saida.absolute()}")
if __name__ == "__main__":
processar_trabalhador()
Script Básico de Extração:
import pdfplumber
import pandas as pd
import re
def extrair_contracheque(caminho_pdf):
“””Extrai dados do contracheque Petrobras”””
dados = {
‘matricula’: None,
‘nome’: None,
‘mes_ano’: None,
‘proventos’: {},
‘descontos’: {},
‘total_liquido’: None
}
with pdfplumber.open(caminho_pdf) as pdf:
texto = pdf.pages[0].extract_text()
# Extrair informações básicas
if match := re.search(r'(\d{7})\s+Matrícula’, texto):
dados[‘matricula’] = match.group(1)
if match := re.search(r’Nome\s+(\d+)\s+([A-Z\s]+)\s+Matrícula’, texto):
dados[‘nome’] = match.group(2).strip()
# Extrair rubricas (exemplo para Adicional Periculosidade)
if match := re.search(r’0201\s+Adicional Periculosidade\s+\d+\s+R\$\s+([\d.,]+)’, texto):
valor = match.group(1).replace(‘.’, ”).replace(‘,’, ‘.’)
dados[‘proventos’][‘adicional_periculosidade’] = float(valor)
# Total líquido
if match := re.search(r’Total Líquido\s+R\$\s+([\d.,]+)’, texto):
valor = match.group(1).replace(‘.’, ”).replace(‘,’, ‘.’)
dados[‘total_liquido’] = float(valor)
return dados
# Processar múltiplos PDFs
import os
resultados = []
pasta_pdfs = “caminho/para/pasta/com/pdfs”
for arquivo in os.listdir(pasta_pdfs):
if arquivo.endswith(‘.pdf’):
caminho = os.path.join(pasta_pdfs, arquivo)
try:
dados = extrair_contracheque(caminho)
resultados.append(dados)
print(f”✓ Processado: {arquivo}”)
except Exception as e:
print(f”✗ ERRO em {arquivo}: {e}”)
# Exportar para Excel para revisão
df = pd.DataFrame(resultados)
df.to_excel(‘contracheques_extraidos.xlsx’, index=False)
Script Básico de Extração:
import pdfplumber
import pandas as pd
import re
def extrair_contracheque(caminho_pdf):
“””Extrai dados do contracheque Petrobras”””
dados = {
‘matricula’: None,
‘nome’: None,
‘mes_ano’: None,
‘proventos’: {},
‘descontos’: {},
‘total_liquido’: None
}
with pdfplumber.open(caminho_pdf) as pdf:
texto = pdf.pages[0].extract_text()
# Extrair informações básicas
if match := re.search(r'(\d{7})\s+Matrícula’, texto):
dados[‘matricula’] = match.group(1)
if match := re.search(r’Nome\s+(\d+)\s+([A-Z\s]+)\s+Matrícula’, texto):
dados[‘nome’] = match.group(2).strip()
# Extrair rubricas (exemplo para Adicional Periculosidade)
if match := re.search(r’0201\s+Adicional Periculosidade\s+\d+\s+R\$\s+([\d.,]+)’, texto):
valor = match.group(1).replace(‘.’, ”).replace(‘,’, ‘.’)
dados[‘proventos’][‘adicional_periculosidade’] = float(valor)
# Total líquido
if match := re.search(r’Total Líquido\s+R\$\s+([\d.,]+)’, texto):
valor = match.group(1).replace(‘.’, ”).replace(‘,’, ‘.’)
dados[‘total_liquido’] = float(valor)
return dados
# Processar múltiplos PDFs
import os
resultados = []
pasta_pdfs = “caminho/para/pasta/com/pdfs”
for arquivo in os.listdir(pasta_pdfs):
if arquivo.endswith(‘.pdf’):
caminho = os.path.join(pasta_pdfs, arquivo)
try:
dados = extrair_contracheque(caminho)
resultados.append(dados)
print(f”✓ Processado: {arquivo}”)
except Exception as e:
print(f”✗ ERRO em {arquivo}: {e}”)
# Exportar para Excel para revisão
df = pd.DataFrame(resultados)
df.to_excel(‘contracheques_extraidos.xlsx’, index=False)
⚠️ AVISOS CRÍTICOS
1. RISCO ALTO: Sistema complexo para iniciante
2. TESTE EXAUSTIVO: Valide MANUALMENTE os primeiros 50 casos
3. REVISÃO OBRIGATÓRIA: Sempre revise antes de finalizar
4. BACKUP: Mantenha todos os PDFs originais
5. RESPONSABILIDADE: Erros podem ter consequências jurídicas graves
Ferramentas
Extração de dados de tabelas em PDF: Camelot, pdfplumber, PyMuPDF
Leitor e criador de planilhas Excel: openpyxl
Análise e manipulação de dados: Pandas, Polars
Workflow automation: N8N, Zapier
Workflow orchestration: Apache Airflow
Excel automation: xlwings
Sequência de Caracteres: Regex (Regular expression)
RPA (Robotic Process Automation): UiPath, TagUI, Power Automate Desktop, OpenRPA, Automation Anywhere, Robocorp, LiberRPA, Ui.Vision RPA, Robocorp
Bibliotecas de browser automation: Playwright, Selenium
GUI (Graphical User Interface) automation: PyAutoGUI, SikuliX, TagUI, Automation Anywhere
OCR: Automation Anywhere, Tesseract
Contabilidade: GnuCash
Linguagens de programação: Python, Rust, Go, Elixir
Bibliotecas Python: PyMuPDF, PyAutoGUI, Pandas, xlsxwriter
Editores de código: Virtual Studio Code, PyCharm,
Manuais Fase 1
Rayzer Dev Manual: By ChatGPT, By Claude, By Perplexity, By Grok, By Manus, By DeepSeek, By Le Chat, By Gemini
Estratégias
Links oficiais Python: The Python Tutorial, Using Python on Windows, Python 3.14.0 documentation, What’s new in Python 3.14