Files
mercadodevida/work/artifacts/F-125/architect.md
2026-08-21 17:58:15 +02:00

79 lines
4.0 KiB
Markdown

# 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<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.
```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.