4.0 KiB
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:
// src/modules/orders/domain/order.ts
export const ALLOWED_TRANSITIONS: Readonly<Record<OrderState, ReadonlyArray<OrderState>>> = {
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.
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_TRANSITIONSenapps/admin/src/app/(dashboard)/orders/[id]/page.tsx:32con las mismas nuevas transiciones. - Añadir
ACTION_LABELSpara 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)
- El handler admin
/orders/:id/transitions/adminya envía email en cada transición (orders.routes.ts:271). No requiere cambios. - Cuando
SHIPPED → PROCESSING: el email se reenvía constate='PROCESSING'pero conservacourierytrackingNumberactuales del pedido en el cuerpo (verificado enbuildOrderStatusEmail— 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
- Editar
src/modules/orders/domain/order.ts— añadir transiciones hacia atrás. - Editar
src/modules/orders/tests/order-state-machine.test.ts— actualizar tests. - Editar
apps/admin/src/app/(dashboard)/orders/[id]/page.tsx— añadir transiciones + nuevos labels. - Backend:
npm test(vitest) — verificar 100% tests OK. - Admin:
npx tsc --noEmit && npm run build. - Cerrar gates.