Files
agenciapsilmno/HANDOFF.md
T
Leonardo d6423da9b4 agenda: pre-C10 fix _applyStatusDecisions cancela pendingRecord
Bug: ao mudar status pra faltou/cancelado com multa configurada
em financial_exceptions, _applyStatusDecisions INSERIA o novo
record da multa MAS deixava o pendingRecord original em pending.
Resultado: cobranca dupla (R$ 200 original + R$ 30 multa = R$ 230).

Fix em useMelissaAgenda.js:1450-1505:
- applyFine agora carrega data da sessao na description ("Multa
  por falta - sessao dd/mm/aa") pro paciente identificar na fatura.
- Novo bloco 2b: cancela ctx.pendingRecord quando faltou/cancelado,
  com nota de auditoria appendada em notes ("[YYYY-MM-DD] Cancelada
  - substituida por multa de no-show" ou similar). Vale tanto pra
  caso com multa quanto sem (status mudado sem aplicar multa).

Fix dormente em useAgendaFinanceiro.js:59 ('fixed' -> 'fixed_fee')
- charge_mode no schema eh 'fixed_fee' mas calcChargeAmount usava
  'fixed' silenciosamente caia no fallback. Path nao exercitado na
  Melissa (usa _applyStatusDecisions, nao handleStatusChange), mas
  iria quebrar se algum dia fosse.

Pre-teste C10: financial_exceptions seedadas no DB para tenant
Bruno Terapeuta / owner Leonardo:
- patient_no_show: fixed_fee R$ 30
- patient_cancellation: full, min_hours_notice=2, default_consume_on_miss=true

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

27 KiB
Raw Blame History

HANDOFF — 2026-05-20 (C10 pré-teste · code-fix + seeds prontos)

Documento de continuidade. Quando voltar, comece lendo esta página até o fim.

🎯 SE A FORÇA CAIR / SESSÃO PERDER CONTEXTO: estamos no Cenário 10 (status change avulsa). Code-fix pra cancelar pendingRecord aplicado + financial_exceptions da Melissa seedadas no DB. Falta: rodar os 5 sub-testes (a/a2/b/c/c2) e validar.

🟢 PRÉ-TESTE C10 SALVO. Pós-teste, próximos cenários: C11 (status change pacote saldo) → C12 (antecipar pagamento) → C13 (edit cobrada).

Code-fix aplicado em 20/05 (pré-C10)

  • useMelissaAgenda.js:1450-1505_applyStatusDecisions agora cancela o ctx.pendingRecord quando faltou/cancelado (com ou sem multa). Antes inseria a multa mas DEIXAVA o original pending → cobrança dupla (R$ 200 + R$ 30 = R$ 230). Audit trail vai em notes do record cancelado, descrição da multa nova carrega data: "Multa por falta · sessão 20/05/26".
  • useAgendaFinanceiro.js:59 — fix dormente 'fixed''fixed_fee' (off-by-key contra schema; path nunca exercitado na Melissa, mas iria quebrar se algum dia fosse).

Financial exceptions seedadas (tenant Bruno Terapeuta / owner Leonardo)

  • patient_no_showfixed_fee R$ 30
  • patient_cancellationfull, min_hours_notice=2, default_consume_on_miss=true

🔴 PRÓXIMO PASSO IMEDIATO — Cenário 10 (Status change AVULSA)

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).

Possíveis pacientes pra teste: usar Joyce, Sándor ou outro com cobrança avulsa pendente já criada.

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

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.


📦 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_idselectedCommitment=nullisSessionEvent=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)

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")

  • 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:
    • paidpi-check-circle verde, label "Pago · R$ X,XX"
    • pendingpi-dollar amber, label "A receber R$ X (cobrança pendente)" (mantido)
    • nonepi-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)

  1. Fonte da hint chargeMode subiu de 0.72rem0.8125rem (acima de text-xs).
  2. 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).
  3. Doc HTML Cenário 2 atualizado.

Cenário 3 (Avulsa cobrar ao salvar)

  1. Refactor payment: paymentSettlementpaymentMethod + 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).
  2. 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.
  3. 🐛 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.
  4. 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)
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)
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)
13 Edit cobrada (parcialmente — lock ativo em Melissa pós-19/05 noite)

📋 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 whitelistuseMelissaAgenda.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