agenda: C9 OK + rowGroup por paciente em /financeiro + bubble cobranca-atualizada

Cenário 9 (Per-session — Michael Balint 12 × R$ 150)
- Testado e passou. 1 rule + 12 agenda_eventos materializadas + 12
  financial_records pending. Sem billing_contract. Badge $ em todas as
  12 sessões. Conforme esperado.

/melissa/financeiro-lancamentos: agrupado por paciente
- DataTable com rowGroupMode='subheader' + groupRowsBy='patient_id'
- Header de grupo com avatar + nome + badge "N lançamento(s)"
- expandableRowGroups + v-model:expandedRowGroups; watcher popula
  todos os grupos da página atual como expandidos (sempre que
  recordsGrouped muda — refletindo paginação/filtros)
- Sort outer por nome do paciente, preserva inner order
  (pai → filhos de multas/taxas via mesmo agenda_evento_id)

Bubble-up @cobranca-atualizada → M.refetch
- Antes: ao marcar como pago no dialog, o card no FC ficava stale
  até trocar de view. AgendaEventoFinanceiroPanel emitia
  cobranca-atualizada mas só o loadOccFinancialRecord do dialog
  escutava; o _paymentStateMap da agenda nao re-rodava.
- Fix: AgendaEventDialog ganhou _onCobrancaAtualizada que dispara
  loadOccFinancialRecord() E emit('cobranca-atualizada') pra cima.
  MelissaLayout escuta nos 2 dialogs e chama M.refetch() +
  refetchEventosHoje(). Card passa pra borda verde na hora.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Leonardo
