From b5e00a7022bd714376fd9b9cd0bd7b9ad0b1866b Mon Sep 17 00:00:00 2001 From: Leonardo Date: Wed, 20 May 2026 15:07:11 -0300 Subject: [PATCH] agenda: popover sincroniza com eventos + antecipar nao reusa cancelled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dois bugs descobertos no ciclo C12 antecipar -> revogar -> reantecipar: 1) confirmAnteciparPagamento reusava record cancelled: Quando user revogava (vira cancelled) e antecipava de novo, o existRec query pegava o cancelled e UPDATE-ava pra paid no MESMO record id. Resultado: notes mantinham historico "Cancelada via reversao" + "Antecipacao revogada" + record reativo como paid, confuso pra audit trail. Fix: filtrar .neq('status', 'cancelled') na busca de existRec — agora a re-antecipacao via RPC cria record fresh. 2) Popover snapshot stale (pendencia documentada em project_melissa_popover_snapshot, antecipada pra agora): eventoSelecionado.value era snapshot do clique e nao acompanhava updates do _paymentStateMap pos M.refetch. User antecipava, o record paid era criado, mas o popover continuava com paymentState antigo -> botao continuava "Antecipar pagamento" em vez de alternar pra "Revogar pagamento". Fix: watch em M.eventos sincroniza eventoSelecionado com a versao fresh quando id bate. flush:'post' pra rodar apos o computed reagir. Como o popover agora atualiza in-place, removido fecharEvento() de confirmAnteciparPagamento e onRevogarAntecipacao — o user pode ver o botao alternar live em vez de precisar reabrir o popover. Cleanup do estado do Andre: deletado record orfa 3a4c79e0 pra reset do teste C12. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/layout/melissa/MelissaLayout.vue | 32 +++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/src/layout/melissa/MelissaLayout.vue b/src/layout/melissa/MelissaLayout.vue index 029d610..d6e929c 100644 --- a/src/layout/melissa/MelissaLayout.vue +++ b/src/layout/melissa/MelissaLayout.vue @@ -665,6 +665,21 @@ function fecharEvento() { eventoBusy.value = false; } +// Mantém eventoSelecionado em sincronia com a lista reativa M.eventos. +// Sem isso, eventoSelecionado.value é snapshot do clique e não acompanha +// updates do _paymentStateMap pós refetch (caso típico: revogar/antecipar +// pagamento — record muda mas popover continuava mostrando estado antigo). +// Watch dispara apenas quando o ev correspondente aparece novo no eventos +// computed (paymentState atualizado pelo bulk-load). +watch(() => M.eventos.value, (novos) => { + const sel = eventoSelecionado.value; + if (!sel?.id) return; + const fresh = novos.find((e) => e.id === sel.id); + if (fresh && fresh !== sel) { + eventoSelecionado.value = fresh; + } +}, { flush: 'post' }); + // ── Actions do MelissaEventoPanel ────────────────────────────── // Fase 5 (2026-05-14): TODOS os status (realizado/faltou/cancelado/etc) // passam por M.onUpdateSeriesEvent — que abre o AgendaStatusChangeConfirmDialog @@ -811,12 +826,17 @@ async function confirmAnteciparPagamento() { } } - // 2) Verifica se já tem financial_record vinculado + // 2) Verifica se já tem financial_record vinculado. + // IMPORTANTE: filtra cancelled — caso típico após revogar a antecipação + // (record vira cancelled) e user re-antecipa. Sem o filtro, o handler + // reusava o record cancelled atualizando pra paid, mantendo notes + // da revogação no audit trail (confuso). const { data: existRec } = await supabase .from('financial_records') .select('id, status') .eq('agenda_evento_id', eventoId) .is('deleted_at', null) + .neq('status', 'cancelled') .order('created_at', { ascending: false }) .limit(1) .maybeSingle(); @@ -869,9 +889,10 @@ async function confirmAnteciparPagamento() { life: 4000 }); anteciparDialogOpen.value = false; - M.refetch(); + await M.refetch(); refetchEventosHoje(); - fecharEvento(); + // Não fecha o popover — watch em eventos sincroniza o ev pro novo + // estado (paymentState='paid' agora). Botão alterna pra "Revogar". } catch (e) { toast.add({ severity: 'error', summary: 'Erro', detail: e?.message || 'Falha ao antecipar pagamento.', life: 5000 }); } finally { @@ -943,9 +964,10 @@ async function onRevogarAntecipacao() { detail: `Cobrança de R$ ${Number(paidRec.final_amount || paidRec.amount || 0).toFixed(2).replace('.', ',')} cancelada.`, life: 4000 }); - M.refetch(); + await M.refetch(); refetchEventosHoje(); - fecharEvento(); + // Não fecha popover — watch em eventos sincroniza paymentState='none'. + // Botão alterna de volta pra "Antecipar pagamento". } catch (e) { toast.add({ severity: 'error', summary: 'Erro', detail: e?.message || 'Falha ao revogar antecipação.', life: 5000 }); } finally {