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

5.7 KiB

Why

Hoje o LerNotaFiscal extrai e persiste uma data completa (purchase_date, YYYY-MM-DD) do documento fiscal via OCR/IA, mas o valor real de negócio para o usuário é apenas o mês de referência do gasto (competência), não o dia exato. A extração de dia é frequentemente a fonte de baixa confiança/ilegibilidade no OCR (regex DATE_RE e checagem _is_plausible_purchase_date), o dashboard já converte a data para "mês/ano" para exibição (_mes_label, substr(purchase_date, 1, 7)), e o formulário manual já reforça a convenção "1º dia do mês corrente" como fallback. Trocar o campo de data por competência (mês/ano) simplifica a extração, elimina uma fonte de erro/ilegibilidade desnecessária e alinha o dado armazenado com o que o Dashboard e os relatórios realmente precisam.

What Changes

  • BREAKING: Remover a coluna purchase_date (data completa) de fiscal_documents e detected_documents, substituindo por dois campos: mes (inteiro 1-12) e ano (inteiro, ex. 2026).
  • Atualizar a extração via IA (app/ai_extraction.py) e o fallback OCR (lernotafiscal/extraction.py) para não mais extrair dia — apenas identificar mês/ano do documento quando possível (ex. a partir da mesma data encontrada no texto, descartando o dia).
  • Adicionar uma etapa de confirmação obrigatória antes do botão "Enviar/Revisar": perguntar explicitamente a Competência: Mês/Ano de cada documento na tela de revisão (staging.html), com dois campos <select> — Mês (Jan, Fev, Mar, Abr, Mai, Jun, Jul, Ago, Set, Out, Nov, Dez) e Ano (2026, 2027, 2028, 2029, 2030) — pré-selecionados com o valor detectado quando disponível, permitindo correção manual antes de confirmar o lote.
  • Aplicar o mesmo par de selects (Mês/Ano) no formulário manual de documento (document_form.html), substituindo o <input type="date"> atual.
  • Atualizar confirm_batch/create_fiscal/update_fiscal para gravar mes/ano em vez de purchase_date, com mes e ano obrigatórios (mesma garantia de não-nulo que purchase_date tinha).
  • Atualizar todas as agregações e filtros que hoje dependem de purchase_date:
    • monthly_totals (Dashboard: "Gastos por mês") passa a agrupar diretamente por ano/mes em vez de substr(purchase_date, 1, 7).
    • category_totals, _fiscal_where, FISCAL_SORT_COLUMNS e a listagem /documents (documents_list.html) passam a filtrar/ordenar por ano/mes em vez de intervalo de datas (start/end do tipo date).
    • Exibição de "Competência" (ex. Jun/2026) substitui a coluna/célula "Data" no Dashboard e em /documents.
  • Atualizar scripts e testes que hoje gravam/leem purchase_date diretamente (scripts/migrate_notas.py, tests/test_app.py, tests/test_ingestion.py, tests/test_categorization.py) para o novo par de campos.

Capabilities

New Capabilities

  • document-competencia: Cobre a captura, revisão, persistência, exibição e filtragem da competência (mês/ano) de um documento fiscal, substituindo o conceito de data completa de compra em todo o fluxo (upload → revisão → confirmação → dashboard → listagem/filtros).

Modified Capabilities

(nenhuma — não há specs existentes em openspec/specs/; o item acima é tratado como nova capability, que substitui integralmente o comportamento de data hoje implícito no código)

Impact

  • Banco de dados (app/database.py): remoção de purchase_date (e do índice idx_fiscal_date) de fiscal_documents e detected_documents; novas colunas mes INTEGER NOT NULL e ano INTEGER NOT NULL (com índice equivalente idx_fiscal_competencia em (ano, mes)); rebuild de tabela (SQLite não remove coluna com ALTER TABLE em todas as versões-alvo) com backfill dos dados existentes a partir do purchase_date atual antes de descartá-lo.
  • Extração (app/ai_extraction.py, lernotafiscal/extraction.py, app/ingestion.py): prompt de IA e regex OCR passam a reportar mês/ano em vez de dia completo; RawExtraction/DetectedDocumentCandidate trocam purchase_date_raw/purchase_date por mes_raw/ano_raw.
  • Normalização (app/dates.py): resolve_purchase_date/parse_date/format_br_date são substituídos ou adaptados para resolver/validar/formatar competência (mes/ano → "Jun/2026"), reaproveitando a lógica de fallback "mês corrente" já existente.
  • Rotas e templates de upload/revisão (app/routes/upload_routes.py, app/templates/staging.html): novo par de <select> Mês/Ano por linha, com pergunta explícita de competência antes de habilitar "Enviar"/confirmar lote.
  • Rotas e templates de CRUD manual (app/routes/documents_routes.py, app/templates/document_form.html): mesmo par de <select> Mês/Ano substituindo <input type="date">.
  • Dashboard (app/routes/dashboard_routes.py, app/templates/dashboard.html): monthly_totals e exibição de "Últimos documentos" passam a usar ano/mes nativos; _mes_label deixa de derivar de string e passa a formatar diretamente a partir das colunas.
  • Listagem/filtros (app/routes/documents_routes.py, app/templates/documents_list.html): filtros start/end (datas) são substituídos por filtro de competência (mês/ano ou intervalo de competência); ordenação por purchase_date substituída por ordenação por (ano, mes).
  • Scripts e testes: scripts/migrate_notas.py, tests/test_app.py, tests/test_ingestion.py, tests/test_categorization.py precisam ser atualizados para o novo schema/campos.
  • Compatibilidade: mudança é destrutiva para dados existentes no formato antigo — exige migração/backfill de purchase_date para mes/ano antes da remoção da coluna; documentos sem data reconhecível hoje (fallback "1º dia do mês corrente") migram diretamente para o mês/ano correspondente.