# F-125 — Permitir transiciones hacia atrás en pedidos + reenviar email SHIPPED↔PROCESSING ## Diagnóstico El operador reporta que al equivocarse marcando un pedido (p. ej., marcar PROCESSING cuando aún no está listo, o SHIPPED antes de tiempo) **no puede revertir**. El state machine actual solo permite avanzar o cancelar: ```ts // src/modules/orders/domain/order.ts export const ALLOWED_TRANSITIONS: Readonly>> = { PENDING: ['AWAITING_PAYMENT', 'CANCELLED'], AWAITING_PAYMENT: ['PAID', 'CANCELLED'], PAID: ['PROCESSING', 'CANCELLED', 'REFUNDED'], PROCESSING: ['SHIPPED', 'CANCELLED', 'REFUNDED'], SHIPPED: ['DELIVERED', 'PARTIALLY_REFUNDED'], DELIVERED: ['PARTIALLY_REFUNDED'], CANCELLED: [], REFUNDED: [], PARTIALLY_REFUNDED: [], }; ``` El state machine está **duplicado** en el frontend (`apps/admin/src/app/(dashboard)/orders/[id]/page.tsx:32`) — ambos deben actualizarse para mantener consistencia. El reenvío de email ya ocurre en cada transición admin (orders.routes.ts:271), así que añadir transiciones nuevas implica que el email se envía automáticamente con el estado y courier/tracking actuales del pedido. ## Diseño ### Transiciones hacia atrás (un paso) - `PROCESSING → PAID` (revertir procesado) - `SHIPPED → PROCESSING` (revertir envío) - `DELIVERED → SHIPPED` (revertir entrega) Estados terminales (`CANCELLED`, `REFUNDED`, `PARTIALLY_REFUNDED`) **se mantienen terminales** — un reembolso no se puede deshacer. ```ts export const ALLOWED_TRANSITIONS = { PENDING: ['AWAITING_PAYMENT', 'CANCELLED'], AWAITING_PAYMENT: ['PAID', 'CANCELLED'], PAID: ['PROCESSING', 'CANCELLED', 'REFUNDED'], PROCESSING: ['PAID', 'SHIPPED', 'CANCELLED', 'REFUNDED'], // + PAID SHIPPED: ['PROCESSING', 'DELIVERED', 'PARTIALLY_REFUNDED'], // + PROCESSING DELIVERED: ['SHIPPED', 'PARTIALLY_REFUNDED'], // + SHIPPED CANCELLED: [], REFUNDED: [], PARTIALLY_REFUNDED: [], }; ``` ### Frontend - Actualizar la copia local de `ALLOWED_TRANSITIONS` en `apps/admin/src/app/(dashboard)/orders/[id]/page.tsx:32` con las mismas nuevas transiciones. - Añadir `ACTION_LABELS` para los nuevos botones: - `PAID → PROCESSING`: ya existía como "Procesar pedido" - `PROCESSING → PAID`: "Revertir a Pagado" (nuevo) - `SHIPPED → PROCESSING`: "Revertir a En preparación" (nuevo) - `DELIVERED → SHIPPED`: "Revertir a Enviado" (nuevo) ### Email - El handler admin `/orders/:id/transitions/admin` ya envía email en cada transición (orders.routes.ts:271). No requiere cambios. - Cuando `SHIPPED → PROCESSING`: el email se reenvía con `state='PROCESSING'` pero conserva `courier` y `trackingNumber` actuales del pedido en el cuerpo (verificado en `buildOrderStatusEmail` — incluye las líneas solo si están presentes, independientemente del estado). - El cliente recibe el email con "Tu pedido #ABC ahora está: En preparación" más el bloque de courier/tracking si aún están guardados. ### Tests Actualizar `src/modules/orders/tests/order-state-machine.test.ts`: - Eliminar o invertir el test "rejects SHIPPED back to PENDING" (sigue válido: SHIPPED → PENDING sigue prohibido, pero SHIPPED → PROCESSING ahora permitido). - Añadir test explícito: `isTransitionAllowed('SHIPPED', 'PROCESSING') === true`, etc. ## Riesgos - **Bajo**. El cambio es ampliar el grafo de transiciones. Los terminales siguen terminales. - **Riesgo operativo**: un operador puede hacer ping-pong entre PROCESSING y SHIPPED. Cada cambio envía email al cliente → spam si abusa. Mitigación: no se cambia el límite de frecuencia, es responsabilidad del operador. ## Plan 1. Editar `src/modules/orders/domain/order.ts` — añadir transiciones hacia atrás. 2. Editar `src/modules/orders/tests/order-state-machine.test.ts` — actualizar tests. 3. Editar `apps/admin/src/app/(dashboard)/orders/[id]/page.tsx` — añadir transiciones + nuevos labels. 4. Backend: `npm test` (vitest) — verificar 100% tests OK. 5. Admin: `npx tsc --noEmit && npm run build`. 6. Cerrar gates.