refactor: organiza imports e simplifica funções em diversos arquivos
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
use crate::domain::{
|
||||
entities::{
|
||||
nota::Nota,
|
||||
resultado_analise::{IntervaloSerie, ResultadoAnalise, ResultadoPreAnalise},
|
||||
resultado_analise::{ResultadoAnalise, ResultadoPreAnalise},
|
||||
},
|
||||
services::{
|
||||
detector_duplicidade::duplicidades_por_serie,
|
||||
detector_sequencia::{calcular_intervalo, detectar_faltantes, LIMITE_FALTANTES},
|
||||
detector_sequencia::{LIMITE_FALTANTES, calcular_intervalo, detectar_faltantes},
|
||||
},
|
||||
};
|
||||
use rust_decimal::Decimal;
|
||||
@@ -55,9 +55,7 @@ pub fn pre_analisar(notas: &[Nota]) -> ResultadoPreAnalise {
|
||||
|
||||
/// Verifica se alguma série excede o limite de faltantes.
|
||||
/// Retorna lista de séries que precisam de confirmação.
|
||||
pub fn series_com_intervalo_excessivo(
|
||||
pre: &ResultadoPreAnalise,
|
||||
) -> Vec<(String, u64)> {
|
||||
pub fn series_com_intervalo_excessivo(pre: &ResultadoPreAnalise) -> Vec<(String, u64)> {
|
||||
pre.intervalos_por_serie
|
||||
.iter()
|
||||
.filter(|(_, iv)| iv.excede_limite(LIMITE_FALTANTES))
|
||||
@@ -89,19 +87,18 @@ pub fn expandir_analise(pre: ResultadoPreAnalise, notas: &[Nota]) -> ResultadoAn
|
||||
}
|
||||
}
|
||||
|
||||
/// Executa análise completa sem verificar limites (use case simplificado).
|
||||
/// Útil quando o caller já confirmou ou sabe que não há intervalos excessivos.
|
||||
pub fn executar_analise(notas: &[Nota]) -> ResultadoAnalise {
|
||||
let pre = pre_analisar(notas);
|
||||
expandir_analise(pre, notas)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::domain::entities::nota::Nota;
|
||||
use rust_decimal_macros::dec;
|
||||
|
||||
/// Executa análise completa sem verificar limites (atalho para testes).
|
||||
fn executar_analise(notas: &[Nota]) -> ResultadoAnalise {
|
||||
let pre = pre_analisar(notas);
|
||||
expandir_analise(pre, notas)
|
||||
}
|
||||
|
||||
fn nota(numero: u64, serie: &str, valor: Option<rust_decimal::Decimal>) -> Nota {
|
||||
Nota::new(numero, serie.to_string(), valor, None)
|
||||
}
|
||||
@@ -133,10 +130,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn pre_analise_detecta_intervalo_excessivo() {
|
||||
let notas = vec![
|
||||
nota(1, "001", None),
|
||||
nota(20_000, "001", None),
|
||||
];
|
||||
let notas = vec![nota(1, "001", None), nota(20_000, "001", None)];
|
||||
let pre = pre_analisar(¬as);
|
||||
let excessivos = series_com_intervalo_excessivo(&pre);
|
||||
assert_eq!(excessivos.len(), 1);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::domain::entities::layout::{Layout, LayoutCsv, LayoutXlsx};
|
||||
use crate::domain::entities::layout::{LayoutCsv, LayoutXlsx};
|
||||
use crate::domain::entities::nota::Nota;
|
||||
use crate::domain::entities::serie::validar_serie;
|
||||
use crate::domain::errors::{ErroArquivo, ResumoAvisos};
|
||||
@@ -24,7 +24,10 @@ pub fn listar_abas_xlsx(caminho: &Path) -> Result<InfoXlsx, ErroArquivo> {
|
||||
}
|
||||
|
||||
/// Importa um arquivo CSV e retorna as notas válidas + avisos consolidados.
|
||||
pub fn importar_csv(caminho: &Path, config: &LayoutCsv) -> Result<ResultadoImportacao, ErroArquivo> {
|
||||
pub fn importar_csv(
|
||||
caminho: &Path,
|
||||
config: &LayoutCsv,
|
||||
) -> Result<ResultadoImportacao, ErroArquivo> {
|
||||
let resultado = csv_reader::ler_csv(
|
||||
caminho,
|
||||
config.delimitador as u8,
|
||||
@@ -46,17 +49,21 @@ pub fn importar_csv(caminho: &Path, config: &LayoutCsv) -> Result<ResultadoImpor
|
||||
}
|
||||
|
||||
/// Importa um arquivo XLSX e retorna as notas válidas + avisos consolidados.
|
||||
pub fn importar_xlsx(caminho: &Path, config: &LayoutXlsx) -> Result<ResultadoImportacao, ErroArquivo> {
|
||||
pub fn importar_xlsx(
|
||||
caminho: &Path,
|
||||
config: &LayoutXlsx,
|
||||
) -> Result<ResultadoImportacao, ErroArquivo> {
|
||||
// Determinar linha de início a partir de qualquer campo mapeado
|
||||
let linha_inicio = {
|
||||
let coord_num = xlsx_reader::parsear_letra_linha(&config.pos_numero)
|
||||
.ok_or_else(|| ErroArquivo::ErroLeitura(format!(
|
||||
"Posição de Numero inválida: '{}'", config.pos_numero
|
||||
)))?;
|
||||
let coord_ser = xlsx_reader::parsear_letra_linha(&config.pos_serie)
|
||||
.ok_or_else(|| ErroArquivo::ErroLeitura(format!(
|
||||
"Posição de Serie inválida: '{}'", config.pos_serie
|
||||
)))?;
|
||||
let coord_num = xlsx_reader::parsear_letra_linha(&config.pos_numero).ok_or_else(|| {
|
||||
ErroArquivo::ErroLeitura(format!(
|
||||
"Posição de Numero inválida: '{}'",
|
||||
config.pos_numero
|
||||
))
|
||||
})?;
|
||||
let coord_ser = xlsx_reader::parsear_letra_linha(&config.pos_serie).ok_or_else(|| {
|
||||
ErroArquivo::ErroLeitura(format!("Posição de Serie inválida: '{}'", config.pos_serie))
|
||||
})?;
|
||||
coord_num.linha.min(coord_ser.linha)
|
||||
};
|
||||
|
||||
@@ -113,7 +120,9 @@ fn mapear_linhas_para_notas(
|
||||
Some(s) if !s.trim().is_empty() => s.trim().to_string(),
|
||||
_ => {
|
||||
avisos.numeros_invalidos += 1;
|
||||
avisos.detalhes.push(format!("Linha {}: campo Numero ausente", linha_num));
|
||||
avisos
|
||||
.detalhes
|
||||
.push(format!("Linha {}: campo Numero ausente", linha_num));
|
||||
continue;
|
||||
}
|
||||
};
|
||||
@@ -123,7 +132,9 @@ fn mapear_linhas_para_notas(
|
||||
Ok(n) => n,
|
||||
Err(msg) => {
|
||||
avisos.numeros_invalidos += 1;
|
||||
avisos.detalhes.push(format!("Linha {}: {}", linha_num, msg));
|
||||
avisos
|
||||
.detalhes
|
||||
.push(format!("Linha {}: {}", linha_num, msg));
|
||||
continue;
|
||||
}
|
||||
};
|
||||
@@ -133,7 +144,9 @@ fn mapear_linhas_para_notas(
|
||||
Some(s) => s.trim().to_string(),
|
||||
None => {
|
||||
avisos.series_invalidas += 1;
|
||||
avisos.detalhes.push(format!("Linha {}: campo Serie ausente", linha_num));
|
||||
avisos
|
||||
.detalhes
|
||||
.push(format!("Linha {}: campo Serie ausente", linha_num));
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@ use crate::domain::{
|
||||
entities::layout::{Layout, LayoutJson},
|
||||
errors::ErroLayout,
|
||||
};
|
||||
use crate::infrastructure::sqlite::{layout_repository, migrations};
|
||||
use crate::infrastructure::sqlite::layout_repository;
|
||||
use rusqlite::Connection;
|
||||
|
||||
/// Salva um layout no banco de dados.
|
||||
@@ -14,12 +14,10 @@ pub fn salvar_layout(conn: &Connection, layout: &Layout) -> Result<i64, String>
|
||||
}
|
||||
|
||||
if let Some(id) = layout.id() {
|
||||
layout_repository::atualizar(conn, layout)
|
||||
.map_err(|e| e.to_string())?;
|
||||
layout_repository::atualizar(conn, layout).map_err(|e| e.to_string())?;
|
||||
Ok(id)
|
||||
} else {
|
||||
layout_repository::salvar(conn, layout)
|
||||
.map_err(|e| e.to_string())
|
||||
layout_repository::salvar(conn, layout).map_err(|e| e.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,11 +26,6 @@ pub fn listar_layouts(conn: &Connection) -> Result<Vec<Layout>, String> {
|
||||
layout_repository::listar(conn).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
/// Carrega um layout pelo id.
|
||||
pub fn carregar_layout(conn: &Connection, id: i64) -> Result<Option<Layout>, String> {
|
||||
layout_repository::buscar_por_id(conn, id).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
/// Exclui um layout pelo id.
|
||||
pub fn excluir_layout(conn: &Connection, id: i64) -> Result<(), String> {
|
||||
layout_repository::excluir(conn, id).map_err(|e| e.to_string())
|
||||
@@ -61,8 +54,8 @@ pub fn importar_layout_json(
|
||||
novo_nome: Option<&str>,
|
||||
) -> Result<i64, ErroLayout> {
|
||||
// Deserializar
|
||||
let json_repr: LayoutJson = serde_json::from_str(json)
|
||||
.map_err(|e| ErroLayout::JsonMalformado(e.to_string()))?;
|
||||
let json_repr: LayoutJson =
|
||||
serde_json::from_str(json).map_err(|e| ErroLayout::JsonMalformado(e.to_string()))?;
|
||||
|
||||
let mut layout = Layout::try_from(json_repr)?;
|
||||
|
||||
@@ -105,6 +98,5 @@ pub fn importar_layout_json(
|
||||
}
|
||||
|
||||
// Inserir novo
|
||||
layout_repository::salvar(conn, &layout)
|
||||
.map_err(|e| ErroLayout::JsonMalformado(e.to_string()))
|
||||
layout_repository::salvar(conn, &layout).map_err(|e| ErroLayout::JsonMalformado(e.to_string()))
|
||||
}
|
||||
|
||||
@@ -1,13 +1,5 @@
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error, Clone)]
|
||||
pub enum ErroNumero {
|
||||
#[error("Número zero não é válido para uma nota fiscal")]
|
||||
Zero,
|
||||
#[error("Valor não numérico: '{0}'")]
|
||||
NaoNumerico(String),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error, Clone)]
|
||||
pub enum ErroSerie {
|
||||
#[error("Série inválida: '{0}' (deve conter de 1 a 3 dígitos numéricos)")]
|
||||
@@ -40,52 +32,10 @@ pub enum ErroArquivo {
|
||||
TamanhoExcedido(u64),
|
||||
#[error("Arquivo corrompido ou ilegível: {0}")]
|
||||
Corrompido(String),
|
||||
#[error("Formato não suportado: {0}")]
|
||||
FormatoNaoSuportado(String),
|
||||
#[error("Erro de leitura: {0}")]
|
||||
ErroLeitura(String),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ErroAnalise {
|
||||
#[error("Intervalo de faltantes muito grande para a série '{serie}': {intervalo} registros")]
|
||||
IntervaloMuitoGrande { serie: String, intervalo: u64 },
|
||||
#[error("Campo obrigatório não mapeado: {0}")]
|
||||
CampoObrigatorioNaoMapeado(String),
|
||||
#[error("Índice de coluna inválido: campo '{campo}', índice {indice}")]
|
||||
IndiceInvalido { campo: String, indice: usize },
|
||||
#[error("Dois campos mapeados para o mesmo índice: {0}")]
|
||||
IndicesDuplicados(String),
|
||||
}
|
||||
|
||||
/// Aviso coletado durante a importação, para ser exibido de forma consolidada ao usuário.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AvisoImportacao {
|
||||
pub categoria: CategoriaAviso,
|
||||
pub mensagem: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum CategoriaAviso {
|
||||
LinhaMalformada,
|
||||
NumeroInvalido,
|
||||
SerieInvalida,
|
||||
ValorInvalido,
|
||||
RegistroDescartado,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for CategoriaAviso {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
CategoriaAviso::LinhaMalformada => write!(f, "Linhas malformadas"),
|
||||
CategoriaAviso::NumeroInvalido => write!(f, "Valores de Numero inválidos"),
|
||||
CategoriaAviso::SerieInvalida => write!(f, "Registros com Série inválida"),
|
||||
CategoriaAviso::ValorInvalido => write!(f, "Valores monetários inválidos"),
|
||||
CategoriaAviso::RegistroDescartado => write!(f, "Registros descartados"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Resumo consolidado de avisos para exibição em um único modal.
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct ResumoAvisos {
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::domain::services::parser_monetario::formatar_valor_br;
|
||||
use chrono::{DateTime, Local};
|
||||
use genpdf::{
|
||||
elements::{Break, Paragraph},
|
||||
fonts, style, Document, Element, SimplePageDecorator,
|
||||
fonts, style, Document, SimplePageDecorator,
|
||||
};
|
||||
use std::path::Path;
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
use rusqlite::Connection;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
const LIMITE_50MB: u64 = 50 * 1024 * 1024;
|
||||
|
||||
/// Determina o caminho do banco de dados conforme o sistema operacional.
|
||||
pub fn caminho_banco() -> PathBuf {
|
||||
let config_dir = dirs::config_dir().unwrap_or_else(|| PathBuf::from("."));
|
||||
@@ -33,7 +31,7 @@ pub fn abrir_banco_no_caminho(caminho: &Path) -> Result<(Connection, bool), Stri
|
||||
// Testar se o banco funciona com uma query simples
|
||||
match conn.execute_batch("SELECT 1;") {
|
||||
Ok(_) => return Ok((conn, false)),
|
||||
Err(e) => {
|
||||
Err(_e) => {
|
||||
// Banco corrompido
|
||||
drop(conn);
|
||||
let bak = caminho.with_extension("db.bak");
|
||||
|
||||
@@ -145,55 +145,6 @@ pub fn listar(conn: &Connection) -> Result<Vec<Layout>> {
|
||||
layouts
|
||||
}
|
||||
|
||||
/// Busca um layout pelo id.
|
||||
pub fn buscar_por_id(conn: &Connection, id: i64) -> Result<Option<Layout>> {
|
||||
let mut stmt = conn.prepare(
|
||||
"SELECT id, nome, tipo,
|
||||
delimitador, encoding, linha_cabecalho,
|
||||
indice_numero, indice_serie, indice_valor, indice_data,
|
||||
aba, pos_numero, pos_serie, pos_valor, pos_data
|
||||
FROM layouts WHERE id = ?1",
|
||||
)?;
|
||||
|
||||
let mut results = stmt.query_map([id], |row| {
|
||||
let id: i64 = row.get(0)?;
|
||||
let nome: String = row.get(1)?;
|
||||
let tipo: String = row.get(2)?;
|
||||
|
||||
if tipo == "csv" {
|
||||
let delim_str: String = row.get(3)?;
|
||||
let delimitador = delim_str.chars().next().unwrap_or(';');
|
||||
Ok(Layout::Csv {
|
||||
id: Some(id),
|
||||
nome,
|
||||
config: LayoutCsv {
|
||||
delimitador,
|
||||
encoding: row.get(4)?,
|
||||
linha_cabecalho: row.get::<_, i64>(5)? as usize,
|
||||
indice_numero: row.get::<_, i64>(6)? as usize,
|
||||
indice_serie: row.get::<_, i64>(7)? as usize,
|
||||
indice_valor: row.get::<_, Option<i64>>(8)?.map(|v| v as usize),
|
||||
indice_data: row.get::<_, Option<i64>>(9)?.map(|v| v as usize),
|
||||
},
|
||||
})
|
||||
} else {
|
||||
Ok(Layout::Xlsx {
|
||||
id: Some(id),
|
||||
nome,
|
||||
config: LayoutXlsx {
|
||||
aba: row.get(10)?,
|
||||
pos_numero: row.get(11)?,
|
||||
pos_serie: row.get(12)?,
|
||||
pos_valor: row.get(13)?,
|
||||
pos_data: row.get(14)?,
|
||||
},
|
||||
})
|
||||
}
|
||||
})?;
|
||||
|
||||
results.next().transpose()
|
||||
}
|
||||
|
||||
/// Remove um layout pelo id.
|
||||
pub fn excluir(conn: &Connection, id: i64) -> Result<()> {
|
||||
conn.execute("DELETE FROM layouts WHERE id = ?1", [id])?;
|
||||
|
||||
@@ -21,12 +21,12 @@ pub fn aplicar_migrations(conn: &Connection) -> Result<()> {
|
||||
)
|
||||
.unwrap_or(0);
|
||||
|
||||
if versao_atual < 1 {
|
||||
if versao_atual < VERSAO_SCHEMA_ATUAL {
|
||||
migration_v1(conn)?;
|
||||
if versao_atual == 0 {
|
||||
conn.execute("INSERT INTO schema_version (versao) VALUES (?1);", [1])?;
|
||||
conn.execute("INSERT INTO schema_version (versao) VALUES (?1);", [VERSAO_SCHEMA_ATUAL])?;
|
||||
} else {
|
||||
conn.execute("UPDATE schema_version SET versao = ?1;", [1])?;
|
||||
conn.execute("UPDATE schema_version SET versao = ?1;", [VERSAO_SCHEMA_ATUAL])?;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ pub fn ler_xlsx(
|
||||
.worksheet_range(nome_aba)
|
||||
.map_err(|e| ErroArquivo::Corrompido(e.to_string()))?;
|
||||
|
||||
let mut avisos = ResumoAvisos::default();
|
||||
let avisos = ResumoAvisos::default();
|
||||
let mut linhas: Vec<Vec<String>> = Vec::new();
|
||||
|
||||
let linha_inicio_base0 = (linha_inicio.saturating_sub(1)) as usize;
|
||||
|
||||
+47
-45
@@ -1,10 +1,6 @@
|
||||
use crate::application::usecases::{
|
||||
executar_analise::{expandir_analise, pre_analisar, series_com_intervalo_excessivo},
|
||||
importar_arquivo::{importar_csv, importar_xlsx, listar_abas_xlsx, ResultadoImportacao},
|
||||
layouts::{
|
||||
carregar_layout, excluir_layout, exportar_layout_json, importar_layout_json,
|
||||
listar_layouts, salvar_layout,
|
||||
},
|
||||
layouts::{excluir_layout, listar_layouts, salvar_layout},
|
||||
};
|
||||
use crate::domain::{
|
||||
entities::{
|
||||
@@ -14,10 +10,7 @@ use crate::domain::{
|
||||
},
|
||||
errors::ResumoAvisos,
|
||||
};
|
||||
use crate::infrastructure::{
|
||||
pdf_generator::GenpdfGenerator,
|
||||
sqlite::{connection::abrir_banco, migrations::aplicar_migrations},
|
||||
};
|
||||
use crate::infrastructure::sqlite::{connection::abrir_banco, migrations::aplicar_migrations};
|
||||
use egui::Context;
|
||||
use rusqlite::Connection;
|
||||
use std::path::PathBuf;
|
||||
@@ -34,10 +27,7 @@ pub enum EstadoApp {
|
||||
/// Resultado pronto para exibição.
|
||||
ExibindoResultado(ResultadoAnalise),
|
||||
/// Aguardando confirmação do usuário para expandir faltantes.
|
||||
ConfirmandoIntervalo {
|
||||
pre: ResultadoPreAnalise,
|
||||
series_excessivas: Vec<(String, u64)>,
|
||||
},
|
||||
ConfirmandoIntervalo { pre: ResultadoPreAnalise },
|
||||
/// Gerenciamento de layouts.
|
||||
GerenciandoLayouts,
|
||||
/// Análise em execução em background (thread separada).
|
||||
@@ -72,6 +62,7 @@ pub enum TipoModal {
|
||||
pub enum AcaoModal {
|
||||
ConfirmarExpansaoFaltantes,
|
||||
ConfirmarExclusaoLayout(i64),
|
||||
#[allow(dead_code)]
|
||||
SobrescreverLayout,
|
||||
ConfirmarNovaAnalise,
|
||||
/// Salvar a configuração atual como novo layout, usando modal.input_texto como nome.
|
||||
@@ -396,12 +387,20 @@ impl App {
|
||||
/// Processa o resultado recebido do canal de análise em background.
|
||||
pub fn processar_resultado_pendente(&mut self, resultado: ResultadoPendente) {
|
||||
match resultado {
|
||||
ResultadoPendente::Concluido { resultado, avisos, notas } => {
|
||||
ResultadoPendente::Concluido {
|
||||
resultado,
|
||||
avisos,
|
||||
notas,
|
||||
} => {
|
||||
if let Some(n) = notas {
|
||||
self.notas_importadas = n;
|
||||
}
|
||||
if let Some(av) = &avisos {
|
||||
self.avisos_importacao = if av.tem_avisos() { Some(av.clone()) } else { None };
|
||||
self.avisos_importacao = if av.tem_avisos() {
|
||||
Some(av.clone())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
}
|
||||
self.estado = EstadoApp::ExibindoResultado(resultado);
|
||||
if let Some(av) = &self.avisos_importacao.clone() {
|
||||
@@ -418,18 +417,22 @@ impl App {
|
||||
notas,
|
||||
} => {
|
||||
self.notas_importadas = notas;
|
||||
self.avisos_importacao = if avisos.tem_avisos() { Some(avisos) } else { None };
|
||||
self.avisos_importacao = if avisos.tem_avisos() {
|
||||
Some(avisos)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let msg = series_excessivas
|
||||
.iter()
|
||||
.map(|(serie, count)| {
|
||||
format!("Série {}: intervalo de {} faltantes detectado", serie, count)
|
||||
format!(
|
||||
"Série {}: intervalo de {} faltantes detectado",
|
||||
serie, count
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
self.estado = EstadoApp::ConfirmandoIntervalo {
|
||||
pre,
|
||||
series_excessivas,
|
||||
};
|
||||
self.estado = EstadoApp::ConfirmandoIntervalo { pre };
|
||||
self.exibir_confirmacao(
|
||||
"Intervalo muito grande",
|
||||
format!(
|
||||
@@ -464,16 +467,16 @@ impl App {
|
||||
let msg = excessivos
|
||||
.iter()
|
||||
.map(|(serie, count)| {
|
||||
format!("Série {}: intervalo de {} faltantes detectado", serie, count)
|
||||
format!(
|
||||
"Série {}: intervalo de {} faltantes detectado",
|
||||
serie, count
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
|
||||
let acao = AcaoModal::ConfirmarExpansaoFaltantes;
|
||||
self.estado = EstadoApp::ConfirmandoIntervalo {
|
||||
pre,
|
||||
series_excessivas: excessivos,
|
||||
};
|
||||
self.estado = EstadoApp::ConfirmandoIntervalo { pre };
|
||||
self.exibir_confirmacao(
|
||||
"Intervalo muito grande",
|
||||
format!(
|
||||
@@ -502,7 +505,8 @@ impl App {
|
||||
egui::TopBottomPanel::top("breadcrumb").show(ctx, |ui| {
|
||||
ui.add_space(4.0);
|
||||
ui.horizontal(|ui| {
|
||||
for (i, label) in ["① Arquivo", "② Colunas", "③ Resultado"].iter().enumerate() {
|
||||
for (i, label) in ["① Arquivo", "② Colunas", "③ Resultado"].iter().enumerate()
|
||||
{
|
||||
let n = i + 1;
|
||||
let texto = egui::RichText::new(*label);
|
||||
if n == passo_ativo {
|
||||
@@ -552,25 +556,23 @@ impl eframe::App for App {
|
||||
let tela_id = self.tela_atual();
|
||||
|
||||
// Renderizar a tela atual
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
match tela_id {
|
||||
0 => crate::ui::screens::import::renderizar(ui, ctx, self),
|
||||
1 => crate::ui::screens::import::renderizar_selecao_aba(ui, ctx, self),
|
||||
2 => crate::ui::screens::configuracao_colunas::renderizar(ui, ctx, self),
|
||||
3 => crate::ui::screens::resultado::renderizar(ui, ctx, self),
|
||||
4 => crate::ui::screens::configuracao_colunas::renderizar(ui, ctx, self),
|
||||
5 => crate::ui::screens::layouts::renderizar(ui, ctx, self),
|
||||
6 => {
|
||||
ui.centered_and_justified(|ui| {
|
||||
ui.label(
|
||||
egui::RichText::new("⏳ Analisando... aguarde.")
|
||||
.size(22.0)
|
||||
.strong(),
|
||||
);
|
||||
});
|
||||
}
|
||||
_ => {}
|
||||
egui::CentralPanel::default().show(ctx, |ui| match tela_id {
|
||||
0 => crate::ui::screens::import::renderizar(ui, ctx, self),
|
||||
1 => crate::ui::screens::import::renderizar_selecao_aba(ui, ctx, self),
|
||||
2 => crate::ui::screens::configuracao_colunas::renderizar(ui, ctx, self),
|
||||
3 => crate::ui::screens::resultado::renderizar(ui, ctx, self),
|
||||
4 => crate::ui::screens::configuracao_colunas::renderizar(ui, ctx, self),
|
||||
5 => crate::ui::screens::layouts::renderizar(ui, ctx, self),
|
||||
6 => {
|
||||
ui.centered_and_justified(|ui| {
|
||||
ui.label(
|
||||
egui::RichText::new("⏳ Analisando... aguarde.")
|
||||
.size(22.0)
|
||||
.strong(),
|
||||
);
|
||||
});
|
||||
}
|
||||
_ => {}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,6 +100,16 @@ pub fn renderizar(ui: &mut Ui, ctx: &Context, app: &mut App) {
|
||||
}
|
||||
});
|
||||
|
||||
if valido && !app.notas_importadas.is_empty() {
|
||||
if ui
|
||||
.button("🔄 Reanalisar")
|
||||
.on_hover_text("Reanalisa as notas já importadas sem reimportar o arquivo")
|
||||
.clicked()
|
||||
{
|
||||
app.executar_analise();
|
||||
}
|
||||
}
|
||||
|
||||
if ui.button("💾 Salvar como layout...").clicked() {
|
||||
app.exibir_modal_salvar_layout();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
use crate::application::usecases::importar_arquivo::{
|
||||
importar_csv, importar_xlsx, listar_abas_xlsx,
|
||||
};
|
||||
use crate::application::usecases::importar_arquivo::listar_abas_xlsx;
|
||||
use crate::domain::entities::layout::TipoArquivo;
|
||||
use crate::ui::app::{App, EstadoApp};
|
||||
use egui::{Context, Ui};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::application::usecases::layouts::{
|
||||
exportar_layout_json, importar_layout_json, salvar_layout,
|
||||
};
|
||||
use crate::domain::entities::layout::{Layout, LayoutCsv, LayoutXlsx, TipoArquivo};
|
||||
use crate::domain::entities::layout::{Layout, TipoArquivo};
|
||||
use crate::domain::errors::ErroLayout;
|
||||
use crate::ui::app::{AcaoModal, App, EstadoApp};
|
||||
use egui::{Context, Ui};
|
||||
@@ -83,7 +83,7 @@ pub fn renderizar(ui: &mut Ui, ctx: &Context, app: &mut App) {
|
||||
|
||||
fn renderizar_secao_layouts(
|
||||
ui: &mut Ui,
|
||||
ctx: &Context,
|
||||
_ctx: &Context,
|
||||
app: &mut App,
|
||||
titulo: &str,
|
||||
layouts: &[Layout],
|
||||
|
||||
@@ -10,7 +10,7 @@ use egui::{Context, Ui};
|
||||
const OPCOES_PAGINA: &[usize] = &[50, 100, 200, 1000];
|
||||
|
||||
/// Renderiza a tela de resultados.
|
||||
pub fn renderizar(ui: &mut Ui, ctx: &Context, app: &mut App) {
|
||||
pub fn renderizar(ui: &mut Ui, _ctx: &Context, app: &mut App) {
|
||||
// Extrair resultado do estado (sem mover)
|
||||
let resultado = match &app.estado {
|
||||
EstadoApp::ExibindoResultado(r) => r.clone(),
|
||||
|
||||
Reference in New Issue
Block a user