# Notificação Imediata para Vitrine Fiscal

Objetivo:
- restaurar o comportamento de "guia nova apareceu no ICMS -> vitrine atualiza no app" sem depender de espera longa.
- manter compatibilidade com o polling atual de `GET /api/internal/fiscal-documents?pending_only=1`.

## Endpoint de evento incremental

- `GET /api/internal/fiscal-documents/upload-events`
- autenticação: `Authorization: Bearer <token-da-controladora>`
- retorno com `cursor` + `events[]`.
- o feed pode incluir eventos de `guia`, `extrato` e snapshots SEFIN (`sefin_year_snapshot`, `sefin_pending_month_snapshot`).

### Query params

- `after_id` (opcional): retorna apenas artefatos com ID maior que o cursor informado.
- `bootstrap=1` (opcional): retorna só o cursor atual para iniciar stream incremental.
- `history=1` (opcional): lista histórico recente em ordem decrescente.
- `ready_only=1` (opcional): mantém somente eventos já publicáveis na vitrine (`readyForVitrine=true`).
- `limit` (opcional): limite por chamada.

## Regra operacional recomendada no app.vcnodigital.com

1. Na inicialização do worker fiscal, chamar `upload-events?bootstrap=1` e salvar o `cursor`.
2. A cada 15-30 segundos, chamar `upload-events?after_id=<cursor>`.
3. Se vier evento novo com `readyForVitrine=true`, executar imediatamente:
   - `GET /api/internal/fiscal-documents?pending_only=1`
4. Importar os documentos retornados e marcar cada artefato com:
   - `POST /api/internal/fiscal-documents/mark-exported`
5. Atualizar o cursor local com o `cursor` devolvido na resposta do `upload-events`.

## Delta de pagamento inferido

- `GET /api/internal/fiscal-documents/payment-events` devolve um cursor curto para mudanças de pagamento.
- usar `after_id` para buscar apenas eventos novos de `SEFIN_PAYMENT%`.
- o `sefin_browser.py` pode gerar esses eventos nas janelas `D+1` útil para PIX e `D+5` útil para código de barras/boleto.
- quando chegar evento de pagamento, o app deve atualizar a lista de documentos pendentes sem esperar a próxima varredura completa.
- evento esperado: `SEFIN_PAYMENT_INFERRED`.
- chave de baixa no app:
  - `guideId`, quando o documento local tiver esse vínculo.
  - senão `companyId + numeroDocumento + parcela`.
  - fallback: `cnpj + numeroDocumento + parcela`, porque `numeroDocumento` sozinho pode existir em empresas e parcelas diferentes.
- ação no app: arquivar os documentos locais ainda pendentes com motivo `paid` e registrar o payload do evento no histórico/auditoria.
- depois da baixa, a imagem SEFIN da empresa deve ser atualizada no próximo sync por `GET /api/internal/fiscal-documents/snapshots` ou pelo snapshot que vier no feed de `upload-events`.

Exemplo:

```json
{
  "eventId": 1234,
  "eventType": "SEFIN_PAYMENT_INFERRED",
  "guideId": 987,
  "companyId": 38,
  "numeroDocumento": "20261600469178",
  "parcela": "00",
  "statusCurrent": "PAGA_INFERIDA",
  "isPaidInferred": true,
  "cnpj": "34754978000160",
  "paidInferredAt": "2026-04-07T20:10:00-04:00",
  "notes": "Baixa confirmada na Conta Corrente."
}
```

## Leitura dos campos de evento

- `eventType`: `guia` ou `extrato`.
- `readyForVitrine`: `true` quando já existe `guia_pdf` da guia.
- `documentsHint`: sugestão de documentos que já podem ir para vitrine.
  - contém `dare` quando a guia está pronta para consumo.
  - contém `extrato_dare` quando o extrato separado já existe ou quando o pareamento indica extrato não exigível.
- `pairing.status`: status operacional do casal (ex.: `PAR_COMPLETO`, `DARE_AUTOSSUFICIENTE`, `AGUARDANDO_EXTRATO`).
- `recommendedAction`:
  - `SYNC_VITRINE_NOW` quando o app já deve importar.
  - `WAIT_GUIDE_PDF` quando ainda não há guia final disponível.

## Garantias importantes

- O endpoint de eventos não substitui o endpoint principal de documentos.
- A vitrine sempre deve consumir os arquivos finais a partir de `GET /api/internal/fiscal-documents`.
- Se a mesma guia reaparecer com `artifactId` novo, o app deve substituir o artefato antigo.
- A chave operacional correta para essa substituição é `guideId` ou `companyId + numeroDocumento + parcela + documentType`.
- `numeroDocumento` sozinho não é chave única suficiente.
