agenda: C7 OK + Fase 6 lock-edit ativada em Melissa + cross-week payment propagation
Cenário 7 (Pacote UPFRONT — Ana Souza Ferreira 4×R$ 200 = R$ 800)
- Testado e passou. User criou Ana, pagou os R$ 800 em dinheiro pelo
Financeiro. Borda verde + popover "Pago R$ 800" funcionando.
Fase 6 (lock-edit cobrada) ativada em Melissa
- Removido guard `if (!props.occurrenceMode) return;` em
loadOccFinancialRecord (useAgendaEventLifecycle.js:217+). Agora ele
carrega em ambos modos (Rail/Clínica E Melissa)
- loadOccFinancialRecord SINTETIZA record paid/pending pra siblings de
contrato upfront ativo — assim TODAS as ocorrências da série mostram
"Cobrança paga R$ 800 do pacote" no AgendaEventDialog
- AgendaEventDialog card Sessão/Honorários (flow Melissa) ganhou lock
template: Tag em vez de Select billingType quando occFinancialRecord
existe; Message com cadeado "Cobrança de R$ X já emitida"
- AgendaEventoFinanceiroPanel só renderiza dentro do lock quando record
é REAL (não sintetizado) — evita "Gerar cobrança" indevido em sibling
- paymentSummary do Resumo lateral unificado pra usar occFinancialRecord
(em vez do sessionPaymentRecord paralelo de antes)
Cross-week propagation de pacote upfront
- BUG: ao navegar pra semana só com virtuais (sem reais), bulk-load
caía no else `_rulePaymentMap.value = {}` — virtuais perdiam estado
paid herdado
- FIX em useMelissaAgenda._reloadRange:
* Maps (payment/amount/rule) inicializados SEMPRE no início
* Propagação roda independente de realIds.length (depende só de
ruleIdsInView.size>0, considera reais E virtuais com recurrence_id)
* Query cross-week: pra cada rule em view, busca QUALQUER evento
sibling em qualquer semana + seus records pra determinar estado do
contrato. Encontra o record do pacote mesmo em outra semana
- Saldo NÃO propaga (filter: charging_style='upfront' || NULL); cada
sessão de saldo gera cobrança individual ao realizar
- Memória durável: memory/project_cross_week_propagation.md
Visualização de virtuais cobertas
- MelissaEventoPanel.showPaymentRow: virtuais só escondem quando state
='none'. Com paid/pending herdado, exibem linha colorida
- MelissaAgenda fcEvents: isPaidSession e badge $ pendente removeram
exigência de !is_occurrence. Virtuais herdadas via propagação mostram
borda verde / badge amber
Atalho "Gerar fatura" no popover
- Pill amber pequeno ao lado de "A cobrar R$ X" quando paymentVariant
='none' && !is_occurrence. Click → gerarCobrancaManual direto, fecha
popover pra impedir double-click. Tooltip: "Gerar fatura agora"
- Wire em MelissaLayout via novo emit gerar-cobranca + handler
onGerarCobrancaQuick
Info de pacote no popover
- Header agora mostra "Sessão · Pacote · N sessões" (computed
seriesLabel lê de _raw do rule)
Botão "Excluir série inteira"
- Novo emit delete-series em MelissaEventoPanel + botão ao lado de
"Excluir sessão" quando evento tem recurrence_id
- Handler onDeleteSeries em MelissaLayout: hard delete em 3 etapas
(financial_records pendentes → agenda_eventos materializados →
recurrence_rules CASCADE leva exceptions). Bloqueia se algum record
paid (estorno via Financeiro primeiro)
cancel_session some da agenda
- useRecurrence.expandRules agora pula occurrence com exception.type=
'cancel_session' (era visível com status cancelado; doc dizia que
some). patient_missed/therapist_canceled/holiday_block permanecem
como histórico
recurrence_exceptions cancel idempotente
- MelissaLayout onDeleteEvento usa upsert com onConflict pra exception
cancel — não quebra mais com unique violation em re-cancel
billing_contract_id na 1ª materializada
- _createPackageContract agora .select() o contrato após insert e seta
billing_contract_id no insert da 1ª agenda_eventos materializada
onVerLancamentos cobre virtual de upfront
- Antes virtual sempre toast "Sem lançamentos". Agora busca records via
siblings da série pra encontrar o do pacote. Saldo/sem pacote continua
com toast
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+92
-21
@@ -1,39 +1,112 @@
|
||||
# HANDOFF — 2026-05-19 (C1-C6 ✅, próximo C7 — pacote upfront)
|
||||
# HANDOFF — 2026-05-19 noite (C1-C7 ✅, próximo C8 — pacote saldo)
|
||||
|
||||
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-C6 ✅**.
|
||||
> Próximo passo: **Cenário 7** (Donald Winnicott · pacote UPFRONT · 4 ×
|
||||
> R$ 200 = cobrança única de R$ 800).
|
||||
> `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).
|
||||
|
||||
> **🟢 WORKING TREE LIMPO** após commit/push de 19/05 da manhã. Migration
|
||||
> nova (20260519000001) já rodada no DB local. Pronto pra próximo bloco.
|
||||
> **🟢 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.
|
||||
|
||||
---
|
||||
|
||||
## 🔴 PRÓXIMO PASSO IMEDIATO — Cenário 7 (Pacote UPFRONT)
|
||||
## 🔴 PRÓXIMO PASSO IMEDIATO — Cenário 8 (Pacote SALDO)
|
||||
|
||||
| Campo | Valor |
|
||||
|---|---|
|
||||
| Paciente | **Donald Winnicott** |
|
||||
| Paciente | **Carl Jung** |
|
||||
| Frequência | **Semanal · 4 ocorrências** |
|
||||
| Serviço | qualquer (ex: Sessão particular R$ 200) |
|
||||
| Cobrança ao salvar | **Gerar cobrança** + estilo **"Pacote único (upfront)"** |
|
||||
| Total esperado | **R$ 800** (4 × 200) |
|
||||
| Serviço | Sessão (R$ 40 cada) |
|
||||
| Cobrança ao salvar | **Pacote único** + estilo **"Saldo (Cliniko)"** |
|
||||
| Total contrato | **R$ 160** (4 × 40) |
|
||||
|
||||
**Esperado:**
|
||||
- 1 row em `recurrence_rules` + **1 row em `agenda_eventos`** (1ª materializada)
|
||||
- 1 row em `billing_contracts` (type=package, charging_style=upfront, total_sessions=4, package_price=800)
|
||||
- **1 row em `financial_records`** com amount=800, status=pending (cobrança ÚNICA do pacote, vencimento na 1ª sessão)
|
||||
- Agenda: 1ª com badge $ (R$ 800 a cobrar) + 3 virtuais limpas (cobertas pelo pacote)
|
||||
- 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
|
||||
|
||||
Após C7: C8 (saldo) → C9 (per_session) → C10-C13 (status change + edit cobrada).
|
||||
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)
|
||||
|
||||
Após C8: C9 (per_session) → 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 noite (C7 + lock-edit + propagação cross-week)
|
||||
|
||||
### Cenário 7 ✅ (Pacote UPFRONT — Ana Souza Ferreira)
|
||||
Testado e validado. Usuária criou Ana, R$ 200/sessão × 4 = R$ 800, marcou como pago em dinheiro pelo Financeiro. Visualização correta em mês AND em semana navegando pelas 4 semanas.
|
||||
|
||||
### Fase 6 (lock-edit cobrada) ativada em Melissa
|
||||
Antes: `loadOccFinancialRecord` tinha guard `if (!props.occurrenceMode) return;` — só carregava em Rail/Clínica (edição de ocorrência). Em Melissa, `sessionPaymentRecord` paralelo alimentava só o Resumo lateral, sem trigger de lock.
|
||||
|
||||
Agora unificado: `occFinancialRecord` carrega em ambos modos:
|
||||
- Card Sessão / Honorários ganha **Tag** (em vez de Select billingType) quando há cobrança
|
||||
- Body do card mostra **Message "Cobrança de R$ X já emitida"** + cadeado
|
||||
- Tipo de cobrança (Particular/Convênio/Gratuito) bloqueado
|
||||
- Edição de serviços/preço bloqueada
|
||||
|
||||
### Propagação cross-week de pacote upfront pago/pendente
|
||||
**Bug descoberto durante C7:** ao navegar pra semanas futuras (onde só virtual da Ana 2/3/4 aparecia, sem real event paid na view), o `_rulePaymentMap` era zerado pelo else branch do bulk-load → virtuais perdiam estado paid.
|
||||
|
||||
Fix em `useMelissaAgenda.js _reloadRange`:
|
||||
- Maps (paymentStateMap, amountMap, rulePaymentMap) inicializados SEMPRE no início
|
||||
- Propagação agora roda **independente de realIds.length** (ie, mesmo em semanas só-com-virtuais)
|
||||
- Coleta `ruleIdsInView` de TODOS eventos da view (reais + virtuais com recurrence_id)
|
||||
- Cross-week query: pra cada rule em view, busca QUALQUER evento sibling (inclusive em outras semanas) + seus records paid/pending → determina estado do contrato
|
||||
- Propaga estado pra eventos reais (via map) + virtuais (via rulePaymentMap acessado pelo normalize)
|
||||
|
||||
### Atalho "Gerar fatura" no popover
|
||||
- Pill amber pequeno ao lado de "A cobrar R$ X" no popover (`paymentVariant === 'none' && !is_occurrence`)
|
||||
- Click → `gerarCobrancaManual` direto, fecha popover pra impedir double-click
|
||||
- Tooltip: "Gerar fatura agora"
|
||||
|
||||
### Info de pacote no popover
|
||||
- Header agora mostra `Sessão · Pacote · N sessões` (computed `seriesLabel` lê de `_raw` do rule)
|
||||
|
||||
### Botão "Excluir série inteira"
|
||||
- Novo emit `delete-series` em `MelissaEventoPanel` + botão ao lado de "Excluir sessão" quando evento tem `recurrence_id`
|
||||
- Handler `onDeleteSeries` em MelissaLayout faz hard delete: `financial_records` pendentes → `agenda_eventos` materializados → `recurrence_rules` (CASCADE leva exceptions + rule_services)
|
||||
- Bloqueia se algum record tem `status='paid'` (estornar primeiro)
|
||||
|
||||
### RPC `create_financial_record_for_session` ignora cancelled
|
||||
**Migration 20260519000001:** idempotência da RPC passou a filtrar `AND status != 'cancelled'` além de `deleted_at IS NULL`. Antes: cancelar cobrança sem querer → todo "Gerar fatura" subsequente retornava o cancelado em vez de criar nova. Toast verde mentindo.
|
||||
|
||||
Memória durável em `memory/project_rpc_idempotency_cancelled.md`.
|
||||
|
||||
### `cancel_session` exception some da agenda
|
||||
- `useRecurrence.expandRules` agora pula ocorrência com `exception.type === 'cancel_session'` (era visível com status cancelado; doc dizia "some da agenda" mas código mantinha)
|
||||
- `patient_missed` / `therapist_canceled` / `holiday_block` permanecem visíveis como histórico
|
||||
|
||||
### `recurrence_exceptions` cancel idempotente
|
||||
- Cancel de ocorrência (virtual e materializada) usa `upsert` com `onConflict: 'recurrence_id,original_date'` — não quebra mais com unique violation quando há exception zumbi de tentativa anterior.
|
||||
|
||||
### Visualização paid/pending de upfront em virtuais
|
||||
- `MelissaEventoPanel.showPaymentRow` antes excluía virtuais incondicionalmente. Agora só esconde quando `paymentState === 'none'` (saldo/sem pacote continua limpo; upfront propagado mostra).
|
||||
- `MelissaAgenda.fcEvents`: removida exigência de `!is_occurrence` no `isPaidSession` e no badge $ pendente. Virtuais herdadas via propagação mostram borda verde/badge amber.
|
||||
|
||||
### `onVerLancamentos` cobre virtual de upfront
|
||||
- Antes: virtual sempre toast "Sem lançamentos". Agora: busca records via siblings da série pra encontrar o do pacote. Saldo/sem pacote continua com toast.
|
||||
|
||||
### Confirmação 3 decisões UX (não codar)
|
||||
Antes de C7, user perguntou e concordou:
|
||||
1. Editar serviço já lançado e pago → **NÃO** (cobrança fiscal imutável)
|
||||
2. Alternar Particular/Convênio/Gratuito em série com cobrança ativa → **NÃO** (mesma razão)
|
||||
3. "Gerar fatura" extra em sessão coberta por contrato upfront → **NÃO** (duplicaria cobrança)
|
||||
Tudo isso o lock-edit (Fase 6 ativada acima) cobre.
|
||||
|
||||
---
|
||||
|
||||
## 📦 O que foi feito em 18/05
|
||||
|
||||
### Cenário 4 (Joyce · "Já recebi") ✅
|
||||
@@ -150,15 +223,13 @@ User tentou rodar C5 e bateu em 3 problemas seguidos. Cada um virou um fix:
|
||||
| 4 | Avulsa "já recebi" no salvar | ✅ |
|
||||
| 5 | Avulsa convênio (Sándor + Unimed) | ✅ |
|
||||
| 6 | Recorrente sem pacote (Maria Magali / Anna Freud) | ✅ |
|
||||
| **7** | **Pacote UPFRONT (Donald Winnicott 4 × R$ 200)** | 🔴 **PRÓXIMO** |
|
||||
| 6 | Recorrente sem pacote (Anna Freud 4 sem) | ⏳ |
|
||||
| 7 | Pacote upfront (Donald Winnicott 4× R$ 200) | ⏳ |
|
||||
| 8 | Pacote saldo (Carl Jung 4× R$ 40) | ⏳ |
|
||||
| 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) | ⏳ |
|
||||
| 10 | Status change avulsa (realizado/faltou/cancelado) | ⏳ |
|
||||
| 11 | Status change pacote saldo | ⏳ |
|
||||
| 12 | Antecipar pagamento (Carl Jung) | ⏳ |
|
||||
| 13 | Edit cobrada | ⏳ |
|
||||
| 13 | Edit cobrada | ⏳ (parcialmente — lock ativo em Melissa pós-19/05 noite) |
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user