# US-MAI003-007: Cancelar OT ## Metadata | Campo | Valor | |-------|-------| | **ID** | US-MAI003-007 | | **Epica** | EPIC-MAI-003 - Ordenes de Transporte | | **Modulo** | ordenes-transporte | | **Prioridad** | P1 | | **Story Points** | 3 | | **Sprint** | Por asignar | | **Estado** | Backlog | ## Historia de Usuario **Como** despachador o coordinador de una empresa transportista, **quiero** cancelar una orden de transporte que ya no sera ejecutada, **para** liberar los recursos asociados y mantener actualizado el estado de las operaciones con un registro claro del motivo de cancelacion. ## Descripcion Detallada La cancelacion de una OT es una operacion necesaria cuando el cliente retira la solicitud, se duplica una orden, o se presentan circunstancias que impiden la ejecucion del servicio. El sistema debe permitir cancelar OTs en estados previos a la ejecucion (BORRADOR, CONFIRMADA y ASIGNADA), pero no una vez que el viaje esta en proceso o completado. Al cancelar, el sistema debe registrar el motivo de cancelacion y marcar la OT con estado CANCELADA y deleted_at como fecha de cancelacion logica. Si la OT estaba agrupada en un embarque, debe removerse automaticamente y recalcular los totales del embarque. Si estaba asignada a un viaje, se debe evaluar si el viaje puede continuar sin esa OT. La cancelacion es una operacion irreversible: una OT cancelada no puede volver a un estado activo. Si se necesita reactivar, se debe crear una nueva OT. ## Criterios de Aceptacion ### Escenario 1: Cancelar OT en estado BORRADOR **Dado** que existe una OT en estado BORRADOR, **Cuando** el despachador solicita la cancelacion con motivo "Solicitud retirada por el cliente", **Entonces** el sistema cambia el estado a CANCELADA, registra deleted_at con la fecha actual, registra updated_by_id con el usuario que cancela, y la OT deja de aparecer en listados activos. ### Escenario 2: Cancelar OT CONFIRMADA que pertenece a un embarque **Dado** que una OT en estado CONFIRMADA esta agrupada en un embarque con 3 OTs, **Cuando** el coordinador cancela la OT con motivo "Carga no disponible en origen", **Entonces** el sistema cambia la OT a CANCELADA, la remueve del embarque (limpia embarque_id), recalcula los totales del embarque (total_ots = 2, peso y volumen actualizados). ### Escenario 3: Rechazo de cancelacion en estado EN_PROCESO **Dado** que una OT se encuentra en estado EN_PROCESO (viaje en curso), **Cuando** el usuario intenta cancelar la OT, **Entonces** el sistema rechaza la operacion con error 422 y mensaje "No se puede cancelar una orden de transporte en estado EN_PROCESO. Registre una incidencia en su lugar." ### Escenario 4: Motivo de cancelacion obligatorio **Dado** que el usuario solicita cancelar una OT en estado valido, **Cuando** no proporciona un motivo de cancelacion, **Entonces** el sistema rechaza la operacion indicando que el motivo de cancelacion es obligatorio. ## Tareas Tecnicas - **Database:** Utilizar campo `estado` para marcar como CANCELADA y `deleted_at` para soft delete. El campo `deleted_at` (TIMESTAMPTZ) sirve como fecha de cancelacion logica. - **Backend:** Agregar metodo `cancelar(id, motivo, userId)` en `OrdenTransporteService` con validacion de estado: solo permitir cancelacion desde BORRADOR, CONFIRMADA o ASIGNADA. Si la OT tiene embarque_id, invocar `EmbarqueService.removeOt()` para limpiar la referencia y recalcular totales. Configurar endpoint PATCH `/api/v1/ordenes-transporte/:id/cancelar`. Crear `CancelarOrdenTransporteDto` con campo obligatorio `motivo`. - **Frontend:** Agregar boton "Cancelar OT" visible solo en estados BORRADOR, CONFIRMADA y ASIGNADA. Al presionar, mostrar dialogo de confirmacion con campo obligatorio de motivo. Tras cancelacion exitosa, redirigir al listado con mensaje de confirmacion. - **Tests:** Tests unitarios de validacion de estados permitidos para cancelacion. Tests de integracion del flujo con embarque (remocion y recalculo). Tests de motivo obligatorio. Tests de rechazo en estados no cancelables. ## Dependencias - **Depende de:** US-MAI003-001 (la OT debe existir), US-MAI003-004 (logica de remocion de embarque) - **Bloquea:** Ninguno directamente (flujo terminal) ## Notas Tecnicas - **Endpoint:** PATCH `/api/v1/ordenes-transporte/:id/cancelar` - **Body:** `{ "motivo": "string (requerido)" }` - **Estados cancelables:** BORRADOR, CONFIRMADA, ASIGNADA - **Estados no cancelables:** EN_PROCESO, COMPLETADA, FACTURADA, CANCELADA - **Soft delete:** `deleted_at = NOW()` marca la cancelacion logica. Los listados por defecto filtran `WHERE deleted_at IS NULL` - **Efecto cascada:** Si la OT pertenece a un embarque, el servicio debe: 1) limpiar embarque_id de la OT, 2) recalcular totales del embarque, 3) si el embarque queda sin OTs, evaluar si se cierra automaticamente - **Irreversibilidad:** No existe endpoint para reactivar una OT cancelada. El motivo de cancelacion se almacena en la columna instrucciones_especiales o en una tabla de auditoria dedicada --- *US-MAI003-007 - ERP Transportistas v1.0.0*