# US-MAI003-004: Agrupar OTs en embarque ## Metadata | Campo | Valor | |-------|-------| | **ID** | US-MAI003-004 | | **Epica** | EPIC-MAI-003 - Ordenes de Transporte | | **Modulo** | ordenes-transporte | | **Prioridad** | P0 | | **Story Points** | 8 | | **Sprint** | Por asignar | | **Estado** | Backlog | ## Historia de Usuario **Como** coordinador de trafico de una empresa transportista, **quiero** agrupar multiples ordenes de transporte en un embarque de consolidacion, **para** optimizar la capacidad de las unidades, reducir costos operativos y asignar el grupo completo a un viaje. ## Descripcion Detallada Un embarque (shipment) es una agrupacion logica de OTs que se transportaran juntas. La consolidacion es una practica fundamental en el transporte de carga, especialmente en modalidad LTL (Less Than Truckload), donde se combinan cargas parciales de distintos clientes o destinos para aprovechar al maximo la capacidad de la unidad. La tabla `transport.embarques` almacena el codigo unico del embarque, una descripcion, el cliente principal, los totales consolidados (total_ots, peso_total_kg, volumen_total_m3), el estado del embarque y la referencia al viaje asignado. Al agregar OTs a un embarque, el sistema actualiza el campo `embarque_id` en cada OT y recalcula los totales consolidados del embarque. El coordinador debe poder crear un embarque, buscar OTs confirmadas compatibles (por zona, ventana de tiempo, tipo de carga) y agregarlas al embarque. Al completar la agrupacion, el embarque puede asignarse a un viaje para su ejecucion. ## Criterios de Aceptacion ### Escenario 1: Crear embarque nuevo **Dado** que el coordinador tiene permisos de creacion de embarques, **Cuando** ingresa codigo, descripcion y selecciona el cliente principal, **Entonces** el sistema crea el embarque en estado ABIERTO con total_ots = 0 y genera un registro en `transport.embarques` con constraint uq_embarque_tenant_codigo. ### Escenario 2: Agregar OTs al embarque **Dado** que existe un embarque en estado ABIERTO y hay OTs en estado CONFIRMADA del mismo tenant, **Cuando** el coordinador selecciona 3 OTs y las agrega al embarque, **Entonces** el sistema actualiza embarque_id en cada OT, recalcula total_ots (3), peso_total_kg (suma de peso_kg de las 3 OTs) y volumen_total_m3 (suma de volumen_m3 de las 3 OTs). ### Escenario 3: Remover OT de un embarque **Dado** que un embarque tiene 3 OTs agrupadas y ninguna esta en estado EN_PROCESO o posterior, **Cuando** el coordinador remueve una OT del embarque, **Entonces** el sistema limpia el campo embarque_id de la OT removida, recalcula los totales del embarque (total_ots = 2, peso y volumen actualizados). ### Escenario 4: Asignar embarque a viaje **Dado** que un embarque tiene al menos una OT agrupada, **Cuando** el coordinador asigna el embarque a un viaje existente, **Entonces** el sistema actualiza viaje_id en el embarque, actualiza viaje_id y embarque_id en cada OT del embarque, y cambia el estado de las OTs de CONFIRMADA a ASIGNADA. ## Tareas Tecnicas - **Database:** Verificar tabla `transport.embarques` con campos: codigo (VARCHAR(50)), descripcion (VARCHAR(500)), cliente_id (UUID), total_ots (INT DEFAULT 0), peso_total_kg (DECIMAL(12,2)), volumen_total_m3 (DECIMAL(12,4)), estado (VARCHAR(20) DEFAULT 'ABIERTO'), viaje_id (UUID). Verificar indices idx_embarque_tenant e idx_embarque_cliente. Verificar constraint uq_embarque_tenant_codigo. - **Backend:** Crear entity `Embarque` mapeada a `transport.embarques`. Crear `EmbarqueService` con metodos: `create()`, `addOts()`, `removeOt()`, `assignToViaje()`, `recalcularTotales()`. Crear `EmbarqueController` con endpoints: POST `/api/v1/embarques`, GET `/api/v1/embarques`, GET `/api/v1/embarques/:id`, POST `/api/v1/embarques/:id/ots` (agregar OTs), DELETE `/api/v1/embarques/:id/ots/:otId` (remover OT), PATCH `/api/v1/embarques/:id/asignar-viaje`. Crear DTOs correspondientes. - **Frontend:** Crear pagina `EmbarquesListPage` con listado de embarques y conteo de OTs. Crear pagina `EmbarqueDetailPage` con detalle del embarque y lista de OTs agrupadas. Implementar selector de OTs disponibles (CONFIRMADA, sin embarque) con filtros. Incluir resumen visual de capacidad (peso/volumen utilizados). - **Tests:** Tests unitarios de recalculo de totales. Tests de integracion del flujo completo (crear embarque, agregar OTs, asignar a viaje). Tests de validacion de estados permitidos. ## Dependencias - **Depende de:** US-MAI003-001 (las OTs deben existir y estar CONFIRMADAS), MAI-002 (datos de clientes para cliente_id del embarque) - **Bloquea:** MAI-004 (Planeacion - el embarque es la unidad de asignacion a viajes), MAI-005 (Despacho - el despacho opera sobre viajes con embarques) ## Notas Tecnicas - **Endpoints:** POST `/api/v1/embarques`, POST `/api/v1/embarques/:id/ots`, PATCH `/api/v1/embarques/:id/asignar-viaje` - **Entity:** `Embarque` -> `transport.embarques` - **RLS:** Politica `tenant_isolation_embarques` filtra por `tenant_id` - **Recalculo atomico:** El metodo `recalcularTotales()` debe ejecutarse en una transaccion que actualice las OTs y los totales del embarque de forma atomica - **Generacion de codigo:** Formato sugerido: `EMB-{YYYY}-{SECUENCIAL:5}` - **Validaciones de negocio:** Solo OTs en estado CONFIRMADA pueden agregarse a un embarque. Una OT solo puede pertenecer a un embarque a la vez (embarque_id es nullable pero unico por OT activa). Al asignar a viaje, todas las OTs del embarque pasan a estado ASIGNADA --- *US-MAI003-004 - ERP Transportistas v1.0.0*