e95ed9b585
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>
217 lines
11 KiB
Markdown
217 lines
11 KiB
Markdown
---
|
||
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`
|