65 lines
3.3 KiB
Markdown
65 lines
3.3 KiB
Markdown
# 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 `"<from>><to>"`. 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. |