Files
dmed/examples/comparador_completo.rs

203 lines
7.6 KiB
Rust
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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<()> {
let _ = mapa_xls;
let _ = mapa_txt;
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(())
}