From 9d212747e7c9ccd9e5a96cfa40451025f48bc9ab Mon Sep 17 00:00:00 2001 From: Felipe Canin Novaes Date: Mon, 2 Mar 2026 21:21:18 -0300 Subject: [PATCH] =?UTF-8?q?atualiza=20vers=C3=A3o=20do=20PRD=20para=201.5?= =?UTF-8?q?=20e=20detalha=20campos=20l=C3=B3gicos=20e=20regras=20de=20vali?= =?UTF-8?q?da=C3=A7=C3=A3o?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PRD.md | 85 ++++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 68 insertions(+), 17 deletions(-) diff --git a/PRD.md b/PRD.md index ad54925..dd03670 100644 --- a/PRD.md +++ b/PRD.md @@ -1,6 +1,6 @@ # PRD — Comparador de Notas -**Versão:** 1.3 +**Versão:** 1.5 **Data:** 02/03/2026 **Status:** Planejamento @@ -100,10 +100,10 @@ O sistema trabalhará com os seguintes campos lógicos: | Campo | Obrigatório | Descrição | | ------ | ----------- | ------------------------------------------------------------------------- | -| Numero | Sim | Número incremental da nota. Junto com a Série forma o identificador único | -| Serie | Sim | Série da nota (1–3 dígitos, ex: 001–999). Separa lotes de emissão | -| Valor | Não | Valor monetário da nota | -| Data | Não | Data de emissão da nota | +| Numero | Sim | Número incremental da nota. Junto com a Série forma o identificador único. 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. Quando mapeado, exibido como informação adicional no relatório PDF. Não participa de nenhuma regra de validação ou cálculo. | 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. @@ -253,6 +253,18 @@ Registros com o campo Numero igual a `0` devem ser descartados e reportados ao u Faltantes são detectados **por série**. Cada série possui sua própria sequência independente. +### Série com Apenas um Registro + +Se uma série contiver apenas um registro, o intervalo de sequência é `numero..numero`. Não há faltantes nesse caso. A série é processada e exibida normalmente. + +### Ordenação + +A ordenação dos registros dentro de cada série é 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. + ### Proteção contra Intervalos Anormalmente Grandes Se o intervalo de faltantes de qualquer série exceder **10.000 registros**, o sistema deve: @@ -267,11 +279,15 @@ Esse comportamento protege contra mapeamentos incorretos de colunas que gerariam ### Tratamento de Série com Valor Inválido -Se o campo Série de um registro estiver vazio ou contiver valor não utilizável: +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 @@ -299,20 +315,41 @@ O sistema deve calcular: ### Formato de Valor Aceito -O sistema deve aceitar valores numéricos com ponto **ou** vírgula como separador decimal, detectando o formato automaticamente por registro. +O sistema deve aceitar valores numéricos em formato brasileiro ou americano, detectando o formato automaticamente por registro seguindo o algoritmo abaixo. -| Exemplo de entrada | Resultado parseado | -| ------------------ | ------------------ | -| `1000.00` | 1000,00 | -| `1000,00` | 1000,00 | -| `1.000,00` | 1000,00 | -| `1,000.00` | 1000,00 | -| `1.000` | 1000,00 (ponto interpretado como separador de milhar quando seguido de exatamente 3 dígitos sem parte decimal subsequente) | +#### 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 série 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`). @@ -387,6 +424,8 @@ O sistema deve suportar no mínimo: 100.000 registros por arquivo +> **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 @@ -405,6 +444,16 @@ O idioma da interface é **Português do Brasil (PT-BR)**. 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 mal formação" +* "12 valores de Numero inválidos convertidos ou descartados" +* "5 registros com Série inválida 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 @@ -443,6 +492,7 @@ Sem dependências externas obrigatórias. | SQLite | rusqlite | Definido | | Leitura de CSV | csv | Definido | | Leitura de XLSX | calamine | Definido | +| Decimal fixo | rust_decimal | Definido | | Geração de PDF | genpdf | Definido | --- @@ -489,10 +539,11 @@ O diretório e o arquivo devem ser **criados automaticamente** pelo sistema na p 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. Recriar o banco de dados vazio -3. Continuar a execução normalmente +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. +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. ------------------------------------------------------------------------