// 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(()) }