Files
LerNota/openspec/changes/archive/2026-07-24-competencia/tasks.md
T

66 lines
6.8 KiB
Markdown

## 1. Migração de Schema (Banco de Dados)
- [x] 1.1 Em `app/database.py`, implementar rebuild de `fiscal_documents` (tabela `_new` com `mes INTEGER NOT NULL`, `ano INTEGER NOT NULL`, demais colunas iguais, sem `purchase_date`), copiando dados existentes com `mes = CAST(substr(purchase_date,6,2) AS INTEGER)` e `ano = CAST(substr(purchase_date,1,4) AS INTEGER)`, dentro de uma transação, guardado por checagem `PRAGMA table_info` (só roda se `purchase_date` ainda existir).
- [x] 1.2 Repetir o mesmo rebuild para `detected_documents`, com `mes INTEGER` e `ano INTEGER` nullable (mantendo o mesmo comportamento opcional que `purchase_date` tinha).
- [x] 1.3 Substituir o índice `idx_fiscal_date` por `idx_fiscal_competencia ON fiscal_documents(ano, mes)`.
- [x] 1.4 Testar a migração rodando `init_db()` contra uma cópia do banco atual (com dados de `purchase_date` já existentes) e validar que `mes`/`ano` foram preenchidos corretamente e que rodar `init_db()` de novo não falha nem duplica/recria o rebuild.
## 2. Normalização de Competência (`app/dates.py`)
- [x] 2.1 Promover a lista `_MESES` (hoje em `dashboard_routes.py`) para `app/dates.py` como fonte única de abreviações de mês.
- [x] 2.2 Implementar `resolve_competencia(date_raw, reference=None) -> tuple[int, int]`, substituindo `resolve_purchase_date`, com fallback para mês/ano corrente quando o valor for inválido/ausente.
- [x] 2.3 Implementar `format_competencia(mes: int, ano: int) -> str` no formato `"Jun/26"` (mês abreviado capitalizado + ano com 2 dígitos), substituindo `format_br_date`, e atualizar o filtro Jinja registrado em `app/templating.py` (`brdate` → filtro de competência).
- [x] 2.4 Ajustar `_is_plausible_purchase_date` (ou equivalente) para validar plausibilidade em termos de mês/ano (ex. não mais que alguns meses no futuro, ano não muito no passado), em vez de dias.
## 3. Extração (IA e OCR)
- [x] 3.1 Confirmar que `app/ai_extraction.py` e `lernotafiscal/extraction.py` continuam extraindo a data completa do documento sem alteração de prompt/regex (Decisão de design: descarte do dia acontece na normalização, não na extração).
- [x] 3.2 Renomear/ajustar `RawExtraction.purchase_date_raw` e `DetectedDocumentCandidate.purchase_date` apenas na camada de repasse (`app/ingestion.py:_candidate_to_raw`), garantindo que o valor bruto siga até `resolve_competencia` sem mudança de comportamento de extração.
## 4. Persistência e Consultas (`app/database.py`)
- [x] 4.1 Atualizar `insert_detected` e `update_staged` para gravar `mes`/`ano` (via `resolve_competencia`) em vez de `purchase_date`.
- [x] 4.2 Atualizar `confirm_batch` para exigir `mes`/`ano` válidos (em vez de `purchase_date` truthy) antes de promover um documento de staging para `fiscal_documents`.
- [x] 4.3 Atualizar `create_fiscal` e `update_fiscal` para receber `mes`/`ano` como parâmetros obrigatórios em vez de `purchase_date`.
- [x] 4.4 Atualizar `_fiscal_where` para filtrar por intervalo de competência usando a expressão `(ano * 12 + mes) BETWEEN ? AND ?`, substituindo o filtro por `purchase_date >= / <=`.
- [x] 4.5 Atualizar `FISCAL_SORT_COLUMNS` para expor ordenação por `(ano, mes)` no lugar de `purchase_date`.
- [x] 4.6 Atualizar `monthly_totals` para agrupar diretamente por `ano, mes` (`GROUP BY ano, mes ORDER BY ano, mes`), eliminando `substr(purchase_date, 1, 7)`.
- [x] 4.7 Atualizar `category_totals` para filtrar por competência usando a mesma expressão `(ano * 12 + mes)`.
## 5. Fluxo de Upload e Revisão (Staging)
- [x] 5.1 Em `app/routes/upload_routes.py`, atualizar o handler de upload (`POST /upload`) para gravar `mes`/`ano` (via `resolve_competencia`) em vez de `purchase_date` ao inserir em `detected_documents`.
- [x] 5.2 Atualizar `app/templates/staging.html`: substituir o `<input type="date" name="purchase_date">` por dois `<select>` — Mês (Jan-Dez) e Ano (2026-2030) — pré-selecionados com o valor detectado quando disponível.
- [x] 5.3 Atualizar o handler `POST /import/{batch_id}/update/{detected_id}` para receber e persistir `mes`/`ano` do formulário de revisão.
- [x] 5.4 Garantir que o cálculo de `summary["pendentes"]` (usado para bloquear o botão de confirmação do lote) passe a considerar `mes`/`ano` ausentes/inválidos como pendência, em vez de `purchase_date`.
## 6. Cadastro Manual de Documento
- [x] 6.1 Atualizar `app/templates/document_form.html`: substituir o `<input type="date" name="purchase_date">` pelos mesmos dois `<select>` de Mês/Ano usados na revisão, pré-preenchidos na edição.
- [x] 6.2 Atualizar os handlers de criação/edição em `app/routes/documents_routes.py` (`/documents/new`, `/documents/{id}/edit`) para ler `mes`/`ano` do formulário e repassar a `create_fiscal`/`update_fiscal`.
## 7. Listagem e Filtros (`/documents`)
- [x] 7.1 Atualizar `app/templates/documents_list.html`: substituir os filtros `<input type="date" name="start">`/`name="end"` por dois pares de `<select>` Mês/Ano ("De" e "Até").
- [x] 7.2 Atualizar o handler `GET /documents` em `app/routes/documents_routes.py` para ler os novos parâmetros de filtro de competência e repassá-los a `list_fiscal`/`fiscal_summary`.
- [x] 7.3 Atualizar a coluna "Data" em `documents_list.html` para exibir a competência formatada (`format_competencia`) e manter a ordenação por competência (`sort_url` apontando para `ano`/`mes`).
## 8. Dashboard
- [x] 8.1 Atualizar `app/routes/dashboard_routes.py`: `_mes_label` passa a usar `format_competencia`/`_MESES` centralizados em `app/dates.py`, consumindo `ano`/`mes` nativos vindos de `monthly_totals`.
- [x] 8.2 Atualizar `app/templates/dashboard.html`: tabela "Últimos documentos" exibe a competência (`format_competencia`) no lugar de `d.purchase_date | brdate`.
## 9. Scripts e Testes
- [x] 9.1 Atualizar `scripts/migrate_notas.py` para gravar `mes`/`ano` em vez de `purchase_date` ao chamar `create_fiscal`.
- [x] 9.2 Atualizar `tests/test_app.py` (usos de `resolve_purchase_date`, `create_fiscal`, `update_fiscal` com `purchase_date=...`) para o novo par de campos.
- [x] 9.3 Atualizar `tests/test_ingestion.py` (`docs[0].purchase_date`) para verificar `mes`/`ano` em vez de data completa.
- [x] 9.4 Atualizar `tests/test_categorization.py` (`db.create_fiscal(..., purchase_date=...)`) para o novo schema.
- [x] 9.5 Rodar a suíte de testes completa e corrigir quaisquer quebras remanescentes relacionadas a `purchase_date`.
## 10. Validação Manual
- [x] 10.1 Rodar a aplicação localmente, fazer upload de um documento de teste, confirmar que a tela de revisão exige seleção de Mês/Ano antes de habilitar a confirmação do lote, e validar que o documento aparece corretamente no Dashboard e em `/documents` após confirmado.
- [x] 10.2 Validar cadastro manual (criar/editar) de um documento fiscal usando os novos seletores de Mês/Ano.
- [x] 10.3 Validar o filtro por intervalo de competência em `/documents` e a ordenação pela coluna de competência.