From 45984e885b49251ec1ba56b3d5de87979405aa26 Mon Sep 17 00:00:00 2001 From: Leonardo Date: Wed, 20 May 2026 12:09:45 -0300 Subject: [PATCH] agenda: label + badge cientes de pacote em sessoes state=none MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug descoberto durante teste C11: sessao 27/05 do Andre Green (materializada via Falta+reverse, pertence ao pacote saldo) mostrava "A cobrar R$ 40,00" no popover mesmo sem fatura ativa. Implicava que dava pra gerar cobranca avulsa solta — conflito com o flow do pacote (Usar consume saldo). Fix em paymentLabel: quando state='none' e ev.contract existe, label muda conforme estilo: - saldo: "Aguardando uso do pacote" - upfront: "Coberta pelo pacote (upfront)" Avulsa sem pacote continua mostrando "A cobrar R$ X". Simetria em MelissaAgenda.vue badge gate: nao mostra badge $ amber em sessao state=none com pacote amarrado (hasPacoteTied). Sem isso, sessao agendada de pacote saldo no calendar ficava com badge "cobranca pendente" enganoso. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/layout/melissa/MelissaAgenda.vue | 6 +++++- src/layout/melissa/MelissaEventoPanel.vue | 6 ++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/layout/melissa/MelissaAgenda.vue b/src/layout/melissa/MelissaAgenda.vue index cbfc128..ed61398 100644 --- a/src/layout/melissa/MelissaAgenda.vue +++ b/src/layout/melissa/MelissaAgenda.vue @@ -659,11 +659,15 @@ const fcOptions = computed(() => ({ // state='none' (records cancelled filtrados) — sessão não rolou, // cobrança nova não cabe. Multa pendente vem com state='pending' // e aí entra pelo ramo anterior, ok. + // Sessão com pacote ativo (saldo OU upfront) com state='none' também + // NÃO ganha badge — billing é via pacote, não cobrança avulsa solta. + // Pra saldo: aguarda "Usar"; pra upfront: já coberta pelo contrato. const statusLower = String(ext.status || '').toLowerCase(); const sessaoEncerrada = statusLower === 'cancelado' || statusLower === 'cancelada' || statusLower === 'faltou'; + const hasPacoteTied = !!ext.contract; const wantBadge = isSessao && ext.patient_id && ext.paymentState !== 'paid' && !sessaoEncerrada && ( ext.paymentState === 'pending' || ext.paymentState === 'overdue' || - (!ext.is_occurrence && (ext.paymentState === 'none' || !ext.paymentState)) + (!ext.is_occurrence && (ext.paymentState === 'none' || !ext.paymentState) && !hasPacoteTied) ); if (wantBadge) { payBadgeHtml = ``; diff --git a/src/layout/melissa/MelissaEventoPanel.vue b/src/layout/melissa/MelissaEventoPanel.vue index 361d211..0d50c58 100644 --- a/src/layout/melissa/MelissaEventoPanel.vue +++ b/src/layout/melissa/MelissaEventoPanel.vue @@ -190,6 +190,12 @@ const paymentLabel = computed(() => { const slug = String(ev.value.status || '').toLowerCase(); if (slug === 'cancelado' || slug === 'cancelada') return 'Sessão cancelada · sem cobrança ativa'; if (slug === 'faltou') return 'Sessão não realizada · sem cobrança ativa'; + // Pacote tied → não cabe "A cobrar R$ X" solto porque o billing é via + // pacote (saldo: aguarda Usar; upfront: já está coberto pelo contrato). + // Contract row já mostra o status do pacote. + const cInfo = ev.value.contract; + if (cInfo?.style === 'saldo') return 'Aguardando uso do pacote'; + if (cInfo?.style === 'upfront') return 'Coberta pelo pacote (upfront)'; return valorFmt ? `A cobrar ${valorFmt}` : 'Cobrança ainda não gerada'; });