Agenda · Compromisso → Financeiro cenários (doc viva)

Esquerda: o que o terapeuta vê na agenda. Direita: o que aparece no financeiro do paciente.

v1 · 2026-05-15 Fase 5 em teste

Indicadores visuais de pagamento

Como o sistema mostra "essa sessão ainda não foi paga" sem que você precise abrir o financeiro pra checar.

Na agenda — 3 estados

Pendente · badge $ amber (canto direito)
$
Ana Souza (14h-14:50h)
agendado presencial
Pago · barra verde (borda esquerda)
Joyce (15h-15:50h)
agendado presencial
Sem cobrança · neutro
Maria (16h-16:50h)
agendado presencial
  • Pendente: sessão com paciente, cobrança gerada mas não paga (ou nenhuma cobrança ainda).
  • Pago: record marcado como paid em /financeiro. Pra ver de relance quais sessões já estão quitadas.
  • Neutro: sem cobrança (compromisso pessoal, bloqueio) ou ocorrência virtual de pacote upfront/saldo — controlados pelo contrato.
  • Os 3 canais não competem: esquerda = pago, direita = pendente, nada = neutro.

No popover da sessão — linha "A receber"

14:00 – 14:50 · 50min
A receber R$ 200,00 (cobrança pendente)
Presencial
Agendado
  • Linha em destaque amber logo abaixo do horário, com ícone $.
  • Texto adapta-se ao caso:
    • "A cobrar R$ X" — quando ainda não há cobrança gerada (sessão "Não cobrar").
    • "A receber R$ X (cobrança pendente)" — quando o record existe mas não foi pago.
    • "Cobrança ainda não gerada" — quando a sessão não tem valor definido.
  • Some quando a sessão fica paga.

1 Bloqueio · sem paciente, sem cobrança

Almoço, feriado, férias ou compromisso pessoal. Aparece cinza no calendário e não gera lançamento no financeiro.

Como testar:
  1. Abra BloqueioDialog pelo menu da agenda, escolha modo Período e crie um bloqueio de 15/05 a 17/05 com motivo "Férias curtas".
  2. Confira que o intervalo aparece em cinza nas 3 agendas (Melissa / Rail / Clínica) e que nada foi gerado em /financeiro.
  3. Clique num horário dentro do bloqueio (ex: 16/05 às 10h). O AgendaEventDialog abre normalmente, com um aviso amarelo no topo avisando que aquele slot está bloqueado. Você pode prosseguir e criar o compromisso assim mesmo — o aviso é só pra evitar acidente.
1a · Criar o bloqueio

BloqueioDialog · "Período"

Modo
Período
15/05/2026 → 17/05/2026
Motivo
Férias curtas
Renderizado como faixa cinza nas 3 agendas (Melissa / Rail / Clínica). Não afeta o financeiro.

Financeiro · Lançamentos

Nada gerado. Bloqueio não é cobrável.
1b · Agendar sobre um bloqueio (aviso, não impede)

AgendaEventDialog · escolha do tipo

Este horário está dentro do bloqueio "Férias curtas". O compromisso pode ser criado mesmo assim.
Selecione o tipo de compromisso para começar.
Sessão
Reunião
Outro
O aviso aparece dentro do dialog (não como toast solto) e some sozinho ao reabrir o dialog em outro horário.

O que acontece

Você decide. Diferente do agendador público (que veta agendamento em bloqueio), aqui o sistema só te avisa. É comum querer encaixar uma sessão sobre um almoço, ou agendar uma reunião em pleno feriado.

  • Se prosseguir, o compromisso é criado normalmente e aparece por cima da faixa cinza do bloqueio.
  • Se quiser cancelar, basta fechar o dialog — nenhum dado é gravado.
  • O aviso usa o título do bloqueio (ex: "Férias curtas") pra você lembrar o motivo do bloqueio antes de decidir.
No agendador público (link de marcação que o paciente acessa) o comportamento é diferente: se o dia inteiro estiver bloqueado, sessões ficam vetadas e aparece a mensagem "Este dia está bloqueado. Compromissos do tipo Sessão não podem ser agendados." — só você (terapeuta) pode passar por cima.

2 Avulsa · sem cobrança chargeMode = none

Sessão única, terapeuta decide depois se cobra (modo manual / fechamento mensal).

