---
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`
