agenda: Fase 5 (status change/edit cobrada) + indicadores visuais + UX convenio
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>
This commit is contained in:
@@ -106,7 +106,7 @@
|
||||
"contact_email_types", "contact_emails"
|
||||
],
|
||||
"Agenda / Agendamento": [
|
||||
"agenda_eventos", "agenda_bloqueios", "agenda_configuracoes", "agenda_excecoes",
|
||||
"agenda_eventos", "agenda_bloqueios", "agenda_configuracoes",
|
||||
"agenda_online_slots", "agenda_regras_semanais",
|
||||
"agenda_slots_bloqueados_semanais", "agenda_slots_regras",
|
||||
"agendador_configuracoes", "agendador_solicitacoes"
|
||||
|
||||
@@ -43,13 +43,12 @@ Mapa completo do banco de dados PostgreSQL 17, extraído de `schema.sql` (2026-0
|
||||
| `module_features` | Features por módulo |
|
||||
| `tenant_modules` | Módulos ativos por tenant |
|
||||
|
||||
### Agenda (11 tabelas)
|
||||
### Agenda (10 tabelas)
|
||||
| Tabela | Descrição |
|
||||
|--------|-----------|
|
||||
| `agenda_bloqueios` | Bloqueios de horário |
|
||||
| `agenda_configuracoes` | Configurações da agenda por tenant_member |
|
||||
| `agenda_eventos` | Eventos da agenda (sessões, bloqueios) |
|
||||
| `agenda_excecoes` | Exceções na agenda (horários extras, bloqueios pontuais) |
|
||||
| `agenda_online_slots` | Slots de agendamento online |
|
||||
| `agenda_regras_semanais` | Regras semanais de disponibilidade |
|
||||
| `agenda_slots_bloqueados_semanais` | Slots bloqueados na semana |
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
-- ============================================================================
|
||||
-- Drop agenda_excecoes (tabela órfã) + tipos relacionados
|
||||
-- ----------------------------------------------------------------------------
|
||||
-- A tabela `public.agenda_excecoes` foi criada num design anterior pra
|
||||
-- representar "exceções no horário de trabalho" (almoço extra, atendimento
|
||||
-- fora do padrão, etc) mas nunca foi integrada à UI. Auditoria em
|
||||
-- 2026-05-13 confirmou 0 referências em src/. As funcionalidades equivalentes
|
||||
-- vivem em:
|
||||
-- - public.agenda_bloqueios — bloqueios (período, dia, horário, feriado)
|
||||
-- - public.agenda_configuracoes.pausas_semanais (jsonb) — pausas semanais
|
||||
-- - public.feriados — feriados nacionais/municipais
|
||||
--
|
||||
-- Esta migration:
|
||||
-- 1) Dropa o trigger tg_agenda_excecoes_updated_at
|
||||
-- 2) Dropa a tabela public.agenda_excecoes (CASCADE pra cair policies)
|
||||
-- 3) Dropa os enums tipo_excecao_agenda e status_excecao_agenda
|
||||
-- (verificados: usados APENAS por agenda_excecoes)
|
||||
-- ============================================================================
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- 1. Trigger (idempotente)
|
||||
DROP TRIGGER IF EXISTS tg_agenda_excecoes_updated_at ON public.agenda_excecoes;
|
||||
|
||||
-- 2. Tabela (CASCADE leva policies junto)
|
||||
DROP TABLE IF EXISTS public.agenda_excecoes CASCADE;
|
||||
|
||||
-- 3. Enums órfãos
|
||||
DROP TYPE IF EXISTS public.tipo_excecao_agenda;
|
||||
DROP TYPE IF EXISTS public.status_excecao_agenda;
|
||||
|
||||
COMMIT;
|
||||
@@ -0,0 +1,24 @@
|
||||
-- ============================================================================
|
||||
-- Adiciona coluna payment_link em financial_records
|
||||
-- ----------------------------------------------------------------------------
|
||||
-- Quando a cobrança for paga via gateway externo (Asaas, Stripe, Mercado Pago)
|
||||
-- e o terapeuta escolher "Enviar link de pagamento" no AgendaEventDialog, o
|
||||
-- link de cobrança gerado pelo gateway é salvo aqui. UI da lista do Financeiro
|
||||
-- usa esse campo pra exibir ícone clicável (external-link).
|
||||
--
|
||||
-- Campo nullable: registros sem integração de gateway (PIX manual, dinheiro,
|
||||
-- depósito, cartão maquininha) ficam com payment_link = NULL.
|
||||
--
|
||||
-- Preparação pra Fase 7 (Pagamento como entidade separada) — quando a
|
||||
-- integração Asaas estiver completa, o webhook vai preencher esse campo.
|
||||
-- ============================================================================
|
||||
|
||||
BEGIN;
|
||||
|
||||
ALTER TABLE public.financial_records
|
||||
ADD COLUMN IF NOT EXISTS payment_link text;
|
||||
|
||||
COMMENT ON COLUMN public.financial_records.payment_link IS
|
||||
'URL externa de cobrança (Asaas/Stripe/etc) quando payment_method indica gateway. Null em pagamentos manuais.';
|
||||
|
||||
COMMIT;
|
||||
@@ -0,0 +1,22 @@
|
||||
-- ============================================================================
|
||||
-- Adiciona coluna default_consume_on_miss em financial_exceptions
|
||||
-- ----------------------------------------------------------------------------
|
||||
-- Define o comportamento padrão pro saldo de pacote quando o status muda
|
||||
-- pra "faltou" ou "cancelado":
|
||||
-- true → desconta 1 sessão do pacote (sessions_used += 1) por padrão
|
||||
-- false → não consome saldo (sessão fica disponível pra remarcar)
|
||||
--
|
||||
-- O dialog de confirmação que aparece ao mudar status sugere essa decisão
|
||||
-- mas o terapeuta pode override caso a caso. Padrão começa false (mais
|
||||
-- benevolente ao paciente).
|
||||
-- ============================================================================
|
||||
|
||||
BEGIN;
|
||||
|
||||
ALTER TABLE public.financial_exceptions
|
||||
ADD COLUMN IF NOT EXISTS default_consume_on_miss boolean DEFAULT false NOT NULL;
|
||||
|
||||
COMMENT ON COLUMN public.financial_exceptions.default_consume_on_miss IS
|
||||
'Default pro toggle "Descontar do saldo" no dialog de status change. false = não consome (paciente pode remarcar); true = consome (sessão perdida).';
|
||||
|
||||
COMMIT;
|
||||
@@ -0,0 +1,31 @@
|
||||
-- ============================================================================
|
||||
-- Adiciona coluna charging_style em billing_contracts
|
||||
-- ----------------------------------------------------------------------------
|
||||
-- Identifica como o pacote foi cobrado na criação:
|
||||
-- 'upfront' → 1 financial_record total criado na hora; sessões só
|
||||
-- consomem saldo, não geram nova cobrança
|
||||
-- 'saldo' → sem financial_record na criação; cada sessão realizada
|
||||
-- gera 1 cobrança individual e incrementa sessions_used
|
||||
-- 'per_session'→ N financial_records já criados na materialização da série
|
||||
-- (chargeMode='per_session' do AgendaEventDialog)
|
||||
--
|
||||
-- Sem esse campo, o handler de status change não saberia distinguir entre
|
||||
-- "já tudo pago, só atualizar status" vs "criar cobrança nova".
|
||||
-- ============================================================================
|
||||
|
||||
BEGIN;
|
||||
|
||||
ALTER TABLE public.billing_contracts
|
||||
ADD COLUMN IF NOT EXISTS charging_style text DEFAULT 'saldo';
|
||||
|
||||
-- Constraint pra restringir aos 3 valores válidos
|
||||
ALTER TABLE public.billing_contracts
|
||||
DROP CONSTRAINT IF EXISTS billing_contracts_charging_style_chk;
|
||||
ALTER TABLE public.billing_contracts
|
||||
ADD CONSTRAINT billing_contracts_charging_style_chk
|
||||
CHECK (charging_style = ANY (ARRAY['upfront'::text, 'saldo'::text, 'per_session'::text]));
|
||||
|
||||
COMMENT ON COLUMN public.billing_contracts.charging_style IS
|
||||
'Estilo de cobrança: upfront (1 record total no início), saldo (cobra por sessão realizada), per_session (N records já criados).';
|
||||
|
||||
COMMIT;
|
||||
Reference in New Issue
Block a user