Files
Felipe 2a21138bf8 feat: implement reimport and analysis functionality for XLSX files
- Added `reimportar_e_analisar` method to `App` struct to handle reimporting and analyzing files.
- Preserved previous analysis results to restore in case of errors or empty imports.
- Updated UI to include a button for reanalyzing the current file.
- Enhanced file selection logic to allow direct processing if a compatible preset is selected.
- Refactored import logic to streamline the analysis process for both CSV and XLSX files.
- Improved user feedback with appropriate error and success messages during file operations.
2026-03-03 23:28:55 -03:00

1061 lines
41 KiB
Markdown
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.
# PRD — Comparador de Notas
**Versão:** 1.7
**Data:** 03/03/2026
**Status:** Implementado (MVP)
---
# 1. Visão Geral
## 1.1 Nome do Produto
Comparador de Notas
## 1.2 Descrição
Software desktop multiplataforma destinado à análise de sequências numéricas de notas fiscais (ou documentos equivalentes) a partir da importação de planilhas.
O sistema identifica quebras de sequência, duplicidades e inconsistências, além de calcular totais financeiros, permitindo auditoria rápida e confiável.
O software deve ser configurável para suportar diferentes formatos de planilhas sem depender de um layout fixo.
---
# 2. Problema
Muitos sistemas legados não fornecem ferramentas adequadas para validação da sequência de notas fiscais.
Como consequência, usuários precisam criar controles manuais em planilhas para:
* Detectar notas faltantes
* Conferir integridade da sequência
* Somar valores
* Separar por série
Esse processo é:
* Manual
* Repetitivo
* Suscetível a erro humano
* Ineficiente
O produto elimina esse processo manual.
---
# 3. Objetivos
## 3.1 Objetivo Principal
Permitir que o usuário importe uma planilha e obtenha automaticamente:
* Notas faltantes
* Notas duplicadas
* Soma total dos valores
* Agrupamento por série e tipo de documento
## 3.2 Objetivos Secundários
* Permitir configuração flexível das colunas
* Permitir reutilização de configurações (presets)
* Reduzir tempo de auditoria
* Reduzir erros humanos
---
# 4. Escopo
## 4.1 Incluído
* Importação de arquivos CSV
* Importação de arquivos XLSX
* Interface gráfica
* Configuração de colunas (CSV por índice numérico; XLSX por letra+linha)
* Detecção de quebras de sequência
* Detecção de duplicidades
* Soma de valores
* Agrupamento por série e tipo de documento
* Pré-visualização das primeiras linhas do arquivo na tela de configuração
* Relatório visual com paginação
* Exibição de faltantes agrupados em intervalos contíguos (ex: `1050 (41 notas)`)
* Indicador de completude por série (ex: `48/50 notas — 96,0% completo`)
* Botão de cópia rápida de listas de faltantes/duplicatas para área de transferência
* Exportação de relatório para PDF (fontes Liberation Sans embutidas no binário)
* Salvar layouts personalizados (exclusivos por tipo de arquivo)
* Carregar layouts salvos
* Selecionar layout por menu dropdown
* Excluir layouts
* Exportar layout para JSON
* Importar layout de JSON
* Reanalisar arquivo sem reconfiguração (reimporta o mesmo arquivo com o layout atual)
* Análise em background thread (UI não bloqueia durante importação e análise)
## 4.2 Não Incluído (MVP)
* Integração com ERP
* Integração com banco de dados externo
* Multiusuário
* Acesso remoto
* Exportação de resultado em CSV
---
# 5. Modelo de Dados
O sistema trabalhará com os seguintes campos lógicos:
| Campo | Obrigatório | Descrição |
| --------------- | ----------- | ------------------------------------------------------------------------- |
| Numero | Sim | Número incremental da nota. Armazenado internamente como `u64`. |
| Serie | Sim | Série da nota. Deve conter de 1 a 3 dígitos numéricos (regex: `^[0-9]{1,3}$`, ex: `1`, `01`, `001`). Separa lotes de emissão. |
| Valor | Não | Valor monetário da nota. Armazenado internamente como decimal fixo (`rust_decimal`). |
| Data | Não | Data de emissão da nota. Exibida como informação adicional no relatório PDF. Não participa de nenhuma regra de validação ou cálculo. Formatos aceitos: `dd/mm/aaaa`, `aaaa-mm-dd` e `dd-mm-aaaa`. |
| TipoDocumento | Não | Tipo do documento (ex: `NFE`, `NFCE`). Quando mapeado, compõe a chave de agrupamento junto com a Série. Qualquer string não vazia é aceita. |
Os campos serão mapeados pelo usuário conforme o tipo de arquivo: via **índice numérico** (base 0) para CSV, e via **notação LetraLinha** (ex: `B3`) para XLSX.
> **Identificador único de uma nota:** combinação de `Numero + Serie + TipoDocumento`. Duplicidade e sequência são sempre avaliadas dentro do mesmo grupo `(Serie, TipoDocumento)`. Quando `TipoDocumento` não é mapeado, o agrupamento é feito somente por `Serie` (retrocompatível).
---
# 6. Requisitos Funcionais
## RF01 — Importação de Arquivo
O sistema deve permitir importar arquivos:
* CSV
* XLSX
### RF01.1 — Configurações de Importação CSV
| Parâmetro | Comportamento |
| ----------------- | ----------------------------------------------------------------------------- |
| Delimitador | Configurável pelo usuário: vírgula, ponto e vírgula ou tabulação |
| Encoding | Suportados: UTF-8 e Windows-1252 (Latin-1). Configurável pelo usuário |
| Linha do cabeçalho| Configurável pelo usuário (pode estar na linha 1, 4, etc.). 0 = sem cabeçalho |
| Linhas em branco | Devem ser ignoradas silenciosamente |
| Linhas malformadas| Devem ser ignoradas; o sistema deve reportar ao usuário quais linhas foram descartadas, sem interromper a importação |
### RF01.2 — Configurações de Importação XLSX
| Parâmetro | Comportamento |
| ---------------------- | -------------------------------------------------------------------------------------------------- |
| Seleção de aba | Imediatamente após a seleção do arquivo, o sistema exibe a lista de abas disponíveis para o usuário selecionar, antes de qualquer configuração de campos |
| Coluna e linha de início | O usuário informa a posição inicial de cada campo no formato `LetraLinha` (ex: `B3`) |
| Linhas em branco | Devem ser ignoradas silenciosamente |
| Linhas malformadas | Devem ser ignoradas; o sistema deve reportar ao usuário quais linhas foram descartadas |
| Arquivo corrompido | Se o arquivo não puder ser lido, exibir mensagem de erro em modal e limpar o arquivo carregado; o estado anterior é descartado |
### RF01.3 — Limite de Tamanho de Arquivo
O sistema deve recusar arquivos maiores que **50 MB** e exibir mensagem de erro ao usuário.
> O limite refere-se ao **tamanho do arquivo no disco** (tamanho comprimido para XLSX, que é um arquivo ZIP internamente). Base de cálculo: 100.000 registros com 510 colunas geram aproximadamente 515 MB em CSV e até 30 MB em XLSX. O limite de 50 MB oferece margem adequada.
---
## RF02 — Configuração de Colunas
O mapeamento de colunas varia conforme o tipo de arquivo.
### RF02.1 — Mapeamento CSV (índice numérico)
O usuário define, via **índice numérico** (posição da coluna, base 0), qual coluna representa cada campo:
* Qual índice representa o número (obrigatório)
* Qual índice representa a série (obrigatório)
* Qual índice representa o valor (opcional)
* Qual índice representa a data (opcional)
* Qual índice representa o tipo de documento (opcional)
### RF02.2 — Mapeamento XLSX (letra + linha)
O usuário define, no formato `LetraLinha`, a posição inicial de cada campo na planilha:
* Ex: `B3` indica coluna B a partir da linha 3
* Os campos mapeáveis são os mesmos: Numero (obrigatório), Serie (obrigatório), Valor (opcional), Data (opcional) e TipoDocumento (opcional)
> A linha informada é a **linha de início dos dados** (não o cabeçalho). O cabeçalho, se existir, é a linha imediatamente anterior.
### RF02.3 — Pré-visualização do Arquivo
A tela de configuração de colunas exibe as primeiras 5 linhas do arquivo com cabeçalho em notação de letras (A, B, C, ... com índice base-0 entre parênteses). A pré-visualização é atualizada automaticamente ao mudar o delimitador ou a aba selecionada.
### Comportamento de Memória
Após a importação, o arquivo permanece em memória e o usuário pode alterar o mapeamento de colunas e reprocessar sem selecionar o arquivo novamente. Ao importar um novo arquivo, os dados do arquivo anterior são descartados da memória.
### Tratamento de Erros de Configuração
| Situação | Comportamento |
| ----------------------------------------------------------- | ---------------------------------------------------------------- |
| Índice/posição configurado não existe no arquivo importado | Exibir erro ao usuário identificando qual campo está inválido |
| Campo obrigatório (Numero ou Serie) não mapeado | Bloquear execução da análise e solicitar configuração |
| Campo opcional não mapeado | Ignorar o campo; funcionalidades dependentes ficam desabilitadas |
| Dois campos mapeados para o mesmo índice/posição | Bloquear e exibir erro de validação imediatamente, antes de executar a análise |
---
## RF03 — Presets
O sistema deve permitir:
* Salvar configurações (armazenadas internamente via SQLite)
* Carregar configurações existentes
* Exportar um layout para arquivo JSON
* Importar um layout a partir de arquivo JSON
O formato JSON é utilizado exclusivamente para importação e exportação de layouts entre dispositivos ou sistemas.
O armazenamento interno dos layouts é realizado via SQLite (ver Seção 10).
### Tipo de Layout
Cada layout é exclusivo de um tipo de arquivo: **CSV** ou **XLSX**. Um layout CSV armazena configurações específicas de CSV (delimitador, encoding, índices de coluna). Um layout XLSX armazena configurações específicas de XLSX (aba, posições no formato `LetraLinha`).
### Conflito ao Importar Layout JSON
Se o usuário importar um arquivo JSON com o nome de um layout já existente no banco, o sistema deve perguntar ao usuário o que fazer, oferecendo as opções:
* Sobrescrever o layout existente
* Cancelar a importação
### Erros na Importação de Layout JSON
Se o arquivo JSON estiver malformado ou com campos obrigatórios ausentes, o sistema deve exibir mensagem de erro descrevendo o problema e cancelar a importação. Nenhum dado parcial deve ser salvo.
### Nome do Arquivo ao Exportar Layout
Ao exportar um layout para JSON, o sistema deve sugerir o nome do arquivo com base no nome do layout (ex: layout `Padrão CSV``Padrão CSV.json`). O usuário pode alterar o nome antes de salvar.
### Limite de Layouts
Não há limite no número de layouts que podem ser armazenados.
---
## RF04 — Detecção de Sequência
O sistema deve:
* Ordenar os registros por número dentro de cada grupo `(Serie, TipoDocumento)`
* Detectar números faltantes na sequência
### Regra de Detecção
A sequência é avaliada entre o **menor** e o **maior** número encontrado dentro de cada grupo. Qualquer número ausente nesse intervalo é considerado faltante.
Exemplo: série 001 / NFE contém os números `0001, 0002, 0003, 0005``0004` está faltando.
### Tratamento de Valores Não Numéricos
Se o valor de um campo Numero não for numérico (ex: `NF-001`, `ABC`):
1. O sistema deve tentar extrair apenas os dígitos do valor (ex: `NF-001``1`)
2. Se após a extração restar um número válido, utilizá-lo
3. Se não restar valor numérico utilizável, descartar o registro
4. Em ambos os casos, reportar ao usuário quais registros foram afetados
### Número Zero
Registros com o campo Numero igual a `0` devem ser descartados e reportados ao usuário. O valor `0` não é considerado um número de nota válido.
### Escopo da Detecção
Faltantes são detectados **por grupo `(Serie, TipoDocumento)`**. Cada grupo possui sua própria sequência independente.
### Série com Apenas um Registro
Se um grupo contiver apenas um registro, o intervalo de sequência é `numero..numero`. Não há faltantes nesse caso. O grupo é processado e exibido normalmente.
### Ordenação
A ordenação dos registros dentro de cada grupo é sempre **numérica crescente**, independentemente do formato original do campo Numero no arquivo de entrada.
### Exibição do Campo Numero
O campo Numero é armazenado e processado como inteiro. Na exibição (listas de faltantes, duplicatas e relatório PDF), o número é exibido **sem zeros à esquerda** (ex: `0001` é exibido como `1`). O formato de exibição não altera a lógica de detecção ou ordenação.
### Agrupamento de Faltantes Contíguos
Na tela de resultado, faltantes consecutivos são exibidos agrupados em intervalos (ex: `1050 (41 notas)`) para facilitar a leitura. Faltantes isolados são exibidos individualmente (ex: `• 75`).
### Proteção contra Intervalos Anormalmente Grandes
Se o intervalo de faltantes de qualquer grupo exceder **10.000 registros**, o sistema deve:
1. Interromper o processamento desse grupo
2. Exibir aviso informando o intervalo calculado (ex: "Série 001 / NFE: intervalo de 999.996 faltantes detectado")
3. Solicitar confirmação do usuário antes de continuar
Se o usuário confirmar, o sistema deve listar todos os faltantes normalmente, com paginação. Não há truncamento da lista após a confirmação.
Esse comportamento protege contra mapeamentos incorretos de colunas que gerariam listas ilegíveis.
### Tratamento de Série com Valor Inválido
A Série é válida se e somente se corresponder à regex `^[0-9]{1,3}$` após remoção de espaços.
Se o campo Série de um registro estiver vazio, não corresponder à regex ou contiver valor não utilizável:
1. O registro deve ser descartado
2. O sistema deve reportar ao usuário quais linhas foram afetadas, sem interromper a importação
Exemplos de valores inválidos: `ABC`, `1A`, `1234` (4 dígitos), string vazia.
---
## RF05 — Detecção de Duplicidade
O sistema deve identificar registros duplicados.
Um registro é considerado duplicado quando existe mais de uma ocorrência da mesma combinação **Numero + Serie + TipoDocumento** no arquivo importado.
### Exibição das Duplicatas
A lista de duplicatas exibe o identificador (`Numero + Serie + TipoDocumento`) e a contagem de ocorrências por grupo. Exemplo: `NF 0004 / Série 001 / NFE — 3 ocorrências`. As ocorrências individuais não são listadas separadamente.
### Impacto na Soma de Valores
Todas as ocorrências de registros duplicados são incluídas na soma de valores, pois refletem os lançamentos reais presentes no arquivo.
---
## RF06 — Soma de Valores
O sistema deve calcular:
* Soma total
* Soma por grupo `(Serie, TipoDocumento)`
### Formato de Valor Aceito
O sistema deve aceitar valores numéricos em formato brasileiro ou americano, detectando o formato automaticamente por registro seguindo o algoritmo abaixo.
O parser remove prefixos `R$` (maiúsculo ou minúsculo) e espaços antes do processamento.
#### Algoritmo de Parsing Monetário
**Regra 1 — Contém ambos ponto e vírgula:**
- O último separador encontrado é o decimal; o outro é o separador de milhar.
- Exemplos: `1.000,00` → 1.000,00 | `1,000.00` → 1.000,00
**Regra 2 — Contém apenas um separador:**
- Vírgula ou ponto com **exatamente 2 dígitos** após → separador decimal.
- Exemplos: `1000,00` → 1.000,00 | `1000.00` → 1.000,00
- Vírgula ou ponto com **exatamente 3 dígitos** após → separador de milhar.
- Exemplos: `1,234` → 1.234,00 | `1.234` → 1.234,00
- Demais casos → separador tratado como decimal.
**Regra 3 — Sem separador:**
- Interpretar como número inteiro.
- Exemplo: `1000` → 1.000,00
| Exemplo de entrada | Regra aplicada | Resultado parseado |
| ------------------ | ---------------------------------- | ------------------ |
| `1000.00` | Regra 2 (ponto + 2 dígitos) | 1.000,00 |
| `1000,00` | Regra 2 (vírgula + 2 dígitos) | 1.000,00 |
| `1.000,00` | Regra 1 (ambos separadores) | 1.000,00 |
| `1,000.00` | Regra 1 (ambos separadores) | 1.000,00 |
| `1.000` | Regra 2 (ponto + 3 dígitos) | 1.000,00 |
| `1,234` | Regra 2 (vírgula + 3 dígitos) | 1.234,00 |
| `1000` | Regra 3 (sem separador) | 1.000,00 |
Valores negativos (precedidos de `-`) devem ser rejeitados e reportados ao usuário como inválidos.
Valores que não puderem ser interpretados como número devem ser descartados e reportados ao usuário.
> **Nota de implementação:** O valor deve ser parseado e armazenado internamente como `rust_decimal::Decimal`, nunca como `f64`. Aritmética de ponto flutuante introduz erros de representação em valores monetários (ex: `0.1 + 0.2 ≠ 0.3` em IEEE 754). A soma total e as somas por grupo devem ser calculadas inteiramente em `Decimal`.
### Formato de Exibição de Valores
Todos os valores monetários são exibidos com **2 casas decimais fixas** no formato brasileiro (ex: `1.234,56`).
---
## RF07 — Exibição de Resultados
O sistema deve exibir:
* Lista de notas faltantes agrupadas por `(Serie, TipoDocumento)`, com faltantes contíguos agrupados em intervalos
* Indicador de completude por grupo (ex: `48/50 notas — 96,0% completo`)
* Lista de duplicadas agrupadas por `(Serie, TipoDocumento)`
* Totais (soma total e soma por grupo, com contagem de notas por grupo)
* Botão de cópia rápida de listas para a área de transferência
### Organização dos Resultados
| Aspecto | Comportamento |
| ------------- | ----------------------------------------------------- |
| Agrupamento | Resultados sempre agrupados por `(Serie, TipoDocumento)`. Quando TipoDocumento não está mapeado, o label do grupo exibe apenas a Série |
| Listas longas | Paginação — o usuário navega entre páginas de resultados |
| Itens por página | Selecionável via dropdown com as opções: 50, 100, 200, 1000 |
### RF07.1 — Exportação de Relatório
O sistema deve permitir exportar o relatório de resultados para **PDF** utilizando a biblioteca `genpdf`.
As fontes do PDF (Liberation Sans) são embutidas no binário em tempo de compilação, eliminando dependência de fontes instaladas no sistema operacional.
O PDF deve conter:
* Notas faltantes por grupo `(Serie, TipoDocumento)`
* Duplicatas por grupo
* Totais por grupo e total geral
**Metadados do relatório:**
* Nome do arquivo importado
* Data e hora da geração
* Nome do layout utilizado (se houver)
### RF07.2 — Reanalisar Arquivo
O sistema deve permitir reimportar o mesmo arquivo do disco com o layout atual e executar a análise novamente, sem nenhuma interação adicional. A operação é executada em background thread para não bloquear a interface.
---
## RF08 — Gerenciamento de Layouts
* O sistema deve permitir salvar layouts personalizados.
* O sistema deve permitir listar layouts salvos.
* O sistema deve permitir selecionar layouts salvos.
* O sistema deve permitir excluir layouts.
* Os layouts devem ser armazenados localmente via SQLite.
> A exibição dos resultados de análise é coberta pelo RF07.
---
# 7. Requisitos Não Funcionais
## RNF01 — Plataforma
O sistema deve funcionar em:
* Windows
* Linux
* macOS
---
## RNF02 — Execução Offline
O sistema deve funcionar sem conexão com internet.
---
## RNF03 — Performance
O sistema deve suportar no mínimo:
100.000 registros por arquivo
A análise é executada em uma thread separada (background) para não bloquear a interface gráfica durante o processamento.
> **Nota de implementação:** A detecção de faltantes deve ser implementada de forma **incremental** — ordenar a lista e percorrer comparando elementos consecutivos — evitando a geração de listas intermediárias completas antes da confirmação do usuário. Isso garante consumo de memória proporcional aos dados reais, não ao intervalo.
---
## RNF04 — Usabilidade
O sistema deve possuir interface simples.
---
## RNF05 — Idioma
O idioma da interface é **Português do Brasil (PT-BR)**.
---
## RNF06 — Mensagens de Erro e Aviso
Todas as mensagens de erro e aviso devem ser exibidas em **modal/popup bloqueante**. O usuário deve fechar o modal explicitamente para continuar.
### Consolidação de Mensagens
Quando múltiplos avisos forem gerados durante uma mesma operação (ex: múltiplas linhas malformadas, múltiplos registros descartados), esses avisos devem ser **consolidados em um único modal**, exibindo um resumo ao final da operação. Exemplo de conteúdo consolidado:
* "32 linhas descartadas por malformação"
* "12 valores de Numero inválidos convertidos ou descartados"
* "5 registros com Série inválida descartados"
* "3 valores monetários inválidos descartados"
Cada categoria de problema deve ser exibida como um item separado dentro do mesmo modal. Nunca devem ser abertos múltiplos modais sequenciais para a mesma operação de importação.
---
# 8. Fluxo do Usuário
Fluxo principal:
1. Usuário abre o sistema
2. Usuário importa planilha
3. (Para XLSX) Usuário seleciona aba
4. Usuário configura colunas (com pré-visualização das primeiras 5 linhas)
5. Usuário executa análise (processamento em background)
6. Sistema exibe resultado
Fluxo alternativo — layout salvo:
1. Usuário abre o sistema
2. Usuário seleciona layout no dropdown
3. Usuário importa planilha → análise é disparada automaticamente
---
# 9. Arquitetura
Arquitetura desktop local com separação em quatro camadas: `domain`, `application`, `infrastructure` e `ui`.
Não é Clean Architecture radical. É apenas separação suficiente para manter fronteiras claras, domínio isolado e infraestrutura concreta sem vazar para a lógica de negócio.
---
## 9.1 Estrutura de Pastas
```
src/
├─ main.rs
├─ ui/
│ ├─ mod.rs
│ ├─ app.rs
│ ├─ screens/
│ │ ├─ mod.rs (renderizar_tabela_preview, indice_para_letra)
│ │ ├─ import.rs
│ │ ├─ configuracao_colunas.rs
│ │ ├─ layouts.rs
│ │ ├─ resultado.rs
├─ application/
│ ├─ mod.rs
│ ├─ usecases/
│ │ ├─ mod.rs
│ │ ├─ importar_arquivo.rs
│ │ ├─ executar_analise.rs
│ │ ├─ exportar_pdf.rs
│ │ ├─ layouts.rs
├─ domain/
│ ├─ mod.rs
│ ├─ errors.rs
│ ├─ entities/
│ │ ├─ mod.rs
│ │ ├─ nota.rs
│ │ ├─ serie.rs
│ │ ├─ chave_serie.rs
│ │ ├─ layout.rs
│ │ ├─ resultado_analise.rs
│ │
│ ├─ services/
│ │ ├─ mod.rs
│ │ ├─ 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
```
---
## 9.2 Papel de Cada Camada
### Domain (núcleo puro)
Contém toda a lógica de negócio real.
**Não pode depender de:**
* egui / eframe
* rusqlite
* calamine
* csv
* genpdf
Apenas Rust puro + crates matemáticas (`rust_decimal`, `chrono`) e utilitários de erros (`thiserror`, `regex`).
#### Entidades
**`Nota`**
| Campo | Tipo |
| -------------- | ------------------- |
| numero | `u64` |
| serie | `String` |
| documento_tipo | `Option<String>` |
| valor | `Option<Decimal>` |
| data | `Option<NaiveDate>` |
**`ChaveSerie`**
Chave composta que identifica um grupo de notas. Combina `serie` e `documento_tipo`. Quando `documento_tipo` é `None`, o comportamento é idêntico ao agrupamento somente por série (retrocompatível). Implementa `Hash`, `Eq`, `Ord` para uso como chave de `HashMap` e chave de ordenação.
| Campo | Tipo |
| -------------- | ---------------- |
| serie | `String` |
| documento_tipo | `Option<String>` |
O método `label()` formata para exibição: `"001 / NFE"` quando tipo presente, `"001"` quando ausente.
**`ResultadoPreAnalise`**
Resultado intermediário, antes de materializar os faltantes.
| Campo | Tipo |
| -------------------- | ---------------------------------------- |
| intervalos_por_serie | `HashMap<ChaveSerie, IntervaloSerie>` |
| duplicadas_por_serie | `HashMap<ChaveSerie, Vec<(u64, usize)>>` |
| soma_total | `Decimal` |
| soma_por_serie | `HashMap<ChaveSerie, Decimal>` |
| total_por_serie | `HashMap<ChaveSerie, usize>` |
**`ResultadoAnalise`**
| Campo | Tipo |
| -------------------- | ---------------------------------------- |
| faltantes_por_serie | `HashMap<ChaveSerie, Vec<u64>>` |
| duplicadas_por_serie | `HashMap<ChaveSerie, Vec<(u64, usize)>>` |
| soma_total | `Decimal` |
| soma_por_serie | `HashMap<ChaveSerie, Decimal>` |
| total_por_serie | `HashMap<ChaveSerie, usize>` |
> **Importante:** a geração dos faltantes não deve ser eager. O use case `executar_analise` deve primeiro calcular os intervalos por série e retornar um resultado intermediário (`ResultadoPreAnalise`) contendo o intervalo calculado. Somente após confirmação do usuário — quando algum intervalo exceder 10.000 registros (RF04) — o sistema expande e materializa a lista completa de faltantes. Isso evita alocar memória para intervalos gerados por mapeamento incorreto de colunas.
#### Services
`detector_sequencia` — recebe `Vec<&Nota>` agrupadas por `ChaveSerie`, retorna faltantes. Funções:
- `calcular_intervalo` — retorna `IntervaloSerie` sem materializar a lista completa
- `detectar_faltantes` — materializa a lista completa após confirmação
- `agrupar_contiguos` — agrupa uma lista ordenada de faltantes em pares `(inicio, fim)` para exibição compacta
`detector_duplicidade` — retorna mapa de contagem por `(numero, serie, documento_tipo)`.
`parser_monetario` — implementa exatamente o algoritmo definido no RF06. Também expõe `formatar_valor_br` para exibição no formato `1.234,56`.
#### Erros
`domain/errors.rs` define os erros do domínio de forma tipada (ex: `ErroSerie::Invalida`, `ErroNumero::Zero`, `ErroValor::Negativo`, `ErroLayout::NomeConflitante`, `ErroArquivo::TamanhoExcedido`). Nenhuma camada deve propagar `String` livre como erro de domínio.
`ResumoAvisos` consolida contagens de linhas malformadas, números inválidos, séries inválidas e valores inválidos para exibição em um único modal ao final da importação.
---
### Application (orquestração)
Coordenam o fluxo entre domain e infrastructure.
Conhece o domain. O domain não conhece o application.
**`executar_analise.rs`**
Expõe três funções:
1. `pre_analisar(notas)` — calcula intervalos, duplicatas e somas sem expandir faltantes
2. `series_com_intervalo_excessivo(pre)` — retorna grupos com contagem acima de `LIMITE_FALTANTES` (10.000)
3. `expandir_analise(pre, notas)` — materializa a lista completa de faltantes após confirmação
**`exportar_pdf.rs`**
Depende da trait abstrata `PdfGenerator` definida em `infrastructure/pdf_generator.rs`. Isso evita que o application dependa diretamente de `genpdf`.
**`importar_arquivo.rs`**
Expõe:
- `importar_csv(caminho, config)` — lê CSV e mapeia para notas
- `importar_xlsx(caminho, config)` — lê XLSX e mapeia para notas
- `listar_abas_xlsx(caminho)` — lista abas antes de configurar
**`layouts.rs`**
Expõe operações de CRUD e import/export de layouts: `salvar_layout`, `listar_layouts`, `excluir_layout`, `exportar_layout_json`, `importar_layout_json`.
---
### Infrastructure (implementações concretas)
Implementa leitores, persistência e geração de arquivos.
| Arquivo | Responsabilidade |
| -------------------------------- | ----------------------------------------- |
| `csv_reader.rs` | Leitura de arquivos CSV via `csv`; `preview_csv` |
| `xlsx_reader.rs` | Leitura de arquivos XLSX via `calamine`; `preview_xlsx`, `parsear_letra_linha`, `listar_abas` |
| `pdf_generator.rs` | Trait `PdfGenerator` + implementação `GenpdfGenerator` via `genpdf`; fontes Liberation Sans embutidas no binário |
| `sqlite/connection.rs` | Abertura e inicialização da conexão SQLite; tratamento de banco corrompido |
| `sqlite/migrations.rs` | Aplicação de migrations de schema (versão atual: 3) |
| `sqlite/layout_repository.rs` | CRUD de layouts via `rusqlite` |
Nada de infrastructure sobe para domain.
---
### UI (interface)
Apenas coleta input, chama use cases e renderiza resultado.
Nenhuma regra de sequência ou parsing monetário deve estar na camada de UI.
#### App (estado global)
`app.rs` contém o estado global da aplicação (`App`), o enum `EstadoApp`, os tipos `Modal`/`TipoModal`/`AcaoModal`, e a lógica de processamento de resultados assíncronos via `mpsc::channel`.
**Estados da aplicação:**
| Estado | Descrição |
| ----------------------- | --------- |
| `Importando` | Tela inicial: seleção de arquivo e layout |
| `SelecionandoAba` | Aguardando seleção de aba XLSX |
| `ConfigurandoColunas` | Mapeamento de colunas com pré-visualização |
| `Analisando` | Análise em execução em background thread |
| `ConfirmandoIntervalo` | Aguardando confirmação do usuário para expandir faltantes |
| `ExibindoResultado` | Resultado pronto para exibição |
| `GerenciandoLayouts` | Gerenciamento de layouts salvos |
#### Screens
| Arquivo | Responsabilidade |
| ------------------------- | ----------------------------------------------------- |
| `mod.rs` | `renderizar_tabela_preview` e `indice_para_letra` |
| `import.rs` | Seleção de arquivo, dropdown de layout, seleção de aba XLSX |
| `configuracao_colunas.rs` | Mapeamento de colunas com pré-visualização (RF02) |
| `layouts.rs` | Gerenciamento de layouts: salvar, carregar, excluir, exportar/importar JSON (RF08) |
| `resultado.rs` | Exibição de resultados com paginação, intervalos contíguos, indicador de completude, botões copiar/reanalisar/exportar PDF (RF07) |
---
## 9.3 Fluxo de Execução
```
UI → Application → Domain
Infrastructure entra apenas quando necessário.
```
Exemplo real:
1. UI chama `executar_importacao` (thread separada)
2. Infrastructure lê CSV/XLSX
3. Application transforma registros em entidades `Nota`
4. Domain executa pré-análise (`pre_analisar`)
5. Se intervalo excessivo: UI solicita confirmação → Domain expande faltantes (`expandir_analise`)
6. Application retorna `ResultadoAnalise` via `mpsc::channel`
7. UI renderiza
---
## 9.4 Stack Tecnológica
| Camada | Tecnologia | Versão |
| ---------------- | ------------------ | ------- |
| Linguagem | Rust | edition 2024 |
| Framework de UI | egui + eframe | 0.31 |
| Diálogos nativos | rfd | 0.15 |
| SQLite | rusqlite (bundled) | 0.32 |
| Leitura de CSV | csv | 1.3 |
| Leitura de XLSX | calamine | 0.26 |
| Decimal fixo | rust_decimal | 1.36 |
| Geração de PDF | genpdf | 0.2 |
| Datas | chrono | 0.4 |
| Encoding | encoding_rs | 0.8 |
| Caminhos de dados| dirs | 5 |
| Erros tipados | thiserror | 2 |
| Regex | regex | 1 |
| Serialização | serde + serde_json | 1 |
---
# 10. Persistência de Dados
## 10.1 Visão Geral
O sistema deve possuir mecanismo de persistência de dados local para
armazenamento de layouts criados pelo usuário.
Esse mecanismo deve permitir que o usuário salve, carregue, selecione e
exclua layouts sem a necessidade de importar arquivos de configuração
manualmente.
------------------------------------------------------------------------
## 10.2 Tecnologia
O sistema deve utilizar banco de dados local SQLite.
Motivos:
- Não requer servidor
- Funciona offline
- Arquivo único
- Multiplataforma
- Alta confiabilidade
O banco de dados deve ser armazenado na **pasta de dados do usuário**, de acordo com o sistema operacional:
| Sistema Operacional | Caminho |
| ------------------- | ---------------------------------------------------- |
| Linux | `~/.config/comparador-notas/config.db` |
| Windows | `%APPDATA%\comparador-notas\config.db` |
| macOS | `~/Library/Application Support/comparador-notas/config.db` |
O diretório e o arquivo devem ser **criados automaticamente** pelo sistema na primeira execução.
------------------------------------------------------------------------
## 10.2.1 — Banco de Dados Corrompido
Se o arquivo `config.db` estiver ilegível ou corrompido ao iniciar o programa, o sistema deve:
1. Exibir aviso ao usuário informando que o banco de dados está corrompido e será recriado
2. Renomear o arquivo corrompido para `config.db.bak` (sobrescrevendo qualquer `.bak` anterior)
3. Criar um novo banco de dados vazio
4. Continuar a execução normalmente
Os layouts salvos anteriormente serão perdidos neste cenário. O arquivo `config.db.bak` permanece no disco e permite recuperação manual por usuários avançados.
------------------------------------------------------------------------
## 10.2.2 — Migração de Schema
O banco de dados deve conter uma tabela de controle de versão (`schema_version`) com o número da versão atual do schema. A cada inicialização, o sistema deve verificar a versão e aplicar migrations automáticas quando necessário, garantindo compatibilidade com versões anteriores do banco.
**Versão atual do schema: 3**
| Versão | Alteração |
| ------ | --------- |
| 1 | Criação da tabela `layouts` |
| 2 | Índice único em `layouts.nome`; renomeia duplicatas com sufixo `(id)` |
| 3 | Adição das colunas `indice_documento_tipo` (CSV) e `pos_documento_tipo` (XLSX) |
------------------------------------------------------------------------
## 10.3 Dados Armazenados
O sistema deve armazenar:
Layouts personalizados
------------------------------------------------------------------------
## 10.4 Entidade: Layout
Cada layout é exclusivo de um tipo de arquivo (`csv` ou `xlsx`). As configurações variam conforme o tipo.
**Campos comuns:**
| Campo | Tipo | Descrição |
| ---------- | ------- | -------------------------------------- |
| id | inteiro | Identificador único (auto-incremento) |
| nome | texto | Nome do layout (único no banco) |
| tipo | texto | Tipo do arquivo: `csv` ou `xlsx` |
**Campos exclusivos de layouts CSV:**
| Campo | Tipo | Descrição |
| ---------------------- | ------- | ---------------------------------------------------------- |
| delimitador | texto | Caractere delimitador (`,`, `;`, `\t`) |
| encoding | texto | Encoding do arquivo (`utf-8` ou `windows-1252`) |
| linha_cabecalho | inteiro | Número da linha do cabeçalho (base 1). 0 = sem cabeçalho |
| indice_numero | inteiro | Índice da coluna Numero (base 0) |
| indice_serie | inteiro | Índice da coluna Serie (base 0) |
| indice_valor | inteiro | Índice da coluna Valor (base 0, nulo se ausente) |
| indice_data | inteiro | Índice da coluna Data (base 0, nulo se ausente) |
| indice_documento_tipo | inteiro | Índice da coluna TipoDocumento (base 0, nulo se ausente) |
**Campos exclusivos de layouts XLSX:**
| Campo | Tipo | Descrição |
| ------------------ | ------ | ----------------------------------------------------------------- |
| aba | texto | Nome da aba a ser processada |
| pos_numero | texto | Posição inicial da coluna Numero no formato `LetraLinha` (ex: `D3`) |
| pos_serie | texto | Posição inicial da coluna Serie no formato `LetraLinha` (ex: `B3`) |
| pos_valor | texto | Posição inicial da coluna Valor (nulo se ausente) |
| pos_data | texto | Posição inicial da coluna Data (nulo se ausente) |
| pos_documento_tipo | texto | Posição inicial da coluna TipoDocumento (nulo se ausente) |
------------------------------------------------------------------------
## 10.5 Requisitos Funcionais Relacionados
O sistema deve permitir:
Salvar layout
Listar layouts
Selecionar layout
Excluir layout
------------------------------------------------------------------------
## 10.6 Interface do Usuário
Os layouts salvos devem ser exibidos em um menu dropdown, filtrado pelo tipo de arquivo atual (CSV ou XLSX).
O usuário deve poder:
Selecionar layout existente
Criar novo layout (via modal com campo de texto ou via tela de gerenciamento)
Excluir layout (com confirmação)
Exportar layout para JSON
Importar layout de JSON (com tratamento de conflito de nome)
------------------------------------------------------------------------
## 10.7 Requisitos Não Funcionais
O banco de dados deve:
Funcionar offline
Não depender de serviços externos
Não exigir configuração manual
Ser criado automaticamente pelo sistema
------------------------------------------------------------------------
## 10.8 Futuras Expansões
O banco de dados poderá armazenar futuramente:
Histórico de análises
Relatórios
Configurações do usuário
# 11. Formato de Configuração (Import/Export)
Quando o usuário exportar ou importar um layout, o arquivo gerado será no formato JSON.
Esse formato é usado apenas para portabilidade entre dispositivos.
O armazenamento interno é sempre via SQLite.
O campo `tipo` define qual conjunto de configurações está presente no arquivo.
**Exemplo — layout CSV:**
```json
{
"tipo": "csv",
"nome": "Layout Padrão CSV",
"delimitador": ";",
"encoding": "utf-8",
"linha_cabecalho": 1,
"indice_numero": 3,
"indice_serie": 1,
"indice_valor": 5,
"indice_data": null,
"indice_documento_tipo": null
}
```
**Exemplo — layout XLSX:**
```json
{
"tipo": "xlsx",
"nome": "Layout Padrão XLSX",
"aba": "Plan1",
"pos_numero": "D3",
"pos_serie": "B3",
"pos_valor": "F3",
"pos_data": null,
"pos_documento_tipo": null
}
```
> **Nota:** o campo `tipo` é usado como tag de discriminante pelo `serde` (`#[serde(tag = "tipo")]`). O campo `indice_documento_tipo` / `pos_documento_tipo` usa `#[serde(default)]` para retrocompatibilidade com arquivos JSON exportados antes da versão 1.7.
---
# 12. Critérios de Aceite
O sistema será considerado funcional quando:
* Importar planilha CSV com configurações de delimitador, encoding e linha de cabeçalho
* Importar planilha XLSX com seleção de aba e posicionamento por `LetraLinha`
* Detectar notas faltantes por grupo `(Serie, TipoDocumento)` corretamente
* Detectar duplicatas por grupo corretamente
* Calcular soma total e soma por grupo corretamente
* Exibir resultados agrupados por grupo com paginação e intervalos contíguos
* Exibir indicador de completude por grupo
* Exportar relatório de resultados para PDF
* Salvar, carregar, selecionar e excluir layouts
* Exportar e importar layouts via JSON
* Exibir aviso de confirmação quando intervalo de faltantes exceder 10.000 por grupo
* Executar análise em background sem bloquear a interface
* Exibir pré-visualização das primeiras 5 linhas do arquivo na tela de configuração
---
# 13. MVP — Versão Inicial
O MVP inclui o escopo completo descrito neste PRD:
* Importar CSV e XLSX
* Mapear colunas Numero, Serie, Valor, Data e TipoDocumento
* Detectar notas faltantes por grupo `(Serie, TipoDocumento)`
* Detectar duplicatas por grupo
* Calcular soma total e por grupo
* Exibir resultados agrupados por grupo com paginação e intervalos contíguos
* Exportar relatório para PDF
* Gerenciar layouts (salvar, carregar, excluir, exportar/importar JSON)
* Reanalisar arquivo sem reconfiguração
---
# 14. Evoluções Futuras
Possíveis melhorias (ver `FEATURES_BACKLOG.md` para detalhes):
* Exportação de resultado em CSV (`faltantes.csv`, `duplicatas.csv`)
* Busca por número na tela de resultado
* Auto-detecção de delimitador CSV
* Auto-detecção de encoding CSV
* Agrupamento de faltantes como intervalos no PDF
* Integração com ERP
* Histórico de análises
* Automação de importação (monitorar pasta)
* Multiusuário
---
# 15. Definições
Nota
Documento com número sequencial.
Série
Agrupador independente de sequência.
TipoDocumento
Subtipo de documento dentro de uma série (ex: NFE, NFCE). Quando mapeado, compõe a chave de agrupamento junto com a Série.
ChaveSerie
Chave composta `(Serie, TipoDocumento)` que identifica um grupo de notas para fins de detecção de sequência, duplicidade e cálculo de somas.
Sequência
Ordem numérica crescente sem lacunas dentro de um mesmo grupo `(Serie, TipoDocumento)`.
---