agenda: C10 pos-test fixes + lock sessao encerrada + addendum doc
Bugs descobertos durante testes C10/A2/B/C com user:
1) _reloadRange not defined: _buildHandlers nao destruturava
_reloadRange do deps (passava mas nao desempacotava). Toast
ReferenceError ao tentar reload pos-status change. Fix em
useMelissaAgenda.js:_buildHandlers.
2) Badge $ amber em sessao cancelada: MelissaAgenda.vue badge gate
ignorava status. Cancelado+state=none (records cancelled
filtrados) ainda recebia badge "cobranca pendente". Fix: gate
sessaoEncerrada (cancelado/faltou) -> sem badge nunca.
3) Botao "Gerar cobranca" em sessao encerrada: AgendaEventoFinanceiro
Panel mostrava botao mesmo em cancelado/faltou -> user podia
emitir fatura nova em sessao que nao aconteceu. Fix: v-if
!isSessaoEncerrada + label muda pra "Sessao cancelada · sem
cobranca ativa".
4) paymentLabel usava ev.price em vez de paymentAmount pra state
'pending': caso multa R$ 30 mostrava R$ 150 (ev.price original).
Fix: usar paymentAmount tambem em pending.
5) Lock total em sessao encerrada (cancelado/faltou):
- "Editar sessao" SOME do popover
- Realizada/Falta/Reagendar/Cancelar disabled com tooltip
- Apenas "Agendada" continua funcional (caminho explicito de
recuperacao). Single path de saida do estado encerrado.
Adicoes UX em AgendaStatusChangeConfirmDialog:
- Hint contextual sobre min_hours_notice explicando POR QUE multa
veio (des)marcada por padrao: "Cancelou 18.5h antes da sessao.
Regra: multa apenas quando cancelamento <2h -> sem multa por
padrao." Terapeuta ve a razao e pode inverter conscientemente.
Adicoes UX em MelissaEventoPanel:
- Botao "Agendada" (variante --info azul cyan) no grupo status
pra reset/recuperacao. CSS .evento-act--info hover + is-current.
Doc:
- Addendum C10 no topo de src/docs/agenda-compromisso-financeiro
-cenarios.html capturando todas as divergencias/melhorias vs
mockup original + 3 pendencias pos-C13 (reverse transitions,
popover snapshot, A2 markPaid stale).
Pendencias salvas em memoria pra puxar pos-C13.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -135,6 +135,13 @@ const isSessaoComPaciente = computed(
|
||||
() => ev.value.tipo === 'sessao' && (ev.value.patient_id || ev.value.pacienteNome)
|
||||
);
|
||||
|
||||
// Sessão "encerrada" — não rolou (cancelada ou paciente faltou).
|
||||
// Bloqueia: editar sessão (dados não cabem mais) + transições de status
|
||||
// pra realizado/faltou/cancelar (não faz sentido marcar um cancelado como
|
||||
// "faltou"). Mantém SÓ "Agendada" funcional como caminho de recuperação
|
||||
// caso tenha sido marcado por engano.
|
||||
const isSessaoEncerrada = computed(() => statusSlug.value === 'cancelado' || statusSlug.value === 'faltou');
|
||||
|
||||
// Estado de pagamento — vem anotado pelo useMelissaAgenda via bulk-query
|
||||
// em financial_records. 'paid' | 'pending' | 'none'. Renderiza linha
|
||||
// curta abaixo do horário pra sessão com paciente (espelha os 3 canais
|
||||
@@ -177,7 +184,12 @@ const paymentLabel = computed(() => {
|
||||
if (state === 'pending') {
|
||||
return valorFmt ? `A receber ${valorFmt} (cobrança pendente)` : 'Cobrança pendente';
|
||||
}
|
||||
// 'none' — sessão sem cobrança gerada ainda
|
||||
// 'none' — sessão sem cobrança ativa
|
||||
// Quando status='cancelado'/'faltou' + sem record ativo, deixa claro
|
||||
// que não há cobrança em aberto (em vez de "A cobrar R$ X" enganoso).
|
||||
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';
|
||||
return valorFmt ? `A cobrar ${valorFmt}` : 'Cobrança ainda não gerada';
|
||||
});
|
||||
|
||||
@@ -238,6 +250,7 @@ function modalidadeIcon(mod) {
|
||||
</span>
|
||||
<div class="evento-row__edit-stack">
|
||||
<button
|
||||
v-if="!isSessaoEncerrada"
|
||||
type="button"
|
||||
class="evento-row__edit evento-row__edit--primary"
|
||||
:disabled="busy"
|
||||
@@ -285,7 +298,7 @@ function modalidadeIcon(mod) {
|
||||
sessions_used) — gerar fatura solta aqui criaria
|
||||
cobrança duplicada e dessincronizaria o saldo. -->
|
||||
<button
|
||||
v-if="paymentVariant === 'none' && !ev.is_occurrence && !contractInfo"
|
||||
v-if="paymentVariant === 'none' && !ev.is_occurrence && !contractInfo && statusSlug !== 'cancelado' && statusSlug !== 'faltou'"
|
||||
type="button"
|
||||
class="evento-row__pay-action"
|
||||
:disabled="busy"
|
||||
@@ -372,7 +385,8 @@ function modalidadeIcon(mod) {
|
||||
<button
|
||||
class="evento-act evento-act--ok"
|
||||
:class="{ 'is-current': statusSlug === 'realizado' }"
|
||||
:disabled="busy"
|
||||
:disabled="busy || isSessaoEncerrada"
|
||||
v-tooltip.top="isSessaoEncerrada ? 'Sessão encerrada — use Agendada pra reativar antes' : null"
|
||||
@click="emit('concluir')"
|
||||
>
|
||||
<i class="pi pi-check-circle" />
|
||||
@@ -381,7 +395,8 @@ function modalidadeIcon(mod) {
|
||||
<button
|
||||
class="evento-act evento-act--warn"
|
||||
:class="{ 'is-current': statusSlug === 'faltou' }"
|
||||
:disabled="busy"
|
||||
:disabled="busy || isSessaoEncerrada"
|
||||
v-tooltip.top="isSessaoEncerrada ? 'Sessão encerrada — use Agendada pra reativar antes' : null"
|
||||
@click="emit('faltou')"
|
||||
>
|
||||
<i class="pi pi-user-minus" />
|
||||
@@ -390,7 +405,8 @@ function modalidadeIcon(mod) {
|
||||
<button
|
||||
class="evento-act"
|
||||
:class="{ 'is-current': statusSlug === 'remarcar' || statusSlug === 'remarcado' }"
|
||||
:disabled="busy"
|
||||
:disabled="busy || isSessaoEncerrada"
|
||||
v-tooltip.top="isSessaoEncerrada ? 'Sessão encerrada — use Agendada pra reativar antes' : null"
|
||||
@click="emit('remarcar')"
|
||||
>
|
||||
<i class="pi pi-calendar-clock" />
|
||||
@@ -399,7 +415,8 @@ function modalidadeIcon(mod) {
|
||||
<button
|
||||
class="evento-act evento-act--danger"
|
||||
:class="{ 'is-current': statusSlug === 'cancelado' }"
|
||||
:disabled="busy"
|
||||
:disabled="busy || isSessaoEncerrada"
|
||||
v-tooltip.top="isSessaoEncerrada ? 'Sessão encerrada — use Agendada pra reativar antes' : null"
|
||||
@click="emit('cancelar')"
|
||||
>
|
||||
<i class="pi pi-ban" />
|
||||
|
||||
Reference in New Issue
Block a user