# F-125 — Transiciones hacia atrás en pedidos + reenviar email ## Cambios ### `src/modules/orders/domain/order.ts` State machine ampliado con transiciones hacia atrás (un paso): ```diff export const ALLOWED_TRANSITIONS = { PENDING: ['AWAITING_PAYMENT', 'CANCELLED'], AWAITING_PAYMENT: ['PAID', 'CANCELLED'], - PAID: ['PROCESSING', 'SHIPPED', 'CANCELLED', 'REFUNDED'], + PAID: ['PROCESSING', 'SHIPPED', 'CANCELLED', 'REFUNDED'], // unchanged - PROCESSING: ['SHIPPED', 'CANCELLED', 'REFUNDED'], + PROCESSING: ['PAID', 'SHIPPED', 'CANCELLED', 'REFUNDED'], // + PAID (backward) - SHIPPED: ['DELIVERED', 'PARTIALLY_REFUNDED'], + SHIPPED: ['PROCESSING', 'DELIVERED', 'PARTIALLY_REFUNDED'], // + PROCESSING (backward) - DELIVERED: ['PARTIALLY_REFUNDED'], + DELIVERED: ['SHIPPED', 'PARTIALLY_REFUNDED'], // + SHIPPED (backward) CANCELLED: [], REFUNDED: [], PARTIALLY_REFUNDED: [], }; ``` Terminales (CANCELLED, REFUNDED, PARTIALLY_REFUNDED) **siguen terminales** — un reembolso no se puede deshacer. ### `src/modules/orders/tests/order-state-machine.test.ts` - Test renombrado: "rejects SHIPPED back to PENDING (too far)" — el comportamiento de rechazo sigue siendo correcto (PENDING está demasiado lejos). - Test nuevo: "allows one-step backward transitions (F-125)" — verifica `PROCESSING→PAID`, `SHIPPED→PROCESSING`, `DELIVERED→SHIPPED`. ### `apps/admin/src/app/(dashboard)/orders/[id]/page.tsx` - Mismo `ALLOWED_TRANSITIONS` duplicado (frontend) actualizado con las nuevas transiciones. - `ACTION_LABELS` reemplazado por `ACTION_LABELS_BY_TRANSITION` keyed por `">"`. Esto permite que el mismo target (p. ej. `PROCESSING`) tenga label distinto según origen: - `PAID > PROCESSING`: "Procesar pedido" - `SHIPPED > PROCESSING`: "Revertir a En preparación" - Función helper `actionLabel(from, to)` que devuelve el label del par o fallback `"Pasar a {to}"`. - Dos usos actualizados: `{actionLabel(currentState, next)}` (botón) y `{actionLabel(currentState, showConfirm)}` (modal de confirmación). ### Email Sin cambios en backend de email. El handler `POST /orders/:id/transitions/admin` ya envía email en cada transición con `courier` y `trackingNumber` actuales del pedido (ver `orders.routes.ts:271` y `buildOrderStatusEmail` en `order-status-mailer.ts`). Cuando `SHIPPED → PROCESSING`: - El email se reenvía con `state='PROCESSING'` - El bloque "Transportista: X / Número de seguimiento: Y" sigue apareciendo porque `courier` y `trackingNumber` siguen guardados en DB y se incluyen en el body si están presentes (independiente del estado). ## Verificación ### Tests - `npx vitest run src/modules/orders/tests/order-state-machine.test.ts` → **5/5 pass** (incluye el nuevo test de transiciones hacia atrás). ### Build - `cd apps/admin && npx tsc --noEmit` → exit 0. - `cd apps/admin && npm run build` → exit 0. ### Evidencia manual Con pedidos existentes: - PROCESSING → botón "Marcar como Enviado" + botón **nuevo** "Revertir a Pagado" - SHIPPED → botón "Marcar como Entregado" + botón **nuevo** "Revertir a En preparación" - DELIVERED → botón "Reembolso parcial" + botón **nuevo** "Revertir a Enviado" ## Notas - El reenvío de email ya estaba implementado (F-113) — solo había que permitir las transiciones. - El operador debe reiniciar admin (`./scripts/monolith.sh prod restart`) para desplegar la nueva UI.