agenda: expandir e materializar ocorrencias de recorrencia (cross-layout)
PROBLEMA 1 — recorrencias virtuais nao apareciam em listas de sessao
============================================================
Sistema cria 1 row real em agenda_eventos por recorrencia (a primeira
ocorrencia) + 1 regra em recurrence_rules. As N-1 sessoes seguintes sao
geradas em runtime via useRecurrence.loadAndExpand. AgendaTerapeutaPage e
AgendaClinicaPage ja usavam loadAndExpand, mas composables compartilhados
("Hoje", widget, prontuario, ver todas) so liam agenda_eventos direto —
serie semanal de 4 sessoes aparecia como 1.
Fix em 3 composables cross-layout:
- usePatientSessions.load — range padrao -6mo a +12mo, filtra virtuais
por patient_id apos loadAndExpand. Cobre MelissaPaciente Tab Agenda +
PatientProntuario legacy.
- useMelissaEventos._fetchRange — merge real + virtual no range visivel.
Cobre widget "Hoje" (MelissaLayout), mini-cal, fallback standalone do
MelissaAgenda. Falha do expand cai silencioso pra so-reais.
- useMelissaTodasSessoesPaciente.fetch — mesma logica do paciente, range
-6mo a +12mo. Cobre "Ver todas as sessoes" do MelissaAgenda.
normalizeEvent agora aceita shape de virtual (paciente_nome/patient_name)
alem de joined query (patients.nome_completo). Expoe is_occurrence +
recurrence_id pra consumidores diferenciarem.
PROBLEMA 2 — UPDATE em id virtual quebra com "invalid input syntax for type uuid"
============================================================
Apos #1, ocorrencias virtuais aparecem na UI. Quando o user mudava status
(via botoes do MelissaEventoPanel, watcher do form.status no
AgendaEventDialog, ou botoes diretos no MelissaPaciente Tab Agenda), o
UPDATE caia direto no PostgreSQL com id "rec::ruleId::date" — sintaxe
invalida pra coluna UUID.
Materializacao em 4 caminhos:
- usePatientSessions.updateStatus(sessionOrId, status) — aceita row inteira
agora. Se virtual, busca row real por recurrence_id+date, ou cria nova
copiando campos da virtual (com status aplicado).
- useAgendaEventActions watcher do form.status — emit('updateSeriesEvent',
{ ..., row: form }) em vez de UPDATE direto. Parent materializa.
- MelissaLayout.updateEventoStatus — detecta virtual, delega pro
M.onUpdateSeriesEvent passando row: ev (sem isso, dialogEventRow ficaria
vazio porque user nao abriu o dialog antes — criava row orfa sem
patient_id).
- MelissaPaciente — @updateSeriesEvent do dialog local aponta pro
onSessaoDialogUpdateSeries (wrapper que delega pro composable que sabe
materializar). Antes apontava pro save normal.
useMelissaAgenda.onUpdateSeriesEvent atualizado:
- aceita row opcional do chamador (prioridade > dialogEventRow > vazio).
- guard: aborta com toast se rid (recurrence_id) for null, em vez de
criar row orfa.
- error check no .maybeSingle (antes ignorado — query falhando seguia pro
insert e duplicava sessoes).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -321,8 +321,11 @@ async function revertRecordPaid(record) {
|
||||
}
|
||||
|
||||
// Handler de mutacao de status (Realizada / Falta / Cancelar)
|
||||
// Passa a row inteira pro composable porque pode ser ocorrência virtual de
|
||||
// recorrência (id `rec::ruleId::date`) — nesse caso o composable materializa
|
||||
// uma linha real antes de aplicar o status (UPDATE em id virtual quebra).
|
||||
async function updateSessionStatus(ev, novoStatus, msg) {
|
||||
const result = await sessionsHook.updateStatus(ev.id, novoStatus);
|
||||
const result = await sessionsHook.updateStatus(ev, novoStatus);
|
||||
if (result.ok) {
|
||||
toast.add({ severity: 'success', summary: msg, life: 2200 });
|
||||
} else {
|
||||
@@ -539,6 +542,21 @@ async function onSessaoDialogSave(payload) {
|
||||
]);
|
||||
}
|
||||
}
|
||||
// Mudança de status numa ocorrência (cancelado/remarcado/etc) — delega pro
|
||||
// handler do composable que SABE materializar ocorrência virtual antes de
|
||||
// aplicar o status. Sem isso o UPDATE em id virtual quebra ("invalid input
|
||||
// syntax for type uuid"). Espelha o wire-up de MelissaLayout/AgendaTerapeuta.
|
||||
async function onSessaoDialogUpdateSeries(payload) {
|
||||
if (typeof melissaAgenda?.onUpdateSeriesEvent === 'function') {
|
||||
await melissaAgenda.onUpdateSeriesEvent(payload);
|
||||
}
|
||||
if (props.patientId) {
|
||||
await Promise.all([
|
||||
sessionsHook.load(props.patientId),
|
||||
recorrenciasHook.load(props.patientId)
|
||||
]);
|
||||
}
|
||||
}
|
||||
async function onSessaoDialogDelete(payload) {
|
||||
if (typeof melissaAgenda?.onDialogDelete === 'function') {
|
||||
await melissaAgenda.onDialogDelete(payload);
|
||||
@@ -2296,7 +2314,7 @@ onBeforeUnmount(() => {
|
||||
:lock-patient="true"
|
||||
@save="onSessaoDialogSave"
|
||||
@delete="onSessaoDialogDelete"
|
||||
@updateSeriesEvent="onSessaoDialogSave"
|
||||
@updateSeriesEvent="onSessaoDialogUpdateSeries"
|
||||
@editSeriesOccurrence="onSessaoDialogSave"
|
||||
>
|
||||
<template #headerLeft>
|
||||
|
||||
Reference in New Issue
Block a user