agenda: C7 OK + Fase 6 lock-edit ativada em Melissa + cross-week payment propagation
Cenário 7 (Pacote UPFRONT — Ana Souza Ferreira 4×R$ 200 = R$ 800)
- Testado e passou. User criou Ana, pagou os R$ 800 em dinheiro pelo
Financeiro. Borda verde + popover "Pago R$ 800" funcionando.
Fase 6 (lock-edit cobrada) ativada em Melissa
- Removido guard `if (!props.occurrenceMode) return;` em
loadOccFinancialRecord (useAgendaEventLifecycle.js:217+). Agora ele
carrega em ambos modos (Rail/Clínica E Melissa)
- loadOccFinancialRecord SINTETIZA record paid/pending pra siblings de
contrato upfront ativo — assim TODAS as ocorrências da série mostram
"Cobrança paga R$ 800 do pacote" no AgendaEventDialog
- AgendaEventDialog card Sessão/Honorários (flow Melissa) ganhou lock
template: Tag em vez de Select billingType quando occFinancialRecord
existe; Message com cadeado "Cobrança de R$ X já emitida"
- AgendaEventoFinanceiroPanel só renderiza dentro do lock quando record
é REAL (não sintetizado) — evita "Gerar cobrança" indevido em sibling
- paymentSummary do Resumo lateral unificado pra usar occFinancialRecord
(em vez do sessionPaymentRecord paralelo de antes)
Cross-week propagation de pacote upfront
- BUG: ao navegar pra semana só com virtuais (sem reais), bulk-load
caía no else `_rulePaymentMap.value = {}` — virtuais perdiam estado
paid herdado
- FIX em useMelissaAgenda._reloadRange:
* Maps (payment/amount/rule) inicializados SEMPRE no início
* Propagação roda independente de realIds.length (depende só de
ruleIdsInView.size>0, considera reais E virtuais com recurrence_id)
* Query cross-week: pra cada rule em view, busca QUALQUER evento
sibling em qualquer semana + seus records pra determinar estado do
contrato. Encontra o record do pacote mesmo em outra semana
- Saldo NÃO propaga (filter: charging_style='upfront' || NULL); cada
sessão de saldo gera cobrança individual ao realizar
- Memória durável: memory/project_cross_week_propagation.md
Visualização de virtuais cobertas
- MelissaEventoPanel.showPaymentRow: virtuais só escondem quando state
='none'. Com paid/pending herdado, exibem linha colorida
- MelissaAgenda fcEvents: isPaidSession e badge $ pendente removeram
exigência de !is_occurrence. Virtuais herdadas via propagação mostram
borda verde / badge amber
Atalho "Gerar fatura" no popover
- Pill amber pequeno ao lado de "A cobrar R$ X" quando paymentVariant
='none' && !is_occurrence. Click → gerarCobrancaManual direto, fecha
popover pra impedir double-click. Tooltip: "Gerar fatura agora"
- Wire em MelissaLayout via novo emit gerar-cobranca + handler
onGerarCobrancaQuick
Info de pacote no popover
- Header agora mostra "Sessão · Pacote · N sessões" (computed
seriesLabel lê de _raw do rule)
Botão "Excluir série inteira"
- Novo emit delete-series em MelissaEventoPanel + botão ao lado de
"Excluir sessão" quando evento tem recurrence_id
- Handler onDeleteSeries em MelissaLayout: hard delete em 3 etapas
(financial_records pendentes → agenda_eventos materializados →
recurrence_rules CASCADE leva exceptions). Bloqueia se algum record
paid (estorno via Financeiro primeiro)
cancel_session some da agenda
- useRecurrence.expandRules agora pula occurrence com exception.type=
'cancel_session' (era visível com status cancelado; doc dizia que
some). patient_missed/therapist_canceled/holiday_block permanecem
como histórico
recurrence_exceptions cancel idempotente
- MelissaLayout onDeleteEvento usa upsert com onConflict pra exception
cancel — não quebra mais com unique violation em re-cancel
billing_contract_id na 1ª materializada
- _createPackageContract agora .select() o contrato após insert e seta
billing_contract_id no insert da 1ª agenda_eventos materializada
onVerLancamentos cobre virtual de upfront
- Antes virtual sempre toast "Sem lançamentos". Agora busca records via
siblings da série pra encontrar o do pacote. Saldo/sem pacote continua
com toast
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -395,13 +395,13 @@ const fcEvents = computed(() => {
|
||||
const pStatus = ev.paciente_status;
|
||||
const isInactivePatient = pStatus === 'Arquivado' || pStatus === 'Inativo';
|
||||
// Sessão paga → barra esquerda verde (override do border-left que o
|
||||
// FC pinta com a cor do commitment). Espelha as mesmas condições do
|
||||
// badge $ amber: sessão + paciente + não-virtual; aqui inverte pra
|
||||
// paymentState === 'paid'.
|
||||
// FC pinta com a cor do commitment). Aplica a virtuais TAMBÉM quando
|
||||
// herdam 'paid' de pacote upfront pago via propagação no bulk-load
|
||||
// (sem essa propagação, virtuais ficam paymentState='none' e o
|
||||
// check abaixo já as exclui).
|
||||
const isPaidSession =
|
||||
String(ev.tipo || '').toLowerCase() === 'sessao' &&
|
||||
(ev.patient_id || ev.paciente_id) &&
|
||||
!ev.is_occurrence &&
|
||||
ev.paymentState === 'paid';
|
||||
const cls = [];
|
||||
if (isInactivePatient) cls.push('ma-evt--inactive-patient');
|
||||
@@ -650,7 +650,16 @@ const fcOptions = computed(() => ({
|
||||
// não poluir séries recorrentes com pacote upfront/saldo (cobertas
|
||||
// pelo contrato, não por record-por-sessão).
|
||||
let payBadgeHtml = '';
|
||||
if (isSessao && ext.patient_id && !ext.is_occurrence && ext.paymentState !== 'paid') {
|
||||
// Badge $ amber pendente: aparece pra sessão com paciente quando há
|
||||
// cobrança pendente (paymentState='pending'/'overdue') OU quando é
|
||||
// REAL sem cobrança ainda ('none'). Virtuais com 'none' (saldo,
|
||||
// sem pacote, ou virtuais limpas) ficam SEM badge — só virtuais
|
||||
// herdando 'pending' de pacote upfront mostram o badge.
|
||||
const wantBadge = isSessao && ext.patient_id && ext.paymentState !== 'paid' && (
|
||||
ext.paymentState === 'pending' || ext.paymentState === 'overdue' ||
|
||||
(!ext.is_occurrence && (ext.paymentState === 'none' || !ext.paymentState))
|
||||
);
|
||||
if (wantBadge) {
|
||||
payBadgeHtml = `<span class="mc-fc-event__paybadge" title="Cobrança pendente"><i class="pi pi-dollar"></i></span>`;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user