2026-05-19 23:55:06 -03:00
parent fad1f4ebd4
commit ec0a24f5c8
5 changed files with 177 additions and 35 deletions
+56 -31
View File
@@ -1,46 +1,70 @@
# HANDOFF — 2026-05-19 madrugada (C1-C8 + UI saldo + Usar/Revogar, próximo C9)
# HANDOFF — 2026-05-20 madrugada (C1-C9 ✅, próximo C10)
Documento de continuidade. **Quando voltar, comece lendo esta página até o fim.**
> **🎯 SE A FORÇA CAIR / SESSÃO PERDER CONTEXTO:** estamos na rodada de
> testes manuais dos 13 cenários do doc viva
> `src/docs/agenda-compromisso-financeiro-cenarios.html`. **C1-C8 ✅**.
> Próximo: **Cenário 9** (Michael Balint · per_session · 12 × R$ 150 —
> materializa todas 12 sessões + cria 12 records pendentes upfront).
> `src/docs/agenda-compromisso-financeiro-cenarios.html`. **C1-C9 ✅**.
> Próximo: **Cenário 10** (status change avulsa — Joyce/Ana/Sándor:
> marcar como realizado/faltou/cancelado e ver consequências no
> financeiro via STATUS_TO_EXCEPTION + financial_exceptions).
> **🟢 WORKING TREE LIMPO** após commit/push de 19/05 madrugada. UI de
> pacote saldo completa (info card violeta + botão Usar/Revogar atômico
> que materializa+realiza+cobra ou desfaz). Backfill de
> determined_commitment_id em revogar+usar (evita campo "Título" indevido
> em sessões legadas).
> **🟢 WORKING TREE LIMPO** após commit/push do checkpoint pós-C8/C9.
> Per-session funcionando (12 events + 12 records gerados em batch).
> Financeiro com rowGroup por paciente (expand/collapse). Bubble-up
> @cobranca-atualizada → M.refetch faz o card da agenda atualizar
> badge/borda imediatamente após pagar.
---
## 🔴 PRÓXIMO PASSO IMEDIATO — Cenário 9 (Per-session)
## 🔴 PRÓXIMO PASSO IMEDIATO — Cenário 10 (Status change AVULSA)
| Campo | Valor |
|---|---|
| Paciente | **Michael Balint** |
| Frequência | **Semanal · 12 ocorrências** |
| Serviço | Sessão R$ 150 |
| Cobrança ao salvar | **1 por sessão** (chargeMode=per_session) |
Doc HTML diz: testar status change numa sessão avulsa com cobrança pendente,
mudando entre realizado / faltou / cancelado. As consequências financeiras
seguem `financial_exceptions` (regras configuradas pelo terapeuta sobre o
que acontece com a cobrança nesses casos).
**Esperado:**
- 1 row em `recurrence_rules`
- **12 rows em `agenda_eventos`** (TODAS materializadas — diferença chave vs C6-C8)
- 0 rows em `billing_contracts` (per_session NÃO usa contrato)
- **12 rows em `financial_records`** (1 por sessão, todos pending, amount=150 cada)
- Agenda: 12 sessões com **badge $ amber** em todas (cada uma tem seu record próprio)
- Popover de cada uma: linha amber "A receber R$ 150,00 (cobrança pendente)"
Possíveis pacientes pra teste: usar Joyce, Sándor ou outro com cobrança
avulsa pendente já criada.
Diferença chave vs cenários anteriores:
- C6 (none): 1 row materializada + 0 records
- C7 (upfront): 1 row + 1 record (R$ totalPacote)
- C8 (saldo): 0 rows + 0 records (modelo Cliniko, geram on-the-fly via Usar)
- **C9 (per_session): N rows + N records pré-criados**
**Esperado** (depende das `financial_exceptions` configuradas no tenant):
- Realizada: status muda; cobrança permanece (caminho default)
- Faltou: pode ter regra → cobrança 100% (paciente paga falta) ou cancela
- Cancelado: pode ter regra → cancelar cobrança ou cobrar parcial
Após C9: C10-C13 (status change + edit cobrada).
Quando todos passarem, replicar em **Rail** (`AgendaTerapeutaPage.vue`) e **Clínica** (`AgendaClinicaPage.vue`).
Conferir:
- `STATUS_TO_EXCEPTION` mapping em `useAgendaFinanceiro.js`
- `getFinancialExceptionRule(tenantId, exceptionType)` retorna a regra
- `handleStatusChange` orquestra: agenda update + financial adjust
Após C10: C11 (status change pacote saldo — usar a infra do Usar/Revogar)
→ C12 (antecipar pagamento) → C13 (edit cobrada).
Quando todos passarem, replicar em **Rail** (`AgendaTerapeutaPage.vue`) e
**Clínica** (`AgendaClinicaPage.vue`).
---
## 📦 O que foi feito em 20/05 madrugada (C9 + rowGroup financeiro + bubble cobranca-atualizada)
### Cenário 9 ✅ (Per-session — Michael Balint 12 × R$ 150)
Testado e passou. Criou-se 1 rule + 12 agenda_eventos materializadas + 12 financial_records pending. Sem billing_contract. Cada sessão com badge $ amber individual. **Sem nenhuma `linha de pacote`** no popover (não tem contract → não aparece). Conforme esperado.
### `/melissa/financeiro-lancamentos` agrupado por paciente
- DataTable com `rowGroupMode='subheader'` + `groupRowsBy='patient_id'`
- Default: todos os grupos da página expandidos (watcher popula `expandedGroups` com unique patient_ids quando `recordsGrouped` muda)
- Header de grupo: avatar pequeno + nome + badge "N lançamento(s)"
- Click no chevron contrai/expande (auto via PrimeVue `expandableRowGroups`)
- Sort estável: ordena outer por nome do paciente, preserva inner order (pai → filhos de multas/taxas)
### Bubble-up `@cobranca-atualizada`
Antes: `AgendaEventoFinanceiroPanel.@cobranca-atualizada` disparava só `loadOccFinancialRecord` (interno do dialog). O `_paymentStateMap` da agenda ficava stale → card no FC só atualizava ao trocar de view.
Agora: `AgendaEventDialog._onCobrancaAtualizada` faz duas coisas:
1. `loadOccFinancialRecord()` — refresca estado interno do dialog
2. `emit('cobranca-atualizada')` — bubble pra MelissaLayout
MelissaLayout escuta nos 2 dialogs (principal + occurrenceMode) e chama `onCobrancaAtualizada` que dispara `M.refetch() + refetchEventosHoje()`. Resultado: card na agenda passa pra borda verde imediatamente após marcar pago.
---
@@ -282,7 +306,8 @@ User tentou rodar C5 e bateu em 3 problemas seguidos. Cada um virou um fix:
| 6 | Recorrente sem pacote (Maria Magali / Anna Freud) | ✅ |
| 7 | Pacote upfront (Ana Souza Ferreira 4 × R$ 200) | ✅ |
| 8 | Pacote saldo (Otávio 12 × R$ 50) | ✅ |
| **9** | **1 por sessão (Michael Balint 12 × R$ 150)** | 🔴 **PRÓXIMO** |
| 9 | 1 por sessão (Michael Balint 12 × R$ 150) | ✅ |
| **10** | **Status change avulsa (realizado/faltou/cancelado)** | 🔴 **PRÓXIMO** |
| 10 | Status change avulsa (realizado/faltou/cancelado) | ⏳ |
| 11 | Status change pacote saldo | ⏳ |
| 12 | Antecipar pagamento (Carl Jung) | ⏳ |