# Roteiro de Implementação — Comparador de Notas **Versão:** 1.0 **Data:** 02/03/2026 **Baseado no PRD:** v1.6 --- ## Fase 0 — Fundação do Projeto **Objetivo:** Estrutura compilável com todas as dependências. ### 0.1 Configurar `Cargo.toml` Adicionar todas as dependências: ```toml [dependencies] eframe = "0.31" egui = "0.31" rusqlite = { version = "0.32", features = ["bundled"] } csv = "1.3" calamine = "0.26" rust_decimal = { version = "1.36", features = ["serde"] } rust_decimal_macros = "1.36" serde = { version = "1", features = ["derive"] } serde_json = "1" genpdf = "0.2" chrono = { version = "0.4", features = ["serde"] } encoding_rs = "0.8" dirs = "5" thiserror = "2" ``` ### 0.2 Criar estrutura de pastas completa Conforme definido na seção 9.1 do PRD: ``` src/ main.rs ui/ mod.rs app.rs screens/ import.rs configuracao_colunas.rs layouts.rs resultado.rs application/ mod.rs usecases/ importar_arquivo.rs executar_analise.rs exportar_pdf.rs domain/ mod.rs errors.rs entities/ nota.rs serie.rs layout.rs resultado_analise.rs services/ detector_sequencia.rs detector_duplicidade.rs parser_monetario.rs infrastructure/ mod.rs csv_reader.rs xlsx_reader.rs pdf_generator.rs sqlite/ mod.rs connection.rs migrations.rs layout_repository.rs ``` ### 0.3 Criar todos os `mod.rs` com declarações vazias Garantir que o projeto compila antes de começar a implementar. --- ## Fase 1 — Camada Domain (núcleo puro) **Dependência:** Fase 0 concluída. **Restrição:** Zero dependência de egui, rusqlite, calamine, csv ou genpdf. Apenas Rust puro + `rust_decimal` e `chrono`. ### 1.1 `domain/errors.rs` Definir tipos de erro com `thiserror`: - `ErroNumero` (Zero, NaoNumerico) - `ErroSerie` (Invalida, Vazia) - `ErroValor` (Negativo, NaoNumerico) - `ErroLayout` (CampoObrigatorioAusente, JsonMalformado, NomeConflitante) ### 1.2 `domain/entities/nota.rs` ```rust pub struct Nota { pub numero: u64, pub serie: String, pub valor: Option, pub data: Option, } ``` ### 1.3 `domain/entities/serie.rs` - Validação da regex `[0-9]{1,3}` - Função `validar_serie(s: &str) -> Result` ### 1.4 `domain/entities/layout.rs` - `enum TipoArquivo { Csv, Xlsx }` - `struct LayoutCsv` com todos os campos da seção 10.4 do PRD - `struct LayoutXlsx` com todos os campos da seção 10.4 do PRD - `enum Layout { Csv(LayoutCsv), Xlsx(LayoutXlsx) }` ### 1.5 `domain/entities/resultado_analise.rs` ```rust pub struct ResultadoPreAnalise { // min, max, contagem_faltantes por série pub intervalos_por_serie: HashMap, } pub struct ResultadoAnalise { pub faltantes_por_serie: HashMap>, pub duplicadas_por_serie: HashMap>, pub soma_total: Decimal, pub soma_por_serie: HashMap, } ``` ### 1.6 `domain/services/parser_monetario.rs` Implementar exatamente o algoritmo da seção RF06: | Regra | Condição | Comportamento | |-------|----------|---------------| | 1 | Contém ponto **e** vírgula | Último separador é o decimal | | 2a | Apenas um separador + exatamente 2 dígitos após | Separador decimal | | 2b | Apenas um separador + exatamente 3 dígitos após | Separador de milhar | | 2c | Apenas um separador + outros casos | Separador decimal | | 3 | Sem separador | Número inteiro | - Rejeitar valores negativos (precedidos de `-`) - Armazenar como `rust_decimal::Decimal` > **Prioridade:** Testar exaustivamente com todos os exemplos da tabela do PRD antes de avançar. ### 1.7 `domain/services/detector_sequencia.rs` - Recebe `Vec<&Nota>` de uma série - Ordena por `numero` crescente - Percorre **incrementalmente** (sem lista intermediária) - Retorna `Vec` de faltantes - Respeitar o limite de 10.000 registros faltantes (RFC04): retornar `ResultadoPreAnalise` antes de expandir ### 1.8 `domain/services/detector_duplicidade.rs` - Recebe `Vec<&Nota>` - Retorna `HashMap<(u64, String), usize>` com contagem por grupo - Filtra apenas grupos com contagem > 1 --- ## Fase 2 — Camada Infrastructure **Dependência:** Fase 1 concluída. ### 2.1 `infrastructure/sqlite/connection.rs` - Determinar caminho do banco conforme SO via `dirs::config_dir()`: - Linux: `~/.config/comparador-notas/config.db` - Windows: `%APPDATA%\comparador-notas\config.db` - macOS: `~/Library/Application Support/comparador-notas/config.db` - Criar diretório automaticamente se não existir - Abrir conexão SQLite - Tratamento de banco corrompido: renomear para `config.db.bak`, recriar banco vazio ### 2.2 `infrastructure/sqlite/migrations.rs` - Tabela `schema_version` para controle de versão do schema - Migration v1: criar tabela `layouts` com todos os campos da seção 10.4 do PRD - Aplicar migrations automaticamente na inicialização ### 2.3 `infrastructure/sqlite/layout_repository.rs` ```rust fn salvar(layout: &Layout) -> Result fn listar() -> Result> fn buscar_por_id(id: i64) -> Result> fn excluir(id: i64) -> Result<()> fn existe_nome(nome: &str) -> Result ``` ### 2.4 `infrastructure/csv_reader.rs` - Suporte a delimitadores: `,` `;` `\t` - Suporte a encoding: UTF-8 e Windows-1252 (via `encoding_rs`) - Respeitar linha de cabeçalho configurável (base 1) - Ignorar linhas em branco silenciosamente - Coletar linhas malformadas para relatório consolidado - Validar limite de 50 MB antes de ler - Retornar `Vec>` (linhas × colunas) ### 2.5 `infrastructure/xlsx_reader.rs` - `listar_abas(path) -> Vec` — chamado imediatamente após seleção do arquivo - Ler dados da aba selecionada a partir da posição `LetraLinha` - Converter notação `LetraLinha` (ex: `B3`) para `(col_idx, row_idx)` - Ignorar linhas em branco silenciosamente - Validar limite de 50 MB - Tratar arquivo corrompido retornando erro tipado ### 2.6 `infrastructure/pdf_generator.rs` - Implementar trait `PdfGenerator` definida em `application/` - Gerar PDF com `genpdf` contendo: - Metadados: nome do arquivo importado, data/hora de geração, nome do layout - Faltantes agrupados por série - Duplicatas agrupadas por série - Totais por série e total geral --- ## Fase 3 — Camada Application **Dependência:** Fases 1 e 2 concluídas. ### 3.1 `application/usecases/importar_arquivo.rs` 1. Validar tamanho do arquivo (recusar > 50 MB) 2. Detectar tipo (CSV ou XLSX) 3. Para XLSX: chamar `listar_abas()` e retornar lista para UI fazer a seleção 4. Chamar leitor adequado com configurações do layout 5. Mapear colunas e construir `Vec` 6. Aplicar validações: regex de série, numero zero, valores inválidos 7. Retornar notas válidas + relatório de avisos consolidado (RF06/RNF06) ### 3.2 `application/usecases/executar_analise.rs` 1. Receber `Vec` 2. Chamar `parser_monetario` para cada valor 3. Agrupar por série 4. Chamar `detector_sequencia` por série → gerar `ResultadoPreAnalise` 5. Se algum intervalo > 10.000: retornar `ResultadoPreAnalise` para UI solicitar confirmação 6. Após confirmação: expandir faltantes e montar `ResultadoAnalise` 7. Chamar `detector_duplicidade` 8. Calcular somas com `Decimal` (nunca `f64`) 9. Retornar `ResultadoAnalise` ### 3.3 `application/usecases/exportar_pdf.rs` - Depende da trait `PdfGenerator` (não de `genpdf` diretamente) - Recebe `ResultadoAnalise` + metadados do arquivo e layout - Delega geração para a implementação concreta em infrastructure ### 3.4 Gerenciamento de layouts - `salvar_layout` — valida campos obrigatórios antes de persistir - `listar_layouts` — lista separada por tipo (CSV / XLSX) - `carregar_layout` — busca por id - `excluir_layout` — remove do banco - `exportar_layout_json` — serializa com `serde_json`, sugere nome do arquivo - `importar_layout_json` — desserializa + valida campos obrigatórios + verifica conflito de nome --- ## Fase 4 — Camada UI (egui/eframe) **Dependência:** Fase 3 concluída. **Regra:** Nenhuma regra de negócio, parsing ou cálculo dentro da UI. ### 4.1 `ui/app.rs` — Estrutura principal ```rust enum EstadoApp { Importando, ConfigurandoColunas, SelecionandoAba(Vec), // lista de abas XLSX Analisando, ConfirmandoIntervalo(ResultadoPreAnalise), ExibindoResultado(ResultadoAnalise), GerenciandoLayouts, } ``` - Gerenciamento de modais/popups bloqueantes (RNF06) - Struct `App` com estado compartilhado e referência ao banco SQLite ### 4.2 `ui/screens/import.rs` - Botão "Selecionar arquivo" com diálogo nativo - Exibir nome do arquivo selecionado - Para XLSX: exibir dropdown de seleção de aba **imediatamente** após seleção do arquivo - Dropdown de layouts salvos para carregar configuração existente - Botão "Gerenciar Layouts" → navegar para `layouts.rs` ### 4.3 `ui/screens/configuracao_colunas.rs` - Para CSV: campos numéricos para índice de cada coluna (base 0) + delimitador + encoding + linha cabeçalho - Para XLSX: campos de texto para posição `LetraLinha` de cada coluna - Validação em tempo real: índices duplicados, campos obrigatórios ausentes - Bloqueio do botão "Executar Análise" quando configuração inválida - Botão "Executar Análise" ### 4.4 `ui/screens/layouts.rs` - Listar layouts salvos separados por tipo (CSV / XLSX) - Campo de nome + botão "Salvar layout atual" - Botão "Excluir" com modal de confirmação - Botão "Exportar para JSON" → diálogo de salvar com nome sugerido baseado no nome do layout - Botão "Importar de JSON" → diálogo de abertura - Modal de conflito de nome ao importar: "Sobrescrever" ou "Salvar com novo nome" ### 4.5 `ui/screens/resultado.rs` - Seções: **Faltantes** | **Duplicatas** | **Totais** - Agrupamento por série em todas as seções - Paginação com dropdown: 50 / 100 / 200 / 1000 itens por página - Valores monetários formatados em pt-BR (ex: `1.234,56`) - Números de notas sem zeros à esquerda (ex: `0001` exibido como `1`) - Botão "Exportar PDF" → chamar use case `exportar_pdf` ### 4.6 Modais globais (RNF06) | Modal | Trigger | |-------|---------| | Erro genérico | Qualquer erro com mensagem + botão OK | | Aviso consolidado | Final de importação com lista de categorias de problema | | Confirmação de intervalo | Faltantes > 10.000 por série | | Confirmação de exclusão | Excluir layout | | Conflito de nome | Importar JSON com nome existente | | Banco corrompido | Inicialização com `config.db` inválido | --- ## Fase 5 — Testes **Dependência:** Fases 1–4 concluídas (testes unitários podem ser escritos junto com cada fase). ### 5.1 Testes unitários — Domain - `parser_monetario`: todos os casos da tabela do PRD + casos extremos - `detector_sequencia`: sequência completa, com faltantes, série com um único registro, intervalos > 10.000 - `detector_duplicidade`: sem duplicatas, com duplicatas, múltiplas séries - Validação de série (regex `[0-9]{1,3}`): válidos e inválidos - Validação de numero zero: deve ser descartado ### 5.2 Testes de integração — Infrastructure - Leitura CSV: UTF-8, Windows-1252, diferentes delimitadores, linhas malformadas, arquivo > 50 MB - Leitura XLSX: aba correta, posição `LetraLinha`, arquivo corrompido, limite de tamanho - SQLite: migrations, CRUD de layouts, banco corrompido → recriação ### 5.3 Testes de integração — Application - Fluxo completo CSV → análise → resultado - Fluxo completo XLSX → análise → resultado - Importação de layout JSON: válido, malformado, conflito de nome com sobrescrita, conflito com renomeação - Intervalo > 10.000: verificar que `ResultadoPreAnalise` é retornado antes da expansão ### 5.4 Testes manuais de UI - Todas as mensagens em PT-BR - Formatação monetária correta (pt-BR) - Paginação funcionando - Geração do PDF com metadados corretos - Fluxo XLSX: aba exibida imediatamente após seleção do arquivo --- ## Fase 6 — Polimento e Empacotamento ### 6.1 Strings e idioma - Revisar todas as mensagens de erro/aviso para PT-BR (RNF05) - Garantir que nenhuma string de erro interna vaze para a UI como texto bruto ### 6.2 Performance - Validar com arquivo de 100.000 registros (RNF03) - Confirmar que detecção de faltantes é incremental (sem lista intermediária antes da confirmação) - Medir tempo de importação e análise ### 6.3 Build multiplataforma - Linux: validar caminho `~/.config/comparador-notas/config.db` - Windows: validar caminho `%APPDATA%\comparador-notas\config.db` - macOS: validar caminho `~/Library/Application Support/comparador-notas/config.db` ### 6.4 Empacotamento - Configurar ícone da aplicação - Build de release otimizado: `cargo build --release` - Testar executável standalone (sem instalação, sem dependências externas) --- ## Ordem de Execução Recomendada ``` Fase 0 → Fase 1 (com testes unitários) → Fase 2.1/2.2/2.3 → Fase 2.4/2.5 → Fase 3.1/3.2 → Fase 4.1/4.2/4.3/4.5 → Fase 2.6/3.3 → Fase 3.4/4.4 → Fase 5 → Fase 6 ``` > O núcleo de domínio deve estar **totalmente testado** antes de qualquer integração. O `parser_monetario` e o `detector_sequencia` são os componentes mais críticos do sistema. --- ## Dependências entre Componentes ``` domain/errors.rs └─ domain/entities/ (nota, serie, layout, resultado_analise) └─ domain/services/ (parser_monetario, detector_sequencia, detector_duplicidade) └─ application/usecases/ (importar_arquivo, executar_analise, exportar_pdf) └─ infrastructure/ (csv_reader, xlsx_reader, sqlite/, pdf_generator) └─ ui/ (app, screens/) ``` Nenhuma camada pode importar de uma camada acima dela na hierarquia.