Como testar: Criar sessão pra Ana Souza Ferreira em 15/05 14:00, modalidade presencial, tipo avulsa, deixar "Não cobrar agora" selecionado no card Cobrança ao salvar. Salvar. Verificar evento na agenda (status pendente) e zero record em /financeiro.
Indicadores visuais: Como a sessão ainda não foi cobrada, o evento na agenda mostra um círculo amber com cifrão ($) no canto superior direito. Ao clicar no evento, o popover mostra uma linha amber com o ícone $: "A cobrar R$ X,XX" (ou "Cobrança ainda não gerada" se a sessão não tiver valor). Assim você lembra de gerar a cobrança depois no /financeiro.

AgendaEventDialog · Ana Souza Ferreira

Início
15/05 14:00
Duração
50 min
Modalidade
Presencial
Tipo
Avulsa
Cobrança ao salvar

Você pode gerar a cobrança depois pelo /financeiro ou pelo painel da sessão.

Financeiro · Ana

Nenhum lançamento gerado. Sessão fica como pendente na agenda só.

3 Avulsa · cobrar ao salvar particular

Gera lançamento pendente no ato. Default da agenda em modo manual.

Como testar: Criar sessão pra Henrique, serviço Psicoterapia R$ 200, modalidade online. No card "Cobrança ao salvar", escolher "Gerar cobrança". Forma de pagamento: selecionar PIX. Status do pagamento: deixar em "Cobrança pendente" (default). Salvar. Verificar 1 record em /financeiro com status pendente, payment_method = pix e paid_at = null.
Indicadores visuais: A cobrança foi gerada mas o paciente ainda não pagou. O evento na agenda mostra o círculo amber com $ no canto. No popover, linha amber: "A receber R$ 200,00 (cobrança pendente)". O badge some automaticamente assim que o record for marcado como pago no /financeiro.

AgendaEventDialog · Henrique

Sessão
Psicoterapia · 50 min · R$ 200,00
Modalidade
Online
Cobrança ao salvar
Forma de pagamento
Status do pagamento

Só aparece quando a forma de pagamento não é o link Asaas (que é sempre liquidado pelo gateway).

Financeiro · Henrique

Clique em Salvar sessão

4 Avulsa · "já recebi" no momento da criação

Quando o paciente paga PIX/dinheiro na hora — record já nasce pago.

Como testar: Criar sessão pra Joyce, valor R$ 180. No card "Cobrança ao salvar", escolher "Gerar cobrança". Forma de pagamento: PIX. Status do pagamento: "Já recebi (dar baixa)". Salvar. Verificar 1 record em /financeiro com status pago, payment_method = pix e paid_at = hoje.
Indicadores visuais: O pagamento já foi confirmado, então o evento ganha uma barra verde fina na borda esquerda (substitui a cor padrão do commitment naquele lado). Sem badge $ no canto e o popover não mostra linha de cobrança. É o estado-alvo: dá pra olhar a agenda e identificar de relance quais sessões já foram pagas.

Joyce · Sessão R$ 180

Cobrança ao salvar
Forma de pagamento
Status do pagamento

Quando escolhe "Já recebi", o record nasce com paid_at = agora + status = paid.

Financeiro · Joyce

Clique em Salvar sessão

5 Avulsa · convênio

Valor pago pelo plano de saúde, não pelo paciente. Lançamento vinculado ao plano.

Como testar: Criar sessão pra Sándor com convênio Unimed Nacional (criar via InsurancePlanQuickCreateDialog se não existir), valor R$ 95. Salvar. Verificar record com insurance_plan_id preenchido e pill "convênio" visível.
Indicadores visuais: Como o convênio ainda não fechou o mês, o record fica pendente — o evento na agenda mostra badge $ e o popover mostra "A receber R$ 95,00 (cobrança pendente)". Quando você marcar o record do convênio como pago após o fechamento mensal, os indicadores somem.

Sándor · sessão por convênio

Convênio
Unimed Nacional
Valor (clínica recebe)
R$ 95,00

Cobrança vai pra fechamento mensal do convênio — não cobra o paciente.

Financeiro · Sándor

Sessão 15/05 14:00
via convênio Unimed
convênio
R$ 95,00
Vai pra fatura mensal do convênio (não cobra paciente individualmente).

6 Recorrente · sem pacote chargeMode = none

Série semanal, mas cobrança decidida sessão a sessão (status driven ou manual depois).

