# Lógica Backend

Motor de disponibilidad, callouts, procesos y validaciones por evento.

# Motor de Disponibilidad

`AppointmentAvailabilityEngine` concentra la lógica que decide si una cita o un slot es válido para un empleado, servicio y fecha determinados.

## Responsabilidades actuales

- Cargar una configuración activa por organización con fallback a organización `0`.
- Leer duración, precio e intervalo del servicio.
- Aplicar overrides de precio y duración por empleado-servicio.
- Validar que el empleado pueda prestar el servicio.
- Validar días libres de la organización.
- Validar días libres del empleado.
- Validar cobertura del horario semanal del empleado.
- Detectar conflictos con citas existentes pendientes o aprobadas.
- Aplicar límites diarios por empleado cuando correspondan.
- Crear slots persistentes para la UI de iDempiere.

## Regla de conflicto

Una cita se considera en conflicto cuando otra cita activa del mismo empleado se cruza con el rango nuevo y tiene estado pendiente o aprobado.

```
existing.StartDateTime < newEnd
AND existing.EndDateTime > newStart
AND existing.Status IN ('PE', 'AP')
AND existing.IsActive = 'Y'
```

## Generación de slots

La generación actual es específica por empleado. El proceso no genera para todos los empleados ni autoasigna el recurso; parte de una selección explícita de empleado, servicio y fecha.

<p class="callout info">**Nota:** esta decisión hace que el flujo de UI sea más claro: primero se elige quién atiende, luego qué servicio presta, después se generan horarios disponibles.</p>

## Reglas actuales vs reglas esperadas

<table id="bkmrk-reglaestado-actualco" style="width:100%;border-collapse:collapse;margin:12px 0 20px 0;"><thead><tr><th style="border:1px solid #d8dee9;padding:12px;background:#eef2f7;text-align:left;">Regla</th><th style="border:1px solid #d8dee9;padding:12px;background:#eef2f7;text-align:left;">Estado actual</th><th style="border:1px solid #d8dee9;padding:12px;background:#eef2f7;text-align:left;">Comportamiento esperado</th></tr></thead><tbody><tr><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">Empleado presta servicio</td><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">Implementado con `CDS_EmployeeService`.</td><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">Se mantiene como regla obligatoria.</td></tr><tr><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">Día libre de organización</td><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">Implementado por organización. Aunque `CDS_OrgDayOff` tiene `CDS_AppointmentConfig_ID`, el motor actual no filtra por configuración.</td><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">Debe filtrar por configuración cuando el servicio resuelva una configuración específica.</td></tr><tr><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">Día libre de empleado</td><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">Implementado con `CDS_EmployeeDayOff`.</td><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">Se mantiene como bloqueo específico del empleado.</td></tr><tr><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">Horario del empleado</td><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">Implementado con `CDS_EmployeeScheduleLine`.</td><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">Debe validarse después del horario macro de la configuración.</td></tr><tr><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">Horario macro de agenda</td><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">Pendiente. No existe aún una tabla equivalente a horario de configuración.</td><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">Debe definir los días y rangos generales permitidos, por encima del horario del empleado.</td></tr><tr><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">Anticipación mínima</td><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">Pendiente. Existe `MinAdvanceHours`, pero no se valida todavía.</td><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">Debe bloquear citas creadas con menos horas de anticipación que las configuradas.</td></tr><tr><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">Anticipación máxima</td><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">Pendiente. Existe `MaxAdvanceDays`, pero no se valida todavía.</td><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">Debe bloquear citas demasiado futuras.</td></tr><tr><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">Conflictos con citas existentes</td><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">Implementado para estados `Pending` y `Approved`.</td><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">Se mantiene como validación obligatoria.</td></tr><tr><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">Aprobación automática o forzada</td><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">Pendiente. Existen campos de configuración y servicio, pero no se aplican en el evento.</td><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">Debe resolverse en `BeforeNew` usando configuración y servicio.</td></tr></tbody></table>

## Roadmap del motor

- Resolver la configuración desde el servicio seleccionado, no solo desde la organización.
- Agregar validación de horario macro de configuración antes del horario del empleado.
- Aplicar `MinAdvanceHours` y `MaxAdvanceDays` al generar slots y al guardar citas.
- Filtrar días libres de organización por configuración cuando exista relación.
- Aplicar reglas de aprobación en el evento antes de validar el estado final de la cita.

# Callouts y Flujo de UI

`CalloutAppointment` ayuda al usuario a capturar una cita desde iDempiere sin reemplazar la validación final del backend.

## Comportamiento de callouts

<table id="bkmrk-campocomportamientoc" style="width:100%;border-collapse:collapse;margin:12px 0 20px 0;"><thead><tr><th style="border:1px solid #d8dee9;padding:12px;background:#eef2f7;text-align:left;">Campo</th><th style="border:1px solid #d8dee9;padding:12px;background:#eef2f7;text-align:left;">Comportamiento</th></tr></thead><tbody><tr><td style="border:1px solid #d8dee9;padding:12px;">`CDS_AppointmentService_ID`</td><td style="border:1px solid #d8dee9;padding:12px;">Copia precio y duración efectivos. Si existe empleado seleccionado, usa override de `CDS_EmployeeService`. Limpia slot y horas, pero no limpia empleado.</td></tr><tr><td style="border:1px solid #d8dee9;padding:12px;">`AppointmentDate`</td><td style="border:1px solid #d8dee9;padding:12px;">Limpia slot, hora inicial y hora final. No limpia empleado ni servicio.</td></tr><tr><td style="border:1px solid #d8dee9;padding:12px;">`CDS_AppointmentSlot_ID`</td><td style="border:1px solid #d8dee9;padding:12px;">Copia empleado, hora inicial, hora final, precio y duración desde el slot seleccionado.</td></tr></tbody></table>

## Flujo recomendado en ventana

1. Seleccionar el empleado que atenderá la cita.
2. Seleccionar el servicio filtrado para ese empleado.
3. Seleccionar la fecha de la cita.
4. Ejecutar el proceso de generación de slots.
5. Seleccionar el slot generado.
6. Guardar la cita.

<p class="callout warning">**Importante:** si un usuario modifica manualmente horas, precio o duración, el evento de modelo vuelve a validar la consistencia antes de guardar.</p>

# Procesos y Eventos

El plugin mantiene pocos procesos activos y concentra las reglas críticas en eventos de modelo.

## Procesos activos

<table id="bkmrk-procesousogenerateap" style="width:100%;border-collapse:collapse;margin:12px 0 20px 0;"><thead><tr><th style="border:1px solid #d8dee9;padding:12px;background:#eef2f7;text-align:left;">Proceso</th><th style="border:1px solid #d8dee9;padding:12px;background:#eef2f7;text-align:left;">Uso</th></tr></thead><tbody><tr><td style="border:1px solid #d8dee9;padding:12px;">`GenerateAppointmentSlots`</td><td style="border:1px solid #d8dee9;padding:12px;">Genera registros `CDS_AppointmentSlot` para el empleado, servicio y fecha seleccionados. Puede resolver valores desde la cita actual cuando se ejecuta desde la ventana.</td></tr><tr><td style="border:1px solid #d8dee9;padding:12px;">`ReopenAppointment`</td><td style="border:1px solid #d8dee9;padding:12px;">Reabre una cita procesada cambiándola a pendiente y limpiando `Processed` mediante un contexto controlado.</td></tr></tbody></table>

## Proceso `GenerateAppointmentSlots`

Este proceso se usa para crear horarios disponibles persistentes que luego pueden consultarse desde la ventana iDempiere o desde la API leyendo la tabla `CDS_AppointmentSlot`.

<table id="bkmrk-par%C3%A1metrorequeridous" style="width:100%;border-collapse:collapse;margin:12px 0 20px 0;"><thead><tr><th style="border:1px solid #d8dee9;padding:12px;background:#eef2f7;text-align:left;">Parámetro</th><th style="border:1px solid #d8dee9;padding:12px;background:#eef2f7;text-align:left;">Requerido</th><th style="border:1px solid #d8dee9;padding:12px;background:#eef2f7;text-align:left;">Uso</th></tr></thead><tbody><tr><td style="border:1px solid #d8dee9;padding:12px;">`AD_Org_ID`</td><td style="border:1px solid #d8dee9;padding:12px;">Sí, directo o resuelto desde la cita</td><td style="border:1px solid #d8dee9;padding:12px;">Organización para cargar configuración y validar disponibilidad.</td></tr><tr><td style="border:1px solid #d8dee9;padding:12px;">`CDS_AppointmentEmployee_ID`</td><td style="border:1px solid #d8dee9;padding:12px;">Sí</td><td style="border:1px solid #d8dee9;padding:12px;">Empleado para el cual se generan slots.</td></tr><tr><td style="border:1px solid #d8dee9;padding:12px;">`CDS_AppointmentService_ID`</td><td style="border:1px solid #d8dee9;padding:12px;">Sí</td><td style="border:1px solid #d8dee9;padding:12px;">Servicio que define duración, precio e intervalo del slot.</td></tr><tr><td style="border:1px solid #d8dee9;padding:12px;">`AppointmentDate`</td><td style="border:1px solid #d8dee9;padding:12px;">Sí</td><td style="border:1px solid #d8dee9;padding:12px;">Fecha para la cual se generan horarios disponibles.</td></tr><tr><td style="border:1px solid #d8dee9;padding:12px;">`CDS_Appointment_ID`</td><td style="border:1px solid #d8dee9;padding:12px;">Opcional</td><td style="border:1px solid #d8dee9;padding:12px;">Si se ejecuta desde una cita, permite resolver organización, empleado, servicio y fecha desde el registro actual.</td></tr></tbody></table>

Antes de generar nuevos slots, el proceso desactiva slots previos activos del usuario actual. Si hay una cita, desactiva los slots asociados a esa cita. Si no hay cita, desactiva slots del mismo usuario, empleado, servicio y fecha.

<p class="callout info">**Uso recomendado:** desde la ventana de cita se selecciona empleado, servicio y fecha; luego se ejecuta `GenerateAppointmentSlots`; finalmente se selecciona un registro de `CDS_AppointmentSlot`.</p>

## Proceso `ReopenAppointment`

Este proceso reabre una cita procesada para permitir correcciones administrativas controladas.

<table id="bkmrk-par%C3%A1metrorequeridous-1" style="width:100%;border-collapse:collapse;margin:12px 0 20px 0;"><thead><tr><th style="border:1px solid #d8dee9;padding:12px;background:#eef2f7;text-align:left;">Parámetro</th><th style="border:1px solid #d8dee9;padding:12px;background:#eef2f7;text-align:left;">Requerido</th><th style="border:1px solid #d8dee9;padding:12px;background:#eef2f7;text-align:left;">Uso</th></tr></thead><tbody><tr><td style="border:1px solid #d8dee9;padding:12px;">`CDS_Appointment_ID`</td><td style="border:1px solid #d8dee9;padding:12px;">Sí, directo o resuelto desde el registro actual</td><td style="border:1px solid #d8dee9;padding:12px;">Cita procesada que se desea reabrir.</td></tr></tbody></table>

Si la cita ya está abierta, el proceso informa que no requiere reapertura. Si está procesada, activa un contexto temporal `#CDS_ReopenAppointment`, coloca `Processed=N` y cambia el estado a `Pending`.

## Validación por evento actual

`AppointmentModelValidator` se ejecuta antes de crear o cambiar una cita. Para citas pendientes o aprobadas valida los datos mínimos y la disponibilidad real.

- Cliente requerido.
- Empleado requerido.
- Servicio requerido.
- Fecha de cita requerida.
- Hora inicial y final requeridas.
- Hora inicial menor que hora final.
- Fecha de cita igual al día de la hora inicial.
- Duración mayor que cero.
- Precio no negativo.
- Empleado habilitado para prestar el servicio.
- Slot consistente con empleado, servicio, horas, duración y precio.
- Sin bloqueo por día libre de organización o empleado.
- Horario cubierto por agenda del empleado.
- Sin conflicto con otra cita activa pendiente o aprobada.

## Estados y procesamiento actual

- Si el estado viene vacío, se asigna `Draft`.
- Los estados finales marcan `Processed = true`.
- Los estados no finales limpian `Processed`.
- Una cita procesada no se puede modificar salvo mediante reapertura controlada.

## Roadmap de validaciones en eventos

Las siguientes reglas deben agregarse al evento para que el comportamiento sea consistente desde ventanas iDempiere y desde REST/API.

<table id="bkmrk-validaci%C3%B3n-pendiente" style="width:100%;border-collapse:collapse;margin:12px 0 20px 0;"><thead><tr><th style="border:1px solid #d8dee9;padding:12px;background:#eef2f7;text-align:left;">Validación pendiente</th><th style="border:1px solid #d8dee9;padding:12px;background:#eef2f7;text-align:left;">Momento sugerido</th><th style="border:1px solid #d8dee9;padding:12px;background:#eef2f7;text-align:left;">Regla esperada</th></tr></thead><tbody><tr><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">Resolver configuración desde servicio</td><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">`BeforeNew` y `BeforeChange`</td><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">El servicio debe indicar qué configuración de agenda aplica. La cita usa esa configuración para anticipación, horario macro y aprobación.</td></tr><tr><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">Servicio con una sola configuración</td><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">Al guardar servicio o relación configuración-servicio</td><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">Un servicio no debe estar asociado a más de una configuración activa.</td></tr><tr><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">Anticipación mínima</td><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">`BeforeNew` y generación de slots</td><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">Si `StartDateTime` está antes de ahora + `MinAdvanceHours`, la cita o slot debe bloquearse.</td></tr><tr><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">Anticipación máxima</td><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">`BeforeNew` y generación de slots</td><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">Si `StartDateTime` supera hoy + `MaxAdvanceDays`, la cita o slot debe bloquearse.</td></tr><tr><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">Horario macro de configuración</td><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">`BeforeNew`, `BeforeChange` y generación de slots</td><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">El horario de agenda general debe contener el rango solicitado antes de evaluar el horario del empleado.</td></tr><tr><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">Forzar aprobación</td><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">`BeforeNew`</td><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">Si la configuración tiene `ForceApproval=Y`, la cita debe quedar pendiente aunque el servicio permita aprobación automática.</td></tr><tr><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">Autoaprobar si el servicio lo permite</td><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">`BeforeNew`</td><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">Si `AutoApproveIfServiceAllows=Y` y el servicio no requiere aprobación, la cita puede quedar `Approved`.</td></tr></tbody></table>

## Roadmap de workflows y correos

Además de las validaciones, falta definir workflows y plantillas de correo para notificar al cliente cuando cambie el estado de una cita.

<table id="bkmrk-flujo-pendientedispa" style="width:100%;border-collapse:collapse;margin:12px 0 20px 0;"><thead><tr><th style="border:1px solid #d8dee9;padding:12px;background:#eef2f7;text-align:left;">Flujo pendiente</th><th style="border:1px solid #d8dee9;padding:12px;background:#eef2f7;text-align:left;">Disparo sugerido</th><th style="border:1px solid #d8dee9;padding:12px;background:#eef2f7;text-align:left;">Resultado esperado</th></tr></thead><tbody><tr><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">Notificar cita aprobada</td><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">Cambio de estado a `Approved`.</td><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">Enviar correo al cliente con fecha, hora, servicio y empleado.</td></tr><tr><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">Notificar cita cancelada</td><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">Cambio de estado a `Cancelled`.</td><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">Enviar correo al cliente con el motivo de cancelación cuando exista.</td></tr><tr><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">Notificar reprogramación</td><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">Cambio de fecha, hora o empleado.</td><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">Enviar correo con el nuevo horario confirmado.</td></tr><tr><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">Recordatorio de cita</td><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">Proceso programado antes de `StartDateTime`.</td><td style="border:1px solid #d8dee9;padding:12px;vertical-align:top;">Enviar recordatorio al cliente según configuración.</td></tr></tbody></table>

También falta crear las plantillas de correo, definir destinatarios, configurar remitente, parametrizar cuándo enviar cada notificación y evitar reenvíos duplicados con campos de control si aplica.

## Orden recomendado de validación

1. Resolver servicio, empleado y organización.
2. Resolver configuración de agenda desde el servicio.
3. Aplicar reglas de anticipación mínima y máxima.
4. Validar horario macro de configuración.
5. Validar días libres de organización vinculados a la configuración.
6. Validar relación empleado-servicio.
7. Validar horario y días libres del empleado.
8. Validar slot seleccionado.
9. Validar conflictos con citas existentes.
10. Resolver estado de aprobación: pendiente o aprobada.