agenda: C8 OK + Usar/Revogar pacote saldo + UI de contract + ajustes UX
Cenário 8 (Pacote SALDO — Otávio Souza Ferreira 12 × R$ 50)
- Testado e passou. DB: 1 rule, 0 events, 1 contract (saldo), 0 records.
Visual: 12 virtuais limpas no calendário.
UI de pacote (saldo + upfront)
- _ruleContractMap em useMelissaAgenda: bulk-load popula contract info
(id, style, totalSessions, sessionsUsed, packagePrice) por
recurrence_id. Query recurrence_rules.patient_id como fonte
autoritativa — cobre saldo sem materializadas (sem isso, ruleToPatient
via records vinha vazio pra saldo)
- normalize injeta `contract` no evento via ruleContractMap
- MelissaEventoPanel: nova linha colorida (violeta saldo, verde upfront)
com "Pacote saldo · N/M usadas" ou "Pacote · N/M realizadas"
- AgendaEventDialog: info card mt-4 com header+body+hint explicando
modelo, gateado por occFinancialLoading (spinner durante carga
pra evitar piscar entre Usar/Revogar)
Handlers Usar/Revogar atômicos
- onUsarSessao em MelissaLayout: materializa virtual (preserva
determined_commitment_id da regra) → status=realizado +
billing_contract_id → create_financial_record_for_session →
sessions_used++ → (se atingiu total) contract.status=completed
- onRevogarSessao: cancela record + sessions_used-- + reativa contract
se estava completed + status=agendado. Bloqueia se record paid
(precisa estorno formal pelo Financeiro)
- Ambos aceitam payload {eventRow, contract} do dialog OU fallback
pra eventoSelecionado do popover
- Botão "Usar" verde no popover (paymentState=none) substituído por
"Revogar" vermelho (paymentState=pending). Equivalente "Usar agora"/
"Revogar uso" no info card do dialog
Fix enum status_evento_agenda
- 'realizada' não existe no enum — DB exige 'realizado' (masculino).
Corrigido em todas as ocorrências do handler
Fix campo "Título" indevido em sessão
- Sessão sem determined_commitment_id → selectedCommitment=null →
isSessionEvent=false → mostra campo Título (que é só pra não-sessão)
- Fix: materialize do Usar inclui determined_commitment_id (insert
path); update path backfilla via query da rule se NULL; Revogar
também backfilla pra consistência
Fix "Gerar fatura" não cabe em saldo
- Botão "Gerar fatura" do popover hide quando há contractInfo. Em
saldo, gerar fatura solta criaria cobrança duplicada sem incrementar
sessions_used. Fluxo correto: "Usar"
Recorrências Aplicadas — UI
- Header stats coloridos: total **azul**, realizadas **verde**,
faltaram **amber**, canceladas **cinza**, remarcadas **violeta**
- Pills com badge sólido por status (emerald-600 realizado, amber-600
faltou, stone-500 cancelado, violet-600 remarcado)
Race condition no dialog
- AgendaEventDialog mostrava botões Usar/Revogar baseado em
occFinancialRecord async; durante ~500ms de load, botão errado
podia piscar. Fix: spinner "Verificando estado…" enquanto
occFinancialLoading=true; botões só renderizam após
- Popover não fixado (race window pequena, fechar/reabrir resolve)
3 decisões UX confirmadas antes de codar
- Editar serviço pago → NÃO (cobrança fiscal imutável)
- Alternar Particular/Convênio/Gratuito em série cobrada → NÃO
- Gerar fatura individual em pacote upfront → NÃO (duplicação)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+82
-25
@@ -1,47 +1,104 @@
|
||||
# HANDOFF — 2026-05-19 noite (C1-C7 ✅, próximo C8 — pacote saldo)
|
||||
# HANDOFF — 2026-05-19 madrugada (C1-C8 ✅ + UI saldo + Usar/Revogar, próximo C9)
|
||||
|
||||
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-C7 ✅**.
|
||||
> Próximo: **Cenário 8** (Carl Jung · pacote SALDO · 4 × R$ 40 — modelo
|
||||
> Cliniko: contrato sem cobrança imediata, cada sessão gera record ao
|
||||
> ser realizada).
|
||||
> `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).
|
||||
|
||||
> **🟢 WORKING TREE LIMPO** após commit/push de 19/05 noite. Fase 6
|
||||
> (lock-edit cobrada) ativada em Melissa também. Lock + popover atalho
|
||||
> "Gerar fatura" + propagação cross-week de pacote upfront tudo
|
||||
> funcionando.
|
||||
> **🟢 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).
|
||||
|
||||
---
|
||||
|
||||
## 🔴 PRÓXIMO PASSO IMEDIATO — Cenário 8 (Pacote SALDO)
|
||||
## 🔴 PRÓXIMO PASSO IMEDIATO — Cenário 9 (Per-session)
|
||||
|
||||
| Campo | Valor |
|
||||
|---|---|
|
||||
| Paciente | **Carl Jung** |
|
||||
| Frequência | **Semanal · 4 ocorrências** |
|
||||
| Serviço | Sessão (R$ 40 cada) |
|
||||
| Cobrança ao salvar | **Pacote único** + estilo **"Saldo (Cliniko)"** |
|
||||
| Total contrato | **R$ 160** (4 × 40) |
|
||||
| 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) |
|
||||
|
||||
**Esperado:**
|
||||
- 1 row em `recurrence_rules`
|
||||
- **0 rows em `agenda_eventos`** materializadas inicialmente (saldo NÃO materializa 1ª)
|
||||
- 1 row em `billing_contracts` (type=package, charging_style=**saldo**, total_sessions=4, package_price=160)
|
||||
- **0 rows em `financial_records`** (sem cobrança imediata — modelo Cliniko)
|
||||
- Agenda: 4 ocorrências virtuais **TODAS LIMPAS** (sem badge $, sem barra verde) — saldo intencionalmente não propaga estado, cada sessão gera cobrança individual quando vira realidade
|
||||
- **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)"
|
||||
|
||||
Diferença chave vs upfront:
|
||||
- Upfront: 1 cobrança única (paga ou pendente) cobre todas as 4 sessões
|
||||
- Saldo: contrato sem cobrança; cada sessão materializada GERA sua própria cobrança ao virar realidade (status realizado/faltou via flow C10-C12)
|
||||
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**
|
||||
|
||||
Após C8: C9 (per_session) → C10-C13 (status change + edit cobrada).
|
||||
Após C9: C10-C13 (status change + edit cobrada).
|
||||
Quando todos passarem, replicar em **Rail** (`AgendaTerapeutaPage.vue`) e **Clínica** (`AgendaClinicaPage.vue`).
|
||||
|
||||
---
|
||||
|
||||
## 📦 O que foi feito em 19/05 madrugada (C8 + Usar/Revogar saldo + UI de pacote)
|
||||
|
||||
### Cenário 8 ✅ (Pacote SALDO — Otávio Souza Ferreira 12 × R$ 50)
|
||||
Testado e validado. Contract criado com `charging_style='saldo'`, 0 events materializadas, 0 records. Modelo Cliniko: sessões materializam on-demand via Usar.
|
||||
|
||||
### UI do pacote (saldo + upfront)
|
||||
- **`_ruleContractMap`** em useMelissaAgenda: bulk-load agora popula contract info (id, style, totalSessions, sessionsUsed, packagePrice) por `recurrence_id`. Query usa `recurrence_rules.patient_id` como fonte autoritativa (cobre saldo sem materializadas).
|
||||
- **Normalize** injeta `contract` no evento → popover acessa via `ev.contract`.
|
||||
- **Popover** (`MelissaEventoPanel`): nova linha colorida abaixo do payment:
|
||||
- Saldo: violeta `"Pacote saldo · N/M usadas"` + botão verde **"Usar"** (paymentState=none) OU vermelho **"Revogar"** (paymentState=pending)
|
||||
- Upfront: verde `"Pacote · N/M realizadas"` (sem botão; cobrança já tratada)
|
||||
- **AgendaEventDialog**: info card mt-4 (saldo violeta / upfront emerald) com header (pacote+contador), body (total/per-session/restam), botão "Usar agora" ou "Revogar uso", hint explicando o modelo. Gateado por `occFinancialLoading` (spinner durante carga) pra evitar piscar entre estados.
|
||||
|
||||
### Handlers Usar/Revogar atômicos
|
||||
**`onUsarSessao`** em MelissaLayout (aceita payload do popover OU do dialog):
|
||||
1. Materializa virtual se necessário (preserva `determined_commitment_id` da regra)
|
||||
2. Status='realizado' + link `billing_contract_id`
|
||||
3. `create_financial_record_for_session` RPC com per-session amount
|
||||
4. Incrementa `billing_contracts.sessions_used`
|
||||
5. Se atingiu total → contract `status='completed'`
|
||||
6. Toast verde + fecha popover/dialog
|
||||
|
||||
**`onRevogarSessao`** desfaz tudo:
|
||||
1. Cancela financial_record (status='cancelled')
|
||||
2. Decrementa sessions_used (não fica negativo)
|
||||
3. Reativa contract se estava completed
|
||||
4. Status volta pra 'agendado'
|
||||
5. Bloqueia se record já está `paid` (precisa estorno formal pelo Financeiro)
|
||||
6. **Backfill** de `determined_commitment_id` se NULL (fix de legado)
|
||||
|
||||
### Fix: enum status_evento_agenda
|
||||
Era `'realizada'` no insert/update, DB exige `'realizado'` (masculino). Corrigido em todas as ocorrências.
|
||||
|
||||
### Fix: campo "Título" indevido no dialog
|
||||
Sessão sem `determined_commitment_id` → `selectedCommitment=null` → `isSessionEvent=false` → mostra campo Título (que é só pra não-sessão). Fix:
|
||||
- Materialize do Usar inclui `determined_commitment_id` da regra
|
||||
- Update path do Usar (sessão real após revogar) backfilla via query da rule
|
||||
- Revogar também backfilla — garante consistência mesmo sem novo Usar
|
||||
- SQL massivo de backfill disponível no HANDOFF pra limpar rows legadas
|
||||
|
||||
### Fix: "Gerar fatura" não cabe em sessão de saldo
|
||||
Hide do botão "Gerar fatura" no popover quando há `contractInfo`. Geraria cobrança solta sem incrementar saldo → duplicação. Fluxo correto: usar "Usar".
|
||||
|
||||
### Recorrências Aplicadas: cores + badges
|
||||
- Header stats: total **azul**, realizadas **verde**, faltaram **amber**, canceladas **cinza**, remarcadas **violeta**
|
||||
- Pills: badge sólido por status (Realizado=emerald-600, Faltou=amber-600, Cancelado=stone-500, Remarcado=violet-600)
|
||||
|
||||
### Race condition no dialog
|
||||
- AgendaEventDialog mostrava botões "Usar"/"Revogar" baseado em `occFinancialRecord` que carrega async
|
||||
- Durante load (~500ms), botão errado podia aparecer → snap pro correto depois
|
||||
- Fix: spinner "Verificando estado…" enquanto `occFinancialLoading=true`; botões só renderizam após
|
||||
- Popover decidiu manter como está (race window pequena, fechar/reabrir resolve)
|
||||
|
||||
---
|
||||
|
||||
## 📦 O que foi feito em 19/05 noite (C7 + lock-edit + propagação cross-week)
|
||||
|
||||
### Cenário 7 ✅ (Pacote UPFRONT — Ana Souza Ferreira)
|
||||
@@ -224,8 +281,8 @@ User tentou rodar C5 e bateu em 3 problemas seguidos. Cada um virou um fix:
|
||||
| 5 | Avulsa convênio (Sándor + Unimed) | ✅ |
|
||||
| 6 | Recorrente sem pacote (Maria Magali / Anna Freud) | ✅ |
|
||||
| 7 | Pacote upfront (Ana Souza Ferreira 4 × R$ 200) | ✅ |
|
||||
| **8** | **Pacote saldo (Carl Jung 4 × R$ 40)** | 🔴 **PRÓXIMO** |
|
||||
| 9 | 1 por sessão (Michael Balint 12 sem) | ⏳ |
|
||||
| 8 | Pacote saldo (Otávio 12 × R$ 50) | ✅ |
|
||||
| **9** | **1 por sessão (Michael Balint 12 × R$ 150)** | 🔴 **PRÓXIMO** |
|
||||
| 10 | Status change avulsa (realizado/faltou/cancelado) | ⏳ |
|
||||
| 11 | Status change pacote saldo | ⏳ |
|
||||
| 12 | Antecipar pagamento (Carl Jung) | ⏳ |
|
||||
|
||||
Reference in New Issue
Block a user