--- title: Plano de auditoria fase-a-fase — fluxo de compromisso da agenda date: 2026-05-13 status: em-andamento related: [[agenda-billing-pesquisa-mercado]], [[recorrencia-agenda]] --- ## Contexto Auditoria do ciclo completo de compromisso da agenda, fase-a-fase, validando cada etapa contra a [[agenda-billing-pesquisa-mercado|pesquisa de mercado]] (Cliniko / SimplePractice / TherapyNotes). Cada fase tem 3 entregas: **auditar o que existe**, **decidir o gap**, **codar**. ## Decisões já tomadas (5 das 8 perguntas) | # | Decisão | |---|---| | 1 | Disparo de cobrança: **híbrido configurável** (manual / status-driven / note-signed) | | 4 | No-show: **semi-automático via dialog de confirmação** ao mudar status | | 5 | Edit de cobrada: **bloqueia** (já implementado) | | 7 | Refund: **credit note nova** (alinhado NFS-e) | | 8 | Pagamento: **entidade separada** de financial_records | Pendentes: #2 (lead/Inquiry), #3 (pacote upfront), #6 (janela de cancelamento — provavelmente já resolvido por `min_hours_notice` em `financial_exceptions`). --- ## Plano de 8 fases Ordem por dependência ("o que destrava o quê") e por estado atual. ### ✅ Fase 1 — Compromisso SEM paciente (bloqueio/feriado/exceção) — **CONCLUÍDA 2026-05-13** **Auditoria fez:** - ✅ `agenda_excecoes` é tabela órfã (0 referências em src/) — apesar de schema, policies, trigger e enums existentes - ✅ `agenda_bloqueios` é a entidade canônica usada pelos 3 layouts - ✅ `BloqueioDialog` (4 modos: horário/período/dia/feriados) é compartilhado por Melissa Agenda (via `MelissaLayout.vue:2186`), Rail e Clínica - ✅ `MelissaBloqueios.vue` tem form inline próprio pra **admin/edit** (caso de uso legítimo distinto do dialog de 4 modos) - ✅ Bloqueios não eram renderizados no FullCalendar — apenas impediam criação. UX inconsistente vs pausas/feriados que aparecem como background events - ⚠️ Tipos customizáveis de bloqueio: descartado no MVP (sem cliente real) - ⚠️ Robustez de `marcarSessoesParaRemarcar`: adiado pra Fase 5 (status change) **Aplicado:** 1. Migration `20260513000001_drop_agenda_excecoes.sql` — dropa tabela + 2 enums + trigger; policies caem com CASCADE 2. `agendaMappers.js`: nova função `buildBloqueioBackgroundEvents(bloqueios, rangeStart, rangeEnd)` — renderiza bloqueios como background events cinza (`#6b728033`), suporta dia-inteiro, com hora, e recorrente semanal 3. Novo composable `useAgendaBloqueios.js` — load por owner único OU array (multi-owner pra Clínica), `buildEventsForRange` reutilizável 4. Wire em `useMelissaAgenda` + `MelissaAgenda.vue` — bloqueios concatenados ao `fcEvents` 5. Wire em `AgendaTerapeutaPage` — bloqueios concatenados ao `calendarEvents` 6. Wire em `AgendaClinicaPage` — bloqueios consolidados de todos os ownerIds 7. Refs stale removidas de `database-novo/docs/schema_map.md` e `database-novo/db.config.json` **Verificação:** - ESLint nos arquivos modificados: 0 errors novos (11 pré-existentes em código não-tocado) - Vitest `agendaMappers.spec.js`: 40/40 tests passed - ⚠️ **Falta rodar a migration no banco local** (pendente de execução manual; arquivo SQL pronto) - ⚠️ **Falta validar visualmente** nos 3 layouts (Melissa/Rail/Clínica) — verificar que bloqueios aparecem em cinza após criar pelo BloqueioDialog --- ### 🟢 Fase 2 — Compromisso COM paciente **Estado:** dialog refatorado em 11/05 (cards 40px, picker DataTable, 50/50 layout, 3 estados Sessão/Honorários, conceito Pacote, resumo flutuante). Working tree. **Auditar:** - Fluxo de cadastro mínimo de paciente in-line (já existe via `PatientCadastroDialog` quick mode?) - Decidir #2 (Inquiry/lead separado ou só quick-create) - Modalidade presencial/online consistente **Gap potencial:** - Quick-create exige só nome ou mais campos? (Cliniko: só nome; TherapyNotes: só last name) - Decisão #2 (Inquiry/lead) — adiar pra v2 provável **Codar:** ajustes pequenos, principalmente UX. Provavelmente quase nada novo. --- ### 🟢 Fase 3 — Recorrência **Estado:** modelo "1 real + N-1 virtual" + `occurrenceMode` no 2º dialog estabilizado em 12/05. Ver [[recorrencia-agenda]]. **Auditar:** - `occurrenceMode` já replicado em Melissa; falta Rail (`AgendaTerapeutaPage` L1630 + L3080) e Clínica (`AgendaClinicaPage` L1119 + L2398) - Decisão #3 (pacote upfront via account credit) — adiar provável **Codar:** replicar `occurrenceMode` em Rail/Clínica. Talvez add de pacote upfront (Cliniko model) numa fase futura. --- ### 🟠 Fase 4 — Cobrança: modo de disparo configurável (DECISÃO #1) **Estado:** Fase 1 atual ("Gerar cobrança ao salvar") existe como checkbox em criação avulsa+particular. Não tem setting de modo. **Auditar:** - Onde vive a config? Card novo em `/configuracoes/excecoes-financeiras` ou página irmã `/configuracoes/cobranca-defaults`? - Granularidade: por tenant (clínica), por owner (terapeuta), ou ambos com herança? **Gap:** - Tabela/coluna nova pra `charge_trigger_mode` enum (`manual` / `status_driven` / `note_signed`) - UI de config - Job overnight pra modo `status_driven` (Supabase edge function + cron) - Trigger no signature de nota pra `note_signed` (depende de modulo de notas; nao temos) - Checkbox atual da agenda passa a fazer sentido **só em modo manual** (ou vira override universal?) **Codar:** 1. Migration: setting de modo (tenant_billing_settings ou colunas em agenda_configuracoes) 2. UI de config 3. Job pra modo status_driven (avaliar se entra na v1 ou v2) 4. Refator do checkbox atual pra respeitar o modo --- ### 🟠 Fase 5 — Status change → cobrança com confirm dialog (DECISÃO #4) **Estado:** lógica automática roda em `useAgendaFinanceiro.handleStatusChange`. Consulta regra em `financial_exceptions`, cria/ajusta/cancela `financial_record` SEM perguntar. **Auditar:** - Quais status disparam: hoje só `faltou` e `cancelado` (mapping `STATUS_TO_EXCEPTION`) - `professional_cancellation` na tabela mas não no mapping - Onde `handleStatusChange` é chamado (quais entradas de status change disparam) **Gap:** - Confirm dialog ao mudar status pra `faltou` / `cancelado`: *"Aplicar cobrança de R$X conforme regra? [Sim / Não / Editar valor]"* - Adicionar `professional_cancellation` ao mapping (status atual da agenda inclui? checar) - Decidir: dialog aparece **sempre** ou só quando `charge_mode !== 'none'` **Codar:** 1. Dialog componente novo (`AgendaStatusChargeConfirmDialog.vue`) 2. Interceptar `handleStatusChange` antes da aplicação automática 3. Adicionar `professional_cancellation` no mapping 4. Toast diferenciado pra "aplicado/recusado/editado" --- ### 🟢 Fase 6 — Edit de cobrada (DECISÃO #5 — JÁ IMPLEMENTADO) **Estado:** `propagateToSerie` filtra por `financial_records` em status imutável. UI lock em `AgendaEventDialog` via `occFinancialRecord`. Working tree. **Auditar:** validar contra cenários reais (testar série com 4 sessões, 2 cobradas, 2 abertas; editar template; verificar que cobranças não mudam). **Codar:** zero (talvez add de aviso UX se faltar clareza). --- ### 🔴 Fase 7 — Pagamento como entidade separada (DECISÃO #8) **Estado:** hoje `financial_records.paid_at` marca pagamento (acoplado). Não tem entidade `payments` independente. **Auditar:** - Como financial_records.paid_at é usado hoje (queries de receita, dashboards, conciliação) - Webhook PSP existente? (provável que PIX e cartão sejam manuais hoje) **Gap:** - Migration: tabela `payments` (id, amount, method, paid_at, source, allocated_to_record_id NULL-able) - Alocação manual de pagamento "solto" a um financial_record - Pagamento parcial (1 payment cobre N records ou 1 record recebe N payments?) - Repo + composable + UI **Codar:** fase pesada — provavelmente sub-dividir. --- ### 🔴 Fase 8 — Reembolso / credit note (DECISÃO #7) **Estado:** hoje só tem `financial_records.status='cancelled'`. Não preserva original como doc fiscal. **Auditar:** processo fiscal atual (já emite NFS-e? quando? como cancela?) **Gap:** - Migration: tabela `credit_notes` (id, original_record_id, amount, reason, issued_at) - Constraint: credit note tem valor ≤ |original| - UI no Financeiro pra "Reembolsar" - Integração com NFS-e (pode ser separada) **Codar:** fase pesada — provavelmente sub-dividir. --- ### 🟣 Fase 9 — Plano Inicial (entrevista + N sessões regulares) **Estado:** apenas conceito; nada codado. **Pedido do user (2026-05-14):** clínica cobra **1 entrevista inicial** (valor X) + **4 sessões regulares** (valor Y cada). É o "plano de entrada" pra novos pacientes. User faz isso manualmente hoje na clínica dele. **Conceito:** - Config nas settings da agenda do tenant: - Toggle "Habilitar plano inicial" - Valor entrevista (R$) - Qtd de sessões regulares (default 4) - Valor por sessão regular (R$) - (Opcional) Texto/descrição que aparece no fluxo - Quando user cria 1ª sessão de **paciente novo** (sem histórico): - Sistema oferece: "Aplicar plano inicial? Entrevista R$ X + 4× R$ Y = total R$ Z" - Ao aceitar, materializa 5 sessões com `price` diferenciado: 1ª = X, demais = Y - Pode ser tratado como 1 série recorrente "especial" com 1ª ocorrência destacada - OU como 2 entidades distintas (1 avulsa entrevista + 1 série de 4) **Decisões pendentes:** - Estrutura: série única com 1ª diferenciada OU avulsa + série separada? - Onde fica a config: `agenda_configuracoes` (jsonb adicional?) ou tabela nova `intake_plans`? - "Paciente novo" = sem sessões anteriores? Ou marcador manual no cadastro? - Plano único do tenant ou múltiplos planos (avaliação clínica, avaliação neuropsi, etc)? **Cabe na Fase 4 (cobrança)?** Não — Fase 4 é só modo de disparo; aqui é estrutura de pacote pré-configurado. Fica como Fase 9 separada. --- ## Ordem sugerida de execução | Ordem | Fase | Razão | |---|---|---| | 1ª | **Fase 1** | Curta, validação, define se tem cleanup de tabelas necessário | | 2ª | **Fase 5** | Destrava UX urgente (confirm dialog evita cobrar errado) | | 3ª | **Fase 4** | Híbrido configurável — destrava racional do checkbox atual | | 4ª | **Fase 2** | Quase 100% pronta, validar e finalizar | | 5ª | **Fase 3** | Replicar `occurrenceMode` em Rail/Clínica | | 6ª | **Fase 6** | Já feito; só testar | | 7ª | **Fase 7** | Refator estrutural pesado — entra depois das fases UX | | 8ª | **Fase 8** | Depende fiscal NFS-e — pode ir pra v2 | | 9ª | **Fase 9** | Plano Inicial (entrevista + 4 sessões) — pedido do user, conceito pronto, codar pós-7 | ## Como cada fase termina 1. Página da fase na wiki é atualizada com o resultado 2. Commit dedicado com prefixo `agenda(fase-N): ...` 3. Update no [[index]] da wiki 4. Entrada no `log.md`