Files
agenciapsilmno/HANDOFF.md
T
Leonardo 1feb7112ff 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>
2026-05-19 20:54:23 -03:00

22 KiB
Raw Blame History

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-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 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 8 (Pacote SALDO)

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)

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

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

  • 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 (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 (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