Como testar: Criar série recorrente pra Anna Freud, frequência semanal, 4 ocorrências, modo de cobrança "Não cobrar". Salvar. Verificar no banco: 1 row em agenda_eventos + 1 row em recurrence_rules; outras 3 ocorrências expandidas em runtime. Zero em /financeiro.
Indicadores visuais: Apenas a 1ª ocorrência (materializada de verdade) mostra o badge $ e a linha "A cobrar". As outras 3 (virtuais, expandidas em runtime) ficam limpas — vão ganhar o badge automaticamente quando virarem ocorrências reais (ao marcar status ou editar). Isso evita poluir o calendário com $ em séries longas.

Anna Freud · semanal · 4 sessões

Frequência
Semanal · qua 14:00
Ocorrências
4 (1 real + 3 virtuais)
Modo de cobrança
Modelo "1 real + N-1 virtual"

Só a 1ª ocorrência vira row em agenda_eventos. As demais são rec::ruleId::date expandidas em runtime.

Financeiro · Anna Freud

Zero lançamentos. Cobra sessão por sessão quando marcar como Realizada.

7 Recorrente · pacote único UPFRONT

1 cobrança do valor total no salvar. Cada sessão posterior é "consumo" do já pago.

Como testar: Criar série pra Donald Winnicott, 4 sessões semanais R$ 200 = R$ 800 total, modo "Pacote único", estilo "Upfront", forma "Enviar link Asaas". Salvar. Verificar 1 record R$ 800 pending em /financeiro + 1 billing_contract com charging_style=upfront, sessions_used=0.
Indicadores visuais: A 1ª ocorrência (que carrega o record de R$ 800 do pacote) mostra o badge $ enquanto o boleto Asaas não for pago — popover diz "A receber R$ 800,00 (cobrança pendente)". As outras 3 ocorrências (virtuais) ficam limpas, mesmo que o paciente não tenha pago ainda — porque o controle é a nível de pacote, não sessão. Quando o boleto for marcado como pago, o badge some da 1ª sessão.

Donald Winnicott · 4× R$ 200 = R$ 800

Ocorrências
4 semanais
Total
R$ 800,00
Como cobrar?
Estilo do pacote
Forma de pagamento

Financeiro · Donald

Clique em Salvar série

8 Recorrente · pacote único SALDO

Sem record financeiro inicial. Cria billing_contract com sessions_total. Records nascem 1 a 1 quando sessão é realizada.

Como testar: Criar série pra Carl Jung, 4 sessões R$ 40 = R$ 160 total, modo "Pacote único", estilo "Saldo". Salvar. Verificar ZERO record em /financeiro + 1 billing_contract com charging_style=saldo, sessions_total=4, sessions_used=0.
Indicadores visuais: Como não existe record financeiro inicial e o controle é por saldo do contrato, todas as 4 ocorrências ficam limpas no calendário (sem badge $). À medida que cada sessão for realizada, nasce 1 record por vez — e essa sessão recebe o badge $ até o record ser pago. Use "Antecipar pagamento" no popover quando o paciente quiser pagar uma sessão antes dela acontecer.

Carl Jung · 4× R$ 40 = R$ 160

Sessions total
4
Valor por sessão
R$ 40,00
Estilo do pacote

Cobra 1 record por sessão realizada. Pacote dá direito ao preço promocional + controle de saldo.

Financeiro · Carl Jung

Clique em Salvar série

9 Recorrente · 1 cobrança por sessão sem pacote

Cobra individualmente cada ocorrência. Diferença pro Cenário 6: aqui já materializa records pendentes pra cada ocorrência futura.

Como testar: Criar série pra Michael Balint, 12 sessões semanais R$ 150, modo "1 por sessão". Salvar. Verificar 12 records pending em /financeiro (1 por ocorrência) com agenda_evento_id apontando pra cada uma.
Indicadores visuais: Como cada ocorrência foi materializada com seu próprio record pendente, todas as 12 sessões mostram o badge $ no calendário e a linha "A receber R$ 150,00 (cobrança pendente)" no popover. À medida que o paciente pagar cada uma, o badge dessa sessão some.

Michael Balint · semanal

Frequência
Semanal · 12 sessões
Valor / sessão
R$ 150,00
Modo

Financeiro · Michael

Clique em Salvar série

10 Status change · avulsa pendente

Sessão criada com chargeMode=session, agora chegou a hora. Dialog confirm aparece em realizado/faltou/cancelado.

Como testar: Usar a sessão pendente do Henrique (cenário 3) ou criar uma nova pra Maria Magali R$ 200 cobrar ao salvar. (a) Mudar status → Realizada: dialog deve aparecer, confirmar, record continua pendente. (b) Reset (volta pra pendente). Mudar → Faltou: dialog aparece com multa editável R$ 100, confirmar, record original cancelado + novo de multa criado. (c) Reset. Mudar → Cancelado: dialog confirma cancelamento sem multa, record fica cancelado.

Maria Magali · 15/05 16:00 · pendente

Status atual
pendente
Valor sessão
R$ 200,00
Mudar status para:

Cada um abre o AgendaStatusChangeConfirmDialog com variante diferente.

Financeiro · Maria

Sessão 15/05 16:00
avulsa · particular
pendente
R$ 200,00

11 Status change · ocorrência de pacote saldo

Carl Jung tem pacote 4/4. Marca cada sessão como realizada → cria record + decrementa saldo. Faltou → vai pra config default_consume_on_miss.

Como testar: Usando o pacote do Carl Jung (cenário 8): (a) Marcar 1ª sessão como Realizada: dialog confirm → criar record R$ 40 pending + saldo cai pra 3/4. (b) Marcar 2ª como Faltou: dialog com 3 opções (consumir saldo / aplicar multa / nada). Testar a opção consumir saldo → saldo cai pra 2/4 sem cobrança nova. (c) Marcar 3ª como Faltou com aplicar multa R$ 20 → saldo continua 2/4, novo record R$ 20.

Carl Jung · pacote saldo

Saldo: 4 / 4
R$ 40/sessão
Sessão de hoje (15/05 18:00) — virtual rec::...::2026-05-15
pendente
Marcar como:
O que acontece no banco?
  • Materializa a ocorrência virtual: INSERT em agenda_eventos com recurrence_id + recurrence_date
  • UPDATE billing_contracts.sessions_used += 1
  • INSERT em financial_records R$ 40 status=pending (ou paid se já houver record do antecipar)

Financeiro · Carl Jung

Pacote 4× R$ 40
billing_contract · saldo 4/4
contrato
R$ 160,00
Ainda sem records de sessão. Cada Realizada cria 1 record + decrementa saldo.

12 Antecipar pagamento · pacote saldo novo · 14/05

Paciente pediu pra pagar a próxima sessão antes dela acontecer. Cria record pago, MAS não decrementa saldo (só quando marcar realizada depois).

Como testar: No pacote do Carl Jung, clicar numa ocorrência virtual futura → popover abre → seção Financeiro mostra 2 botões: "Lançamentos" + "Antecipar pagamento". Clicar "Antecipar pagamento", escolher PIX (default), confirmar. (a) Verificar 1 novo record R$ 40 paid em /financeiro, MAS saldo do contrato continua intacto. (b) Depois marcar a mesma sessão como Realizada: dialog NÃO deve perguntar "gerar cobrança" (detecta record paid), só atualiza status + decrementa saldo.

Popover da ocorrência virtual

Seção Financeiro do popover:

Diferença pra Realizada: não decrementa sessions_used. Quando user marcar Realizada depois, handler detecta record já paid → só atualiza status.

Financeiro · Carl Jung

Pacote 4× R$ 40
billing_contract · saldo 4/4
contrato
R$ 160,00

13 Edit de sessão já cobrada imutável

Invariante "cobrança emitida é imutável pelo dialog da agenda" (Decisão #5, padrão SimplePractice).

Como testar: Pegar a sessão paga da Joyce (cenário 4) e abrir o AgendaEventDialog. Verificar: (a) aviso amarelo no topo "esta sessão tem cobrança paga"; (b) campos de valor / modalidade / tipo travados (disabled); (c) apenas horário e observações editáveis; (d) tentar mudar valor não persiste.

AgendaEventDialog · Sessão paga

Esta sessão tem cobrança paga. Campos de valor / modalidade / tipo estão travados. Pra ajustar valor, use credit note no /financeiro.
Valor
R$ 200,00
Modalidade
Presencial

Apenas horário / observações podem ser editados.

Financeiro · paciente

Sessão 10/05 14:00
avulsa · particular · paid_at 10/05
pago
R$ 200,00
Record imutável. Reembolso parcial → credit note. Cancelamento → record novo negativo.