## 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 `` por dois `