From ec0a24f5c8c724479aeabad14408016fb67a3c28 Mon Sep 17 00:00:00 2001 From: Leonardo Date: Tue, 19 May 2026 23:55:06 -0300 Subject: [PATCH] agenda: C9 OK + rowGroup por paciente em /financeiro + bubble cobranca-atualizada MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- HANDOFF.md | 87 ++++++++++++------- Obsidian/Brain/log.md | 32 +++++++ .../agenda/components/AgendaEventDialog.vue | 17 +++- .../melissa/MelissaFinanceiroLancamentos.vue | 65 ++++++++++++++ src/layout/melissa/MelissaLayout.vue | 11 +++ 5 files changed, 177 insertions(+), 35 deletions(-) diff --git a/HANDOFF.md b/HANDOFF.md index 3c59951..7131306 100644 --- a/HANDOFF.md +++ b/HANDOFF.md @@ -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) | ⏳ | diff --git a/Obsidian/Brain/log.md b/Obsidian/Brain/log.md index 0a2eb4d..e50fc11 100644 --- a/Obsidian/Brain/log.md +++ b/Obsidian/Brain/log.md @@ -14,6 +14,38 @@ Chronological, append-only record of everything that's happened in this wiki. --- +## [2026-05-20 06:00] session | C9 OK + rowGroup por paciente + bubble cobranca-atualizada +Touched: none (codigo + HANDOFF) +Detalhes: + +CENARIO 9 (per_session): +- Michael Balint 12 sessoes R$ 150 modo "1 por sessao" +- DB conforme esperado: 1 rule, 12 events materializadas, 0 contracts, + 12 records pending (R$ 150 cada) +- Visual: 12 badges $ amber, todas pending. Sem linha de pacote no + popover (per_session nao usa contract). + +ROWGROUP NO /melissa/financeiro-lancamentos: +- DataTable com rowGroupMode='subheader' + groupRowsBy='patient_id' +- Default todos expandidos (watcher recalcula a cada recordsGrouped) +- Header de grupo: avatar + nome + badge "N lançamento(s)" +- Sort outer por nome de paciente, preserva ordem interna pai→filhos +- Click no chevron contrai/expande + +BUBBLE @cobranca-atualizada: +- Bug: card na agenda nao atualizava badge/borda apos marcar como + pago no dialog (so dava refresh ao trocar de view) +- Root cause: AgendaEventoFinanceiroPanel.@cobranca-atualizada + disparava so loadOccFinancialRecord (interno do dialog). O + _paymentStateMap da agenda ficava stale. +- Fix: AgendaEventDialog tem agora _onCobrancaAtualizada que faz + loadOccFinancialRecord + emit('cobranca-atualizada'). MelissaLayout + escuta nos 2 dialogs e chama M.refetch() + refetchEventosHoje(). + Card no FC passa pra borda verde imediatamente. + +PROXIMO: Cenario 10 (status change avulsa — realizado/faltou/cancelado ++ STATUS_TO_EXCEPTION em financial_exceptions). + ## [2026-05-20 03:00] session | C8 OK + Usar/Revogar saldo + UI pacote + ajustes UX Touched: none (sem nova wiki page; mudancas em codigo + HANDOFF) Detalhes: noite longa cobrindo C8 (pacote saldo) e principalmente diff --git a/src/features/agenda/components/AgendaEventDialog.vue b/src/features/agenda/components/AgendaEventDialog.vue index 4057f5e..c8f45ec 100644 --- a/src/features/agenda/components/AgendaEventDialog.vue +++ b/src/features/agenda/components/AgendaEventDialog.vue @@ -147,7 +147,16 @@ const props = defineProps({ blockOverlapWarning: { type: Object, default: null } }); -const emit = defineEmits(['update:modelValue', 'save', 'delete', 'updateSeriesEvent', 'editSeriesOccurrence', 'updated', 'usar-sessao', 'revogar-sessao']); +const emit = defineEmits(['update:modelValue', 'save', 'delete', 'updateSeriesEvent', 'editSeriesOccurrence', 'updated', 'usar-sessao', 'revogar-sessao', 'cobranca-atualizada']); + +// Helper: chamado pelo AgendaEventoFinanceiroPanel quando a cobrança +// muda (gerada, paga, cancelada). Refresha estado interno do dialog +// E bubble pra MelissaLayout disparar refetch da agenda (sem isso, o +// card do FC fica com paymentState stale até trocar de view). +function _onCobrancaAtualizada() { + loadOccFinancialRecord(); + emit('cobranca-atualizada'); +} const confirm = useConfirm(); const toast = useToast(); const router = useRouter(); @@ -1823,7 +1832,7 @@ onBeforeUnmount(() => { Para alterar tipo ou serviços, ajuste a cobrança no Financeiro abaixo. - + @@ -2338,7 +2347,7 @@ onBeforeUnmount(() => { Para alterar tipo ou serviços, ajuste a cobrança no Financeiro abaixo. - +
@@ -2407,7 +2416,7 @@ onBeforeUnmount(() => { v-if="!occFinancialRecord && !occFinancialLoading && isEdit && eventRow?.id" :evento="eventRow" class="m-3" - @cobranca-atualizada="loadOccFinancialRecord" + @cobranca-atualizada="_onCobrancaAtualizada" />