Implementado (3 features)
**Agrupamento de faltantes contíguos** (`detector_sequencia.rs`) - Nova função pública `agrupar_contiguos(&[u64]) -> Vec<(u64, u64)>` com 4 testes - Na tela de resultado, faltantes agora aparecem como `• 100–104 (5 notas)` em vez de 5 linhas separadas **Estatísticas de completude** (`resultado.rs`) - Cada série exibe: `Série 001 — 10 faltante(s) — 990/1000 notas (99.0% completo):` **Copiar para clipboard** (`resultado.rs`) - Botão `📋 Copiar` ao lado de cada série nos faltantes — copia todos os números (não só a página atual) - Botão `📋 Copiar` nas duplicatas — copia no formato `1234 (3x), 5678 (2x)` Total: **42 → 46 testes**, todos passando.
This commit is contained in:
+62
-36
@@ -1,7 +1,7 @@
|
||||
use crate::application::usecases::exportar_pdf::exportar_pdf;
|
||||
use crate::domain::{
|
||||
entities::resultado_analise::ResultadoAnalise,
|
||||
services::parser_monetario::formatar_valor_br,
|
||||
entities::{chave_serie::ChaveSerie, resultado_analise::ResultadoAnalise},
|
||||
services::{detector_sequencia::agrupar_contiguos, parser_monetario::formatar_valor_br},
|
||||
};
|
||||
use crate::infrastructure::pdf_generator::GenpdfGenerator;
|
||||
use crate::ui::app::{AcaoModal, App, EstadoApp};
|
||||
@@ -86,15 +86,15 @@ fn renderizar_totais(ui: &mut Ui, resultado: &ResultadoAnalise) {
|
||||
formatar_valor_br(&resultado.soma_total)
|
||||
));
|
||||
|
||||
let mut series: Vec<&String> = resultado.soma_por_serie.keys().collect();
|
||||
series.sort();
|
||||
let mut chaves: Vec<&ChaveSerie> = resultado.soma_por_serie.keys().collect();
|
||||
chaves.sort();
|
||||
|
||||
for serie in series {
|
||||
let soma = &resultado.soma_por_serie[serie];
|
||||
let total_notas = resultado.total_por_serie.get(serie).copied().unwrap_or(0);
|
||||
for chave in chaves {
|
||||
let soma = &resultado.soma_por_serie[chave];
|
||||
let total_notas = resultado.total_por_serie.get(chave).copied().unwrap_or(0);
|
||||
ui.label(format!(
|
||||
" Série {}: {} nota(s) — R$ {}",
|
||||
serie,
|
||||
chave.label(),
|
||||
total_notas,
|
||||
formatar_valor_br(soma)
|
||||
));
|
||||
@@ -104,12 +104,9 @@ fn renderizar_totais(ui: &mut Ui, resultado: &ResultadoAnalise) {
|
||||
fn renderizar_faltantes(ui: &mut Ui, app: &mut App, resultado: &ResultadoAnalise) {
|
||||
let total_faltantes = resultado.total_faltantes();
|
||||
ui.label(
|
||||
egui::RichText::new(format!(
|
||||
"Notas Faltantes ({} total)",
|
||||
total_faltantes
|
||||
))
|
||||
.heading()
|
||||
.strong(),
|
||||
egui::RichText::new(format!("Notas Faltantes ({} total)", total_faltantes))
|
||||
.heading()
|
||||
.strong(),
|
||||
);
|
||||
ui.add_space(4.0);
|
||||
|
||||
@@ -118,22 +115,36 @@ fn renderizar_faltantes(ui: &mut Ui, app: &mut App, resultado: &ResultadoAnalise
|
||||
return;
|
||||
}
|
||||
|
||||
let mut series: Vec<&String> = resultado.faltantes_por_serie.keys().collect();
|
||||
series.sort();
|
||||
let mut chaves: Vec<&ChaveSerie> = resultado.faltantes_por_serie.keys().collect();
|
||||
chaves.sort();
|
||||
|
||||
for serie in series {
|
||||
let faltantes = &resultado.faltantes_por_serie[serie];
|
||||
for chave in chaves {
|
||||
let faltantes = &resultado.faltantes_por_serie[chave];
|
||||
if faltantes.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
ui.label(format!(
|
||||
"Série {} — {} faltante(s):",
|
||||
serie,
|
||||
faltantes.len()
|
||||
));
|
||||
// Estatística de completude por série
|
||||
let total_notas = resultado.total_por_serie.get(chave).copied().unwrap_or(0);
|
||||
let total_esperado = total_notas + faltantes.len();
|
||||
let percentual = total_notas as f64 / total_esperado as f64 * 100.0;
|
||||
|
||||
// Paginação
|
||||
ui.horizontal(|ui| {
|
||||
ui.label(format!(
|
||||
"Série {} — {} faltante(s) — {}/{} notas ({:.1}% completo):",
|
||||
chave.label(),
|
||||
faltantes.len(),
|
||||
total_notas,
|
||||
total_esperado,
|
||||
percentual,
|
||||
));
|
||||
if ui.button("📋 Copiar").on_hover_text("Copiar todos os números faltantes").clicked() {
|
||||
let texto = faltantes.iter().map(|n| n.to_string()).collect::<Vec<_>>().join(", ");
|
||||
ui.ctx().copy_text(texto);
|
||||
}
|
||||
});
|
||||
|
||||
// Paginação (por faltante individual)
|
||||
let total_paginas = (faltantes.len() + app.itens_por_pagina - 1) / app.itens_por_pagina;
|
||||
if app.pagina_faltantes >= total_paginas {
|
||||
app.pagina_faltantes = 0;
|
||||
@@ -142,8 +153,13 @@ fn renderizar_faltantes(ui: &mut Ui, app: &mut App, resultado: &ResultadoAnalise
|
||||
let inicio = app.pagina_faltantes * app.itens_por_pagina;
|
||||
let fim = (inicio + app.itens_por_pagina).min(faltantes.len());
|
||||
|
||||
for numero in &faltantes[inicio..fim] {
|
||||
ui.label(format!(" • {}", numero));
|
||||
// Exibir grupos contíguos da página atual
|
||||
for (a, b) in agrupar_contiguos(&faltantes[inicio..fim]) {
|
||||
if a == b {
|
||||
ui.label(format!(" • {}", a));
|
||||
} else {
|
||||
ui.label(format!(" • {}–{} ({} notas)", a, b, b - a + 1));
|
||||
}
|
||||
}
|
||||
|
||||
if total_paginas > 1 {
|
||||
@@ -181,20 +197,30 @@ fn renderizar_duplicatas(ui: &mut Ui, app: &mut App, resultado: &ResultadoAnalis
|
||||
return;
|
||||
}
|
||||
|
||||
let mut series: Vec<&String> = resultado.duplicadas_por_serie.keys().collect();
|
||||
series.sort();
|
||||
let mut chaves: Vec<&ChaveSerie> = resultado.duplicadas_por_serie.keys().collect();
|
||||
chaves.sort();
|
||||
|
||||
for serie in series {
|
||||
let duplicatas = &resultado.duplicadas_por_serie[serie];
|
||||
for chave in chaves {
|
||||
let duplicatas = &resultado.duplicadas_por_serie[chave];
|
||||
if duplicatas.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
ui.label(format!(
|
||||
"Série {} — {} grupo(s) duplicado(s):",
|
||||
serie,
|
||||
duplicatas.len()
|
||||
));
|
||||
ui.horizontal(|ui| {
|
||||
ui.label(format!(
|
||||
"Série {} — {} grupo(s) duplicado(s):",
|
||||
chave.label(),
|
||||
duplicatas.len()
|
||||
));
|
||||
if ui.button("📋 Copiar").on_hover_text("Copiar números duplicados").clicked() {
|
||||
let texto = duplicatas
|
||||
.iter()
|
||||
.map(|(n, c)| format!("{} ({}x)", n, c))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
ui.ctx().copy_text(texto);
|
||||
}
|
||||
});
|
||||
|
||||
let total_paginas =
|
||||
(duplicatas.len() + app.itens_por_pagina - 1) / app.itens_por_pagina;
|
||||
@@ -208,7 +234,7 @@ fn renderizar_duplicatas(ui: &mut Ui, app: &mut App, resultado: &ResultadoAnalis
|
||||
for (numero, count) in &duplicatas[inicio..fim] {
|
||||
ui.label(format!(
|
||||
" • NF {} / Série {} — {} ocorrências",
|
||||
numero, serie, count
|
||||
numero, chave.label(), count
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user