- Implemented TXT processor for DMED files, extracting RPPSS records. - Added XLS processor for Prosoft reports, supporting both XLS and XLSX formats. - Created a comparison tool to identify discrepancies between TXT and XLS data. - Developed a GUI using eframe for user-friendly interaction. - Included detailed README documentation for setup and usage instructions. - Added error handling for file processing and improved data extraction methods. - Implemented tests for data processing functions and utilities. - Introduced a build script for Windows resource management. - Added example scripts for basic and complete comparisons. - Included a report generation feature for discrepancies found during comparisons.
201 lines
7.6 KiB
Rust
201 lines
7.6 KiB
Rust
// Comparador completo: TXT (DMED) vs XLS (Prosoft)
|
||
|
||
use dmed::{processar_arquivo, xls::processar_xls, Pessoa};
|
||
use std::collections::{HashMap, HashSet};
|
||
|
||
#[derive(Debug)]
|
||
struct Divergencia {
|
||
cpf: String,
|
||
nome: String,
|
||
valor_txt: f64,
|
||
valor_xls: f64,
|
||
diferenca: f64,
|
||
percentual: f64,
|
||
}
|
||
|
||
fn main() -> std::io::Result<()> {
|
||
println!("═══════════════════════════════════════════════");
|
||
println!(" COMPARADOR DMED (TXT) vs PROSOFT (XLS)");
|
||
println!("═══════════════════════════════════════════════\n");
|
||
|
||
// Processa arquivo TXT (DMED)
|
||
println!("📄 Processando arquivo TXT (DMED)...");
|
||
let arquivo_txt = "LfsIssDmed008425.txt";
|
||
let pessoas_txt = processar_arquivo(arquivo_txt)?;
|
||
println!(" ✓ {} registros encontrados\n", pessoas_txt.len());
|
||
|
||
// Processa arquivo XLS (Prosoft)
|
||
println!("📊 Processando arquivo XLS (Prosoft)...");
|
||
let arquivo_xls = "RELATORIO_PROSOFT_MOD_51.xls";
|
||
let pessoas_xls = match processar_xls(arquivo_xls) {
|
||
Ok(p) => {
|
||
println!(" ✓ {} registros únicos encontrados\n", p.len());
|
||
p
|
||
},
|
||
Err(e) => {
|
||
println!(" ✗ Erro ao processar XLS: {}", e);
|
||
println!(" ℹ️ Coloque o arquivo RELATORIO_PROSOFT_MOD_51.xls na pasta do projeto\n");
|
||
return Ok(());
|
||
}
|
||
};
|
||
|
||
// Cria mapas para comparação
|
||
let mapa_txt: HashMap<&str, &Pessoa> = pessoas_txt
|
||
.iter()
|
||
.map(|p| (p.cpf.as_str(), p))
|
||
.collect();
|
||
|
||
let mapa_xls: HashMap<&str, &Pessoa> = pessoas_xls
|
||
.iter()
|
||
.map(|p| (p.cpf.as_str(), p))
|
||
.collect();
|
||
|
||
// Conjunto de todos os CPFs
|
||
let todos_cpfs: HashSet<&str> = mapa_txt.keys()
|
||
.chain(mapa_xls.keys())
|
||
.copied()
|
||
.collect();
|
||
|
||
println!("═══════════════════════════════════════════════");
|
||
println!(" ANÁLISE");
|
||
println!("═══════════════════════════════════════════════\n");
|
||
|
||
// Estatísticas
|
||
let cpfs_ambos = todos_cpfs.iter()
|
||
.filter(|cpf| mapa_txt.contains_key(**cpf) && mapa_xls.contains_key(**cpf))
|
||
.count();
|
||
|
||
let cpfs_so_txt = todos_cpfs.iter()
|
||
.filter(|cpf| mapa_txt.contains_key(**cpf) && !mapa_xls.contains_key(**cpf))
|
||
.count();
|
||
|
||
let cpfs_so_xls = todos_cpfs.iter()
|
||
.filter(|cpf| !mapa_txt.contains_key(**cpf) && mapa_xls.contains_key(**cpf))
|
||
.count();
|
||
|
||
println!("📊 Estatísticas:");
|
||
println!(" Total de CPFs únicos: {}", todos_cpfs.len());
|
||
println!(" Presentes em ambos: {}", cpfs_ambos);
|
||
println!(" Apenas no TXT: {}", cpfs_so_txt);
|
||
println!(" Apenas no XLS: {}", cpfs_so_xls);
|
||
|
||
// Encontra divergências
|
||
let mut divergencias = Vec::new();
|
||
let margem_tolerancia = 0.01; // R$ 0,01
|
||
|
||
for cpf in todos_cpfs.iter() {
|
||
if let (Some(p_txt), Some(p_xls)) = (mapa_txt.get(*cpf), mapa_xls.get(*cpf)) {
|
||
let diferenca = (p_txt.valor - p_xls.valor).abs();
|
||
|
||
if diferenca > margem_tolerancia {
|
||
let percentual = if p_xls.valor != 0.0 {
|
||
((p_txt.valor - p_xls.valor) / p_xls.valor) * 100.0
|
||
} else {
|
||
0.0
|
||
};
|
||
|
||
divergencias.push(Divergencia {
|
||
cpf: cpf.to_string(),
|
||
nome: p_txt.nome.clone(),
|
||
valor_txt: p_txt.valor,
|
||
valor_xls: p_xls.valor,
|
||
diferenca,
|
||
percentual,
|
||
});
|
||
}
|
||
}
|
||
}
|
||
|
||
// Ordena divergências por diferença (maior primeiro)
|
||
divergencias.sort_by(|a, b| b.diferenca.abs().partial_cmp(&a.diferenca.abs()).unwrap());
|
||
|
||
println!("\n📈 Divergências de valores:");
|
||
println!(" Total: {} registros com diferenças\n", divergencias.len());
|
||
|
||
if !divergencias.is_empty() {
|
||
println!("═══════════════════════════════════════════════");
|
||
println!(" TOP 10 DIVERGÊNCIAS");
|
||
println!("═══════════════════════════════════════════════\n");
|
||
|
||
for (i, div) in divergencias.iter().take(10).enumerate() {
|
||
println!("{}. CPF: {}", i + 1, div.cpf);
|
||
if !div.nome.is_empty() {
|
||
println!(" Nome: {}", div.nome);
|
||
}
|
||
println!(" TXT (DMED): R$ {:>12.2}", div.valor_txt);
|
||
println!(" XLS (Prosoft): R$ {:>12.2}", div.valor_xls);
|
||
println!(" Diferença: R$ {:>12.2} ({:+.1}%)", div.diferenca, div.percentual);
|
||
println!();
|
||
}
|
||
|
||
// Salva relatório completo
|
||
salvar_relatorio(&divergencias, &mapa_txt, &mapa_xls, cpfs_so_txt, cpfs_so_xls)?;
|
||
} else {
|
||
println!(" ✓ Todos os valores conferem!\n");
|
||
}
|
||
|
||
// CPFs que estão apenas em um dos arquivos
|
||
if cpfs_so_txt > 0 {
|
||
println!("⚠️ CPFs apenas no TXT (primeiros 5):");
|
||
for cpf in todos_cpfs.iter().filter(|c| mapa_txt.contains_key(**c) && !mapa_xls.contains_key(**c)).take(5) {
|
||
if let Some(p) = mapa_txt.get(*cpf) {
|
||
println!(" {} - {} - R$ {:.2}", cpf, p.nome, p.valor);
|
||
}
|
||
}
|
||
println!();
|
||
}
|
||
|
||
if cpfs_so_xls > 0 {
|
||
println!("⚠️ CPFs apenas no XLS (primeiros 5):");
|
||
for cpf in todos_cpfs.iter().filter(|c| !mapa_txt.contains_key(**c) && mapa_xls.contains_key(**c)).take(5) {
|
||
if let Some(p) = mapa_xls.get(*cpf) {
|
||
println!(" {} - R$ {:.2}", cpf, p.valor);
|
||
}
|
||
}
|
||
println!();
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
|
||
fn salvar_relatorio(
|
||
divergencias: &[Divergencia],
|
||
mapa_txt: &HashMap<&str, &Pessoa>,
|
||
mapa_xls: &HashMap<&str, &Pessoa>,
|
||
cpfs_so_txt: usize,
|
||
cpfs_so_xls: usize,
|
||
) -> std::io::Result<()> {
|
||
use std::fs::File;
|
||
use std::io::Write;
|
||
|
||
let mut arquivo = File::create("relatorio_divergencias.txt")?;
|
||
|
||
writeln!(arquivo, "RELATÓRIO DE DIVERGÊNCIAS - DMED vs PROSOFT")?;
|
||
writeln!(arquivo, "Data: {}", chrono::Local::now().format("%d/%m/%Y %H:%M:%S"))?;
|
||
writeln!(arquivo, "=")?;
|
||
writeln!(arquivo)?;
|
||
|
||
writeln!(arquivo, "RESUMO:")?;
|
||
writeln!(arquivo, "Total de divergências: {}", divergencias.len())?;
|
||
writeln!(arquivo, "CPFs apenas no TXT: {}", cpfs_so_txt)?;
|
||
writeln!(arquivo, "CPFs apenas no XLS: {}", cpfs_so_xls)?;
|
||
writeln!(arquivo)?;
|
||
|
||
writeln!(arquivo, "DETALHAMENTO DAS DIVERGÊNCIAS:")?;
|
||
writeln!(arquivo, "{:-<100}", "")?;
|
||
|
||
for (i, div) in divergencias.iter().enumerate() {
|
||
writeln!(arquivo, "\n{}. CPF: {}", i + 1, div.cpf)?;
|
||
if !div.nome.is_empty() {
|
||
writeln!(arquivo, " Nome: {}", div.nome)?;
|
||
}
|
||
writeln!(arquivo, " TXT (DMED): R$ {:>12.2}", div.valor_txt)?;
|
||
writeln!(arquivo, " XLS (Prosoft): R$ {:>12.2}", div.valor_xls)?;
|
||
writeln!(arquivo, " Diferença: R$ {:>12.2} ({:+.1}%)", div.diferenca, div.percentual)?;
|
||
}
|
||
|
||
println!("✓ Relatório completo salvo em relatorio_divergencias.txt");
|
||
|
||
Ok(())
|
||
}
|