Recordatorios de cita (cron diario)
Objetivo
Avisar al dueño el día antes de una cita, sin backend involucrado del lado clínica — es 100% autónomo del dominio appointments.
Cómo empieza
SendAppointmentRemindersTask.run(), @Cron(CronExpression.EVERY_DAY_AT_6PM) — sin trigger manual.
Pasos principales
- Si
env.ENABLE_CRONes falso, el método retorna sin hacer nada — ni loguea. appointments.findScheduledBetween(mañana 00:00 UTC, mañana 23:59 UTC)— la query en Prisma filtrascheduledForen ese rango yreminderSentAt: null. Ese segundo filtro es el dedup real, no una verificación aparte en memoria.- Por cada cita: busca la mascota y sus dispositivos, dispara push (
sendToTokens) si hay al menos uno, y siempre marcareminderSentAt = nowal final — haya o no haya dispositivos. - El
codedel push depende del estado:appointment-reminder-suggestedsiAppointmentStatus.SUGGESTED(la clínica sugirió la cita, el dueño no la agendó todavía),appointment-reminder-scheduledpara cualquier otro estado.
Reglas de negocio
- El nombre
findScheduledBetweenes engañoso: no filtra por status en absoluto, solo por fecha +reminderSentAt: null. “Scheduled” se refiere a la fecha, no alAppointmentStatus. - El dueño recibe como máximo un recordatorio por cita — el
reminderSentAtse escribe en el mismo ciclo que se intenta el push, no en una confirmación de entrega.
Interacciones
Cron interno de petid-api → PushSender → notification-api → petid-client. No hay ida y vuelta con petid-crm.
Dónde seguir mirando
petid-api/src/infrastructure/tasks/send-appointment-reminders.task.tspetid-api/src/infrastructure/persistence/appointments/prisma-appointment.repository.ts(findScheduledBetween)petid-api/src/infrastructure/config/env.config.ts→ENABLE_CRON(defaultfalse)
Casos especiales
ENABLE_CRONesfalsepor default. Un deploy nuevo sin esa variable seteada explícitamente no manda ningún recordatorio — y no hay ningún log ni error que lo delate, simplemente el cron corre y no hace nada.- Una cita
CANCELLEDprogramada para mañana igual dispara el push (con codeappointment-reminder-scheduled, ya que soloSUGGESTEDtiene código propio) si nadie la puso enreminderSentAtantes — cancelar una cita no la excluye de este cron. Comparar conappointment-status-push.md: ese flujo sí filtra la transición aCANCELLED, este no filtra nada por status. - Si una cita no tiene dispositivos registrados en el momento exacto del cron, pierde el recordatorio para siempre —
reminderSentAtya quedó seteado, así que aunque el dueño registre un dispositivo esa misma tarde, no hay una segunda pasada.