Files
agenciapsilmno/HANDOFF.md
T
Leonardo e95ed9b585 agenda: Fase 5 (status change/edit cobrada) + indicadores visuais + UX convenio
DB
- drop agenda_excecoes (substituida por financial_exceptions + lock-edit
  baseado em financial_records)
- financial_records.payment_link (Asaas + link compartilhavel)
- financial_exceptions.consume_on_miss (rotular nao-show consome ou nao)
- billing_contracts.charging_style (upfront/saldo/per_session)

Payment refactor
- paymentSettlement -> paymentMethod (string) + markPaidNow (bool).
  Handler aplica payment_method sempre; status='paid'+paid_at apenas
  quando markPaidNow=true && method != 'link'. Asaas (link) sempre
  liquida via webhook, nunca nasce paid.
- create_financial_record_for_session com pos-RPC patch pra payment_method
  e (opcional) status='paid' quando user marca "ja recebi".

Indicadores visuais (3 canais distintos por estado)
- Paid: barra esquerda emerald-500 4px na agenda (MelissaAgenda),
  pi-check-circle no popover/Resumo.
- Pending: badge \$ amber canto direito (mantido); linha amber no popover/
  Resumo "A receber R\$ X (cobranca pendente)".
- Neutro: sem badge nem barra (compromisso pessoal, bloqueio, ou
  ocorrencia virtual de pacote upfront/saldo).
- Bulk-load de paymentState em _reloadRange etapa 4 (1 query unica em
  financial_records mapeada por agenda_evento_id).
- AgendaEventDialog Resumo lateral ganha linha entre pi-clock e
  pi-map-marker via novo sessionPaymentRecord (sem guard de
  occurrenceMode, contrario ao occFinancialRecord que continua so pra
  Rail/Clinica). 5 estados: paid+paid_at, overdue+venceu, pending+vence,
  sem cobranca c/ valor, sem cobranca s/ valor.

UX de convenio
- InsurancePlanServiceQuickCreateDialog novo: cadastra procedimento
  POR CIMA do AgendaEventDialog sem sair da agenda. Auto-seleciona
  novo procedimento so quando nada estava selecionado antes.
- Caixa cinza "Cadastrar procedimento" sempre visivel quando convenio
  selecionado, com copy variavel (0 procedimentos: chamada urgente;
  1+: "se quiser adicionar mais").
- "+ Novo convenio" toolbar em ConfiguracoesConveniosPage (botao
  estava faltando, empty state mandava clicar em botao inexistente).
- Hint contextual abaixo do card Sessao/Honorarios: convenio = "N da
  guia eh opcional", gratuito = "sem cobranca", particular = sem hint.
  Label "N da Guia" tambem ganhou "(opcional)" no service-picker dialog.

Bug fixes
- pickDbFields whitelist faltando 'modalidade' (useMelissaAgenda.js:74)
  — sessoes avulsas eram salvas como presencial independente da
  escolha visual. Adicionado.
- goToConveniosConfig removida — fazia router.push("/therapist/
  configuracoes/convenios") mas /configuracoes/* eh rota raiz, nao
  filha. Substituida pelo quick-create inline (#1).
- bloqueioCobrindo + dialogBlockOverlap passados via deps em
  _buildHandlers (refs do useMelissaAgenda nao sao acessiveis no
  escopo de _buildHandlers).

Fase 5 (status change + edit cobrada)
- AgendaStatusChangeConfirmDialog: confirm dialog quando user muda
  status pra realizada/faltou/cancelado, com opcoes de markPaid ou
  gerar cobranca conforme o caso.
- useAgendaBloqueios novo composable: extrai logica de bloqueios
  cinza (background events) do MelissaAgenda.

Doc viva
- src/docs/agenda-compromisso-financeiro-cenarios.html: 13 cenarios
  de teste manual. C1-C4 ja validados. Cada teste validado vira parte
  da doc final pra area de ajuda (pos-Fase 9).

Wiki/handoff
- agenda-compromisso-fluxo e agenda-billing-pesquisa-mercado (decisoes
  arquiteturais sobre billing).
- HANDOFF.md atualizado.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 08:31:18 -03:00

232 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# HANDOFF — 2026-05-18 noite (C1-C4 OK, UX convênio refinado, C5 ainda não rodou save)
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-C4 ✅, **C5
> ainda não rodou save** — a tarde do dia 18 foi consumida refinando UX
> de convênio (3 bugs/melhorias) e preparando o C5. Próximo passo:
> **executar de fato o save do C5** (Sándor + Unimed Nacional + R$ 95).
> **🟡 WORKING TREE BEM PESADO** — refactor de payment, indicadores
> visuais (barra verde + popover + Resumo do dialog), inline quick-create
> de procedimento, fix de rota convênios, botão "+ Novo convênio",
> hint contextual. Migrations da Fase 5 já rodadas em 14/05. **Considerar
> commitar antes de mais trabalho** — diff tá grande.
---
## 🔴 PRÓXIMO PASSO IMEDIATO — Cenário 5 (Convênio)
Receita do doc HTML. Resumo:
| Campo | Valor |
|---|---|
| Paciente | **Sándor** |
| Convênio | **Unimed Nacional** (criar via `InsurancePlanQuickCreateDialog` se não existir) |
| Valor | **R$ 95** |
**Esperado:**
- Record com `insurance_plan_id` preenchido + pill "convênio" visível
- Na agenda: badge $ amber (record pendente até fechamento mensal do convênio)
- Popover: linha amber "A receber R$ 95,00 (cobrança pendente)"
Após o 5 passar: 6-9 (recorrentes) → 10-13 (status change + edit cobrada). Quando todos passarem, replicar em **Rail** (`AgendaTerapeutaPage.vue`) e **Clínica** (`AgendaClinicaPage.vue`).
---
## 📦 O que foi feito em 18/05
### Cenário 4 (Joyce · "Já recebi") ✅
- Testado e passou: toast "Cobrança paga R$ 180,00 recebido via PIX", record nasceu `paid + payment_method=pix + paid_at=now()`.
### Novo indicador: barra esquerda verde para sessão paga
- Brainstorm de 6 opções; user escolheu #6 (3 canais visuais distintos por estado).
- `MelissaAgenda.vue:395-419` — computa `isPaidSession` (sessão+paciente+não-virtual+`paymentState==='paid'`) e adiciona classe `ma-evt--paid` ao FC event (combina com `ma-evt--inactive-patient` se ambos).
- `MelissaAgenda.vue:2325-2335` — CSS força `border-left-color: #10b981 !important` (emerald-500, 4px). `!important` necessário porque FC seta `borderColor` inline. Trata também list view (`.fc-list-event-dot`).
- Doc HTML atualizado: legenda "Indicadores visuais" agora descreve **3 estados** (pendente / pago / neutro) com 3 mocks empilhados; estado-alvo do C4 reescrito mencionando a barra verde.
- Decisão salva em `memory/project_agenda_payment_indicators.md`.
### Linha "Cobrança" no popover + Resumo do dialog
- **Popover `MelissaEventoPanel`** — antes só mostrava amber "A receber R$ X" pra pendente. Agora cobre os 3 estados, com cor + ícone por variante:
- `paid``pi-check-circle` verde, label **"Pago · R$ X,XX"**
- `pending``pi-dollar` amber, label **"A receber R$ X (cobrança pendente)"** (mantido)
- `none``pi-dollar` amber, label **"A cobrar R$ X"** ou **"Cobrança ainda não gerada"** (mantido)
- CSS reescrito em 3 modificadores `.evento-row--pay-{paid|pending|none}` (com dark mode).
- **Resumo lateral do `AgendaEventDialog`** — nova linha entre `pi-clock` e `pi-map-marker` em ambas as cópias (mobile inline + desktop floating).
- Novo ref `sessionPaymentRecord` em `useAgendaEventLifecycle.js:104+` (sem guard de `occurrenceMode`, contrário ao `occFinancialRecord` que continua só pra Rail/Clínica). Loader `loadSessionPaymentRecord` chamado no mesmo lifecycle.
- Computed `paymentSummary` em `AgendaEventDialog.vue:951+` retorna `{icon, cls, label}` pra 5 casos: paid (verde + paid_at), overdue (vermelho + due_date), pending (amber + due_date), sem cobrança c/ valor (neutro), sem cobrança s/ valor (neutro).
- `@cobranca-atualizada` do `AgendaEventoFinanceiroPanel` agora também dispara `loadSessionPaymentRecord` pra a linha refrescar.
- **Importante:** `occFinancialRecord` (que aciona lock-edit) NÃO foi tocado de propósito — esse é território da Fase 6/C13 (Edit cobrada). Manter dois refs separados evita ativar lock prematuro em Melissa.
### Preparação do C5 (Sándor + Unimed Nacional) — UX de convênio refinado (3 issues)
User tentou rodar C5 e bateu em 3 problemas seguidos. Cada um virou um fix:
1. **Botão "Cadastrar" do procedimento navegava pra `/pages/notfound`**
- Root cause: `goToConveniosConfig` em `AgendaEventDialog.vue` prefixava com `/therapist` ou `/admin`, mas `/configuracoes/*` é rota **raiz** sob `AppLayout` (sibling, não filho). Em Melissa, convênios mora dentro do próprio layout via `secao: 'cfg-convenios'` (sem URL própria).
- Fix descartado: o user não queria sair da agenda. Em vez disso, criamos um quick-create inline (ver #2). `goToConveniosConfig` foi removida (dead code virou armadilha).
2. **Quick-create de procedimento inline (sem sair da agenda)**
- Novo componente `InsurancePlanServiceQuickCreateDialog.vue` (modelo do `InsurancePlanQuickCreateDialog`). 2 campos: nome do procedimento + valor que o convênio paga. Insere em `insurance_plan_services` pro `insurance_plan_id` ativo.
- Wiring em `useAgendaEventLifecycle.js`: novo `planServiceQuickDlgOpen` + `openPlanServiceQuickCreate()` + `onPlanServiceCreated(service)`. Após criar, recarrega `loadInsurancePlans` e **auto-seleciona** o novo procedimento **só quando nada estava selecionado antes** (preserva escolha quando user já tinha selecionado X e está só cadastrando Y pra próxima).
- UI refatorada (`AgendaEventDialog.vue:3110+`): a caixa cinza com botão "Cadastrar" agora aparece **sempre** que um convênio está selecionado. Quando 0 procedimentos: **"Este convênio ainda não tem procedimentos cadastrados."** Quando 1+: **"Se quiser adicionar mais procedimentos a este convênio:"**.
- `planServiceQuickDlgOpen` adicionado ao `anyChildDialogOpen` pra esconder o Resumo flutuante enquanto o quick-create está aberto.
3. **Botão "+ Novo convênio" faltando em `/melissa/cfg-convenios` (e na rota canônica também)**
- Root cause: `ConfiguracoesConveniosPage.vue` tinha o form de "Novo convênio" condicionado a `addingNew === true`, mas **nenhum botão setava esse flag**. Empty state mandava "Clique em 'Novo convênio'" sem botão pra clicar.
- Fix: toolbar simples no topo do template `<template v-else>` com `<Button label="Novo convênio" icon="pi pi-plus" @click="addingNew = true">`. Empty state corrigida pra apontar pro botão certo.
### Hint contextual abaixo do card Sessão / Honorários
- User pediu mensagem clarificando que "Nº da guia" é opcional em convênio.
- **Tentativa 1 (errou o lugar):** coloquei o hint em `AgendaEventDialog.vue:1826` dentro do bloco `v-if="occurrenceMode"` (só edita ocorrência em Rail/Clínica). User não viu.
- **Tentativa 2 (correta):** adicionado em `AgendaEventDialog.vue:2305+` (fluxo principal Melissa, fora do occurrenceMode). Mantive a tentativa 1 também — não atrapalha, só ativa em outro contexto.
- Texto: convênio = **"Nº da guia é opcional — você pode salvar a sessão e preencher depois, quando o convênio responder."** Gratuito = **"Sessão gratuita — nenhum lançamento será gerado no Financeiro."** Particular = sem hint (não há ambiguidade).
- Condição: `isSessionEvent && !occFinancialRecord && billingType === 'convenio'|'gratuito'`. Esconde quando há cobrança paga/pendente (lock-edit) — Message do panel já cobre.
- CSS: `.aed-billing-hint` em `AgendaEventDialog.vue:3558+` — barra esquerda primary, fundo neutro leve, fonte 0.78rem.
- Label do "Nº da Guia" no service-picker dialog também ganhou **(opcional)**.
---
## 📦 O que foi feito antes (16/05 noite/madrugada)
### Cenário 1 (Bloqueio) ✅
1. **Fix `bloqueioCobrindo is not defined`** — função estava no escopo de `useMelissaAgenda` mas `onSelectTime` mora no `_buildHandlers` (outro escopo). Passada via `deps`. Mesmo padrão que `_openStatusDialog`.
2. **Soft warn dentro do dialog** em vez de toast atrás do overlay — novo ref `dialogBlockOverlap` no composable + nova prop `blockOverlapWarning` no `AgendaEventDialog` + Message warn no topo do step 1. Reset nos outros openers (`onCreateEvento`, `onCreateEventoForPatient`, `onEditEvento`).
3. **Doc HTML Cenário 1 expandido** em 1a (criar bloqueio) + 1b (agendar sobre bloqueio), com mock visual da Message + comparação com agendador público (que veta).
### Cenário 2 (Avulsa sem cobrança) ✅
4. **Fonte da hint chargeMode** subiu de `0.72rem``0.8125rem` (acima de `text-xs`).
5. **Card Frequência avulsa** refeito — antes era empty state convidando configurar; agora renderiza com `.aed-pay-summary` (mesma estrutura do estado configurado: "Tipo: Avulsa · Sessão única, sem repetição" + botão Editar).
6. Doc HTML Cenário 2 atualizado.
### Cenário 3 (Avulsa cobrar ao salvar) ✅
7. **Refactor payment: `paymentSettlement` → `paymentMethod` + `markPaidNow`**
- UI antiga misturava método e status num único Select ("Já recebi — PIX").
- Agora 2 controles: Select forma (Enviar link / PIX / Dinheiro / Depósito / Cartão maquininha — SEM prefixo "Já recebi —") + SelectButton status (Cobrança pendente / Já recebi (dar baixa)).
- SelectButton só aparece quando método ≠ link (Asaas só liquida via webhook).
- Watcher força `markPaidNow=false` se voltar pra 'link'.
- Wire: AgendaEventDialog → useAgendaEventActions → useMelissaAgenda (handler avulsa + `_createPackageContract`).
8. **Indicadores visuais de pagamento** (novidade da sessão):
- Bulk-load de `financial_records` em `_reloadRange` etapa 4 (1 query única, mapa eventId → 'paid' | 'pending' | 'none').
- `normalizeForMelissa` agora injeta `paymentState` + `price` no evento.
- **Badge $ no canto** dos eventos da agenda — círculo amber 16px no canto superior direito. Só pra sessão + paciente + não-virtual + paymentState !== 'paid'.
- **Linha "A receber"** no popover (`MelissaEventoPanel`) — texto adaptativo: "A receber R$ X (cobrança pendente)" se pending, "A cobrar R$ X" se none, "Cobrança ainda não gerada" se sem valor.
9. **🐛 Bug fix `pickDbFields` faltando `modalidade`** — sessões avulsas eram salvas sem modalidade, DB caía no default 'presencial' independente da escolha. Adicionado ao whitelist em `useMelissaAgenda.js:74`. **TODAS as sessões avulsas criadas no Melissa antes desse fix estão como 'presencial' no DB** — pode precisar rodar UPDATE manual no banco se quiser corrigir histórico. Gotcha salvo em `memory/project_pickdbfields_whitelist.md`.
10. **Doc HTML atualizada amplamente**:
- Nova seção topo `★ Indicadores visuais de pagamento` com mocks (badge $ + linha popover) e link em violeta no TOC.
- Caixa violeta "Indicadores visuais" em cada cenário relevante (C2-C9).
- C4 ganhou caixa verde "estado-alvo" (sem badge, sem linha — pago).
- Receita do C3 e C4 atualizadas com os 3 controles (Cobrança ao salvar / Forma de pagamento / Status do pagamento) e opções limpas (sem prefixo "Já recebi —").
---
## 🧭 Onde estamos no plano de 9 fases
| Fase | Status |
|---|---|
| **1** Compromisso SEM paciente | ✅ |
| **2** Compromisso COM paciente | ✅ testado (C1-C3 done) |
| **3** Recorrência + replicar occurrenceMode Rail/Clínica | ⏳ |
| **4** Modo disparo cobrança híbrido | ⚠️ parcial |
| **5** Status change → confirm dialog | 🔄 Melissa codado + indicadores visuais done; falta testar (C10-C12) + replicar Rail/Clínica |
| **6** Edit cobrada | ✅ |
| **7** Pagamento separado | ⏳ |
| **8** Refund/credit note | ⏳ |
| **9** Plano Inicial | 📋 |
---
## 📋 Roteiro de testes restantes (`src/docs/agenda-compromisso-financeiro-cenarios.html`)
| # | Cenário | Status |
|---|---|---|
| 1 | Bloqueio (criar + agendar sobre) | ✅ |
| 2 | Avulsa sem cobrança | ✅ |
| 3 | Avulsa cobrar ao salvar | ✅ |
| 4 | Avulsa "já recebi" no salvar | ✅ |
| **5** | **Avulsa convênio (Sándor + Unimed)** | 🔴 **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) | ⏳ |
| 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 | ⏳ |
---
## 📋 Como retomar amanhã (cego)
1. `git status` — confirmar working tree intacto
2. **Ler HANDOFF até o fim**
3. Abrir `src/docs/agenda-compromisso-financeiro-cenarios.html` no browser pra ver o estado atual do doc viva
4. **Começar pelo Cenário 4** (Joyce, "Já recebi (dar baixa)")
5. Cada cenário que passar:
- Atualizar status pra ✅ aqui no HANDOFF
- Se descobrir bug ou texto divergente, corrigir código + doc na hora
6. Quando todos os 13 passarem: replicar em **Rail** e **Clínica**
7. Adicionar `professional_cancellation` no `STATUS_TO_EXCEPTION`
8. Marcar Fase 5 como ✅
9. Decidir Fase 4 (modo disparo cobrança híbrido) OU Fase 3 (replicar occurrenceMode)
---
## 🚨 Pendência IMPORTANTE — não esquecer
**Pós-Fase 9** (quando concluirmos TODAS as fases 1-9):
- User vai passar prompt específico pra criar **documentação completa da parte de ajuda** do sistema
- Está em `memory/project_pendencia_doc_ajuda.md`
- O doc `agenda-compromisso-financeiro-cenarios.html` já está sendo escrito de forma que vira a doc final pra usuário (cada teste validado vira parte da doc)
**Histórico modalidade='presencial' no DB:**
- Bug do `pickDbFields` afetou TODAS as sessões avulsas criadas no Melissa até 16/05/2026
- Se quiser corrigir histórico, rodar UPDATE manual identificando sessões cuja modalidade visual era online (não há como saber retroativamente — perdido)
- Going forward o fix já cobre
---
## ⚠️ Gotchas duráveis (atualizados)
- **`MelissaBloqueios.vue` admin ≠ `BloqueioDialog` (4 modos)** — casos distintos
- **`agenda_excecoes` foi dropada** em 13/05
- **`financial_records.type` undefined sem `type` no BASE_SELECT** — fix 14/05 cedo
- **`financial_records.description` undefined sem `description` no BASE_SELECT** — fix 14/05 noite
- **`handleStatusChange` em `useAgendaFinanceiro.js` está ÓRFÃO** — não reativar
- **`_openStatusDialog` + `bloqueioCobrindo` + `dialogBlockOverlap`** declarados no `useMelissaAgenda` mas usados em `_buildHandlers` — passados via `deps`. **NÃO ESQUECER ao replicar em Rail/Clínica**
- **`billing_contracts.charging_style`** distingue upfront/saldo/per_session
- **Ocorrência virtual tem `id="rec::<rule>::<date>"`** — detectar via `typeof === 'string' && startsWith('rec::')` antes de query Supabase
- **`chargeMode` default dinâmico:** `'session'` em avulsa, `'none'` em recorrente
- **Toast atrás do overlay do dialog** — usar Message no topo do dialog em vez de toast quando contexto for dentro de dialog modal
- **Cuidado com `pickDbFields` whitelist** — `useMelissaAgenda.js:74` descarta campos não listados silenciosamente. Sintoma: campo escolhido na UI mas DB tem valor default. Memória: `memory/project_pickdbfields_whitelist.md`
- **`paymentSettlement` foi renomeado** em 16/05 — agora `paymentMethod` (string) + `markPaidNow` (bool). Handler aplica `payment_method` sempre, `status='paid'` só quando markPaidNow=true && method!='link'
- **Bulk-load de paymentState em `_reloadRange` etapa 4** — 1 query única em `financial_records` mapeada por `agenda_evento_id`. Anota `paymentState` no normalize. Badge na agenda + linha popover lêem daqui
---
## 🧠 Decisões persistidas (memory/)
**Indicadores visuais (16/05):**
- Badge $ no canto: só sessão + paciente + não-virtual + !paid
- Linha popover: 3 textos (a receber pending / a cobrar none / cobrança não gerada)
- Bulk-load 1x por _reloadRange, não query por evento
- Ocorrências virtuais sempre paymentState='none' (cobertas por contrato)
**Payment refactor (16/05):**
- Separar método (forma) de status (já pago?) — controles independentes na UI
- Método 'link' (Asaas) força markPaidNow=false (gateway externo)
- Wire format: `arg.paymentMethod` + `arg.markPaidNow` (no lugar de `arg.paymentSettlement`)
**Bugs evitar repetir:**
- Sempre adicionar campo novo ao `pickDbFields.allowed` quando adicionar coluna em agenda_eventos
- Sempre adicionar campo novo ao `BASE_SELECT` quando query custom
- Detectar `is_occurrence` ou `rec::` antes de query por UUID
- Refs/funções do composable principal NÃO ficam acessíveis em `_buildHandlers` — passar via `deps`
- Toast dentro de dialog modal fica atrás do overlay — usar Message