# Workflow de rutas y pickups

Este documento explica como funciona el workflow actual para rutas de pickup (`Listpickup`) y pickups individuales (`Pickup`), que eventos existen, que reglas se aplican y que tablas participan.

El fin de este cambio es reemplazar el manejo disperso de `status` por una sola fuente de verdad de workflow. La idea es que los documentos que manejan estados no tengan cada uno reglas separadas, setters duplicados o servicios paralelos decidiendo transiciones. Por ahora el alcance es solo rutas y pickup; despues el mismo patron puede extenderse a otros documentos que tengan estados.

## Idea general

El workflow separa tres cosas:

- `event_definition`: catalogo de eventos permitidos. Define el evento, su etiqueta, si cambia estado y a que estado cambia.
- `tenant_workflow_rule`: reglas por compania. Hoy se usa para validar reglas configurables de rutas, especialmente desde que estado se permite ejecutar un evento.
- `logistic_event`: historial real de eventos aplicados. Aqui se registra lo que efectivamente paso sobre una entidad.

Hay que separar dos conceptos:

- Estado guardado: la columna actual en la entidad, usada para consultar rapido el estado vigente.
- Fuente de verdad del flujo: los eventos, reglas y transiciones del workflow.

La entidad todavia guarda su estado actual en una columna propia:

- Rutas: `listpickup.current_state`.
- Pickups: `pickup.status`, usado como snapshot del estado actual mientras el workflow decide las transiciones.
- HBL/AWB: `current_state`, pero no son el foco de este flujo de rutas.

El objetivo no es eliminar la columna que guarda el estado actual, sino evitar que la logica de transiciones viva repartida en muchos lugares. La columna queda como snapshot del estado vigente; el workflow debe ser quien decide si se puede cambiar y que evento historico se registra.

## Rutas de pickup (`Listpickup`)

En el codigo, la ruta es la entidad `Listpickup`. El workflow concreto esta en `src/Workflow/ListpickupWorkflowService.php`.

Rutas ya estan usando el modelo nuevo con `current_state` y reglas por tenant. Esta es la primera parte donde se esta reemplazando el `status` tradicional por un campo mas consistente y por reglas centralizadas.

Estados actuales de ruta:

| Estado | Significado |
| --- | --- |
| `DRAFT` | La ruta existe pero todavia esta en preparacion. Se puede editar libremente y agregar/quitar pickups. |
| `READY` | La ruta esta lista para iniciar. Espera salida del conductor. |
| `IN_PROGRESS` | La ruta esta ejecutandose. Hay entregas o pickups en proceso. |
| `PAUSED` | La ruta esta detenida temporalmente. Ejemplo: almuerzo, falla mecanica o clima. |
| `COMPLETED` | La ruta termino correctamente. |
| `CANCELLED` | La ruta fue cancelada antes de finalizar. |

Los estados vienen de `ListpickupStatusEnum`.

## Eventos de rutas

Estos son los eventos admitidos por el workflow de rutas:

| Evento en codigo | Codigo en DB | Cambia estado | Estado destino | Para que sirve |
| --- | --- | --- | --- | --- |
| `LISTPICKUP_CREATED` | `listpickup_created` | Si | `DRAFT` | Crear la ruta en preparacion. |
| `LISTPICKUP_PICKUPS_ADDED` | `listpickup_pickups_added` | No | `NULL` | Registrar que se agregaron pickups a la ruta. |
| `LISTPICKUP_PICKUPS_REMOVED` | `listpickup_pickups_removed` | No | `NULL` | Registrar que se quitaron pickups de la ruta. |
| `LISTPICKUP_READY` | `listpickup_ready` | Si | `READY` | Marcar la ruta como lista para salir. |
| `LISTPICKUP_STARTED` | `listpickup_started` | Si | `IN_PROGRESS` | Iniciar la ejecucion de la ruta. |
| `LISTPICKUP_PAUSED` | `listpickup_paused` | Si | `PAUSED` | Pausar temporalmente la ruta. |
| `LISTPICKUP_RESUMED` | `listpickup_resumed` | Si | `IN_PROGRESS` | Reanudar una ruta pausada. |
| `LISTPICKUP_COMPLETED` | `listpickup_completed` | Si | `COMPLETED` | Completar la ruta correctamente. Al cerrar, los pickups que aun no terminaron se cancelan automaticamente. |
| `LISTPICKUP_CANCELLED` | `listpickup_cancelled` | Si | `CANCELLED` | Cancelar la ruta antes de finalizar. |

Importante: el servicio puede recibir eventos en mayuscula (`LISTPICKUP_CREATED`), pero `EventDefinitionResolver` los normaliza a minuscula para buscar en `event_definition`.

## Reglas de rutas

Las reglas principales de rutas vienen del comportamiento legacy y ahora se expresan en `tenant_workflow_rule`:

| Evento | Regla |
| --- | --- |
| `listpickup_created` | Solo se permite desde `DRAFT`. En la practica, al crear la ruta ya se inicializa como draft antes de aplicar el evento. |
| `listpickup_pickups_added` | Solo se permite si la ruta esta `DRAFT`. |
| `listpickup_pickups_removed` | Solo se permite si la ruta esta `DRAFT`. |
| `listpickup_ready` | Solo se permite desde `DRAFT`. |
| `listpickup_started` | Solo se permite desde `READY`. |
| `listpickup_paused` | Solo se permite desde `IN_PROGRESS`. |
| `listpickup_resumed` | Solo se permite desde `PAUSED`. |
| `listpickup_completed` | Solo se permite desde `IN_PROGRESS`. |
| `listpickup_cancelled` | Solo se permite desde `DRAFT`, `READY` o `IN_PROGRESS`. |

En SQL, esas reglas se guardan asi:

```sql
entity_type = 'listpickup'
event_code = 'listpickup_pickups_added'
allowed_from_states = JSON_ARRAY('DRAFT')
is_enabled = 1
is_active = 1
```

La clase que lee estas reglas es `TenantWorkflowRuleEvaluator`.

Flujo de validacion de una ruta:

1. `ListpickupWorkflowService::apply()` recibe la entidad, evento y contexto.
2. `EventDefinitionResolver` busca el evento en `event_definition`.
3. `TenantWorkflowRuleEvaluator` busca la regla de la compania en `tenant_workflow_rule`.
4. Si existe regla activa, valida que `current_state` este dentro de `allowed_from_states`.
5. `ListpickupWorkflowService` valida reglas que todavia no estan modeladas en tabla, por ejemplo que agregar/quitar pickups reciba `pickupIds`.
6. Si `event_definition.affects_state = 1`, el workflow cambia `current_state` al `target_state`.
7. `LogisticEventService` registra el evento real en `logistic_event`.

Reglas que siguen en codigo para rutas:

- `LISTPICKUP_PICKUPS_ADDED` requiere `context['pickupIds']` no vacio.
- `LISTPICKUP_PICKUPS_REMOVED` requiere `context['pickupIds']` no vacio.
- Los servicios de aplicacion todavia validan cosas de negocio alrededor de la ruta, por ejemplo que el pickup no este ya asignado a otra ruta.

## Donde se aplica el workflow de rutas

El workflow de rutas se llama desde `ListPickupService`:

| Accion | Evento aplicado |
| --- | --- |
| Crear ruta | `LISTPICKUP_CREATED` |
| Agregar pickups a ruta | `LISTPICKUP_PICKUPS_ADDED` |
| Quitar pickups de ruta | `LISTPICKUP_PICKUPS_REMOVED` |

Los eventos de avance operativo (`LISTPICKUP_READY`, `LISTPICKUP_STARTED`, `LISTPICKUP_PAUSED`, `LISTPICKUP_RESUMED`, `LISTPICKUP_COMPLETED`, `LISTPICKUP_CANCELLED`) ya quedan definidos en el workflow y en las semillas. Hay que conectar endpoints/servicios para dispararlos cuando el ticket de operaciones lo pida.

## Pickups individuales (`Pickup`)

El workflow de pickup individual esta en `src/Workflow/PickupWorkflowService.php`.

Este flujo no es lo mismo que rutas. Una ruta agrupa pickups; un pickup individual representa una orden de recogida concreta.

Pickup individual ya usa el workflow como fuente para cambiar estado. El endpoint y el servicio publico conservan nombres de compatibilidad (`PickupStatusEventService` y `PickupStatusEventDTO`), pero ya no son el motor legacy que decide transiciones ni escribe directamente el estado. Ese servicio actua como adaptador del endpoint: valida que el pickup exista, llama al workflow y devuelve el historial desde `logistic_event`.

Estados actuales de pickup:

| Estado |
| --- |
| `PENDIENTE` |
| `PROGRAMADO` |
| `REPROGRAMADO` |
| `RECOGIDO` |
| `EN_TRANSITO` |
| `RETARDADO` |
| `ENTREGADO` |
| `ANULADO` |
| `PROCESADO` |

Los estados vienen de `PickupStatusEventTypeEnum`.

## Eventos de pickup individual

El workflow de pickup acepta codigos de evento workflow. No se debe enviar el estado directo como evento.

Eventos alias actualmente admitidos:

| Evento | Estado destino |
| --- | --- |
| `PICKUP_CREATED` | `PENDIENTE` |
| `PICKUP_SCHEDULED` | `PROGRAMADO` |
| `PICKUP_RESCHEDULED` | `REPROGRAMADO` |
| `PICKUP_PICKED_UP` | `RECOGIDO` |
| `PICKUP_IN_TRANSIT` | `EN_TRANSITO` |
| `PICKUP_DELAYED` | `RETARDADO` |
| `PICKUP_DELIVERED` | `ENTREGADO` |
| `PICKUP_REJECTED` | `ANULADO` |
| `PICKUP_CANCELLED` | `ANULADO` |
| `PICKUP_PROCESSED` | `PROCESADO` |

## Reglas de pickup individual

Las reglas de transicion de pickup individual ahora se guardan en `tenant_workflow_rule`, igual que rutas. `PickupWorkflowService` exige que exista una regla de tenant para el evento; si no existe, el evento se rechaza. Las validaciones propias del evento, como comentario, payload o adjuntos requeridos, tambien viven en el workflow.

| Estado actual | Estados destino permitidos |
| --- | --- |
| `PENDIENTE` | `PENDIENTE`, `PROGRAMADO`, `REPROGRAMADO`, `ANULADO`, `RECOGIDO`, `PROCESADO` |
| `PROGRAMADO` | `REPROGRAMADO`, `RECOGIDO`, `ANULADO`, `PROCESADO` |
| `REPROGRAMADO` | `PROGRAMADO`, `ANULADO`, `PROCESADO` |
| `RECOGIDO` | `EN_TRANSITO`, `PROCESADO` |
| `EN_TRANSITO` | `ENTREGADO`, `ANULADO`, `RETARDADO`, `PROCESADO` |
| `RETARDADO` | `EN_TRANSITO`, `REPROGRAMADO` |
| `ENTREGADO` | `PROCESADO` |
| `ANULADO` | ninguno |
| `PROCESADO` | ninguno |

Reglas adicionales de pickup:

- No se puede aplicar un evento si el pickup ya esta cancelado/anulado.
- `PICKUP_DELAYED` requiere `comment`.
- `PICKUP_DELAYED` requiere `payload`.
- `PICKUP_DELAYED` requiere `payload.reason` o `payload.estimatedDeliveryAt`.
- `PICKUP_REJECTED` puede usarse para cancelar pickups que siguen en transito o, en rutas de recoger y dejar, pickups que ya fueron recogidos pero aun no entregados.
- `PICKUP_DELIVERED` se puede aplicar aunque el pickup ya haya quedado cancelado por cierre de ruta.

Esas reglas adicionales siguen en codigo porque son validaciones de datos del evento, no transiciones de estado. La transicion de estado como tal viene de `tenant_workflow_rule.allowed_from_states`.

## Diferencia con el legacy

El legacy de pickup es `PickupStatusEventService`. Ese servicio crea eventos en `pickup_status_event`, valida transiciones y actualiza `pickup.status`.

Para el nuevo workflow:

- El legacy sirvio como referencia de reglas.
- Las transiciones de pickup se movieron a `tenant_workflow_rule`.
- El historial nuevo se registra en `logistic_event`.
- `pickup_status_event` ya no debe ser quien decide ni aplique cambios de estado.
- `PickupStatusEventService` queda como adaptador de compatibilidad para los endpoints existentes, sin resolver estados ni duplicar reglas.

Flujo esperado de pickup despues de la migracion:

1. El servicio recibe un evento de negocio, no un cambio directo de `status`.
2. El workflow valida si el evento se permite desde el estado actual.
3. El workflow actualiza el estado vigente.
4. El workflow registra el evento historico.
5. Ningun otro servicio cambia el estado por fuera del workflow.

## SQL actual

El archivo `sql/workflow_tables_relations_hbl_awb_pickup.sql` hace varias cosas:

- Crea las tablas `event_definition`, `logistic_event` y `tenant_workflow_rule` si no existen.
- Agrega columnas o relaciones faltantes de forma condicional.
- Añade `listpickup.current_state` y mantiene `listpickup.status` para legacy.
- Inserta eventos de rutas en `event_definition`.
- Inserta reglas de rutas en `tenant_workflow_rule` para cada `maincompany`.
- Inserta eventos de pickup individual en `event_definition`.
- Inserta reglas de pickup individual en `tenant_workflow_rule` para cada `maincompany`.

## Resumen corto

Para rutas, el workflow actual funciona asi:

1. La ruta vive en `listpickup`.
2. Su estado actual vive en `listpickup.current_state`.
3. Sus estados son `DRAFT`, `READY`, `IN_PROGRESS`, `PAUSED`, `COMPLETED` y `CANCELLED`.
4. Sus eventos principales son `listpickup_created`, `listpickup_pickups_added`, `listpickup_pickups_removed`, `listpickup_ready`, `listpickup_started`, `listpickup_paused`, `listpickup_resumed`, `listpickup_completed` y `listpickup_cancelled`.
5. `event_definition` define que eventos existen y si cambian estado.
6. `tenant_workflow_rule` define desde que estados se permite ejecutar cada evento por compania.
7. `logistic_event` registra el historial real cuando un evento se aplica.

La meta general es que rutas y pickup dejen de depender de reglas de `status` repartidas en varios servicios. Rutas usan `current_state` y reglas de tenant. Pickup mantiene `pickup.status` como snapshot por compatibilidad, pero el cambio de estado debe pasar por workflow y el historial nuevo queda en `logistic_event`.
