Files
mercadodevida/work/artifacts/F-113/architect.md
2026-08-21 12:27:13 +02:00

41 lines
2.5 KiB
Markdown

# F-113 — Arquitectura: email en procesando/enviado con tracking y courier editable
## Descubrimiento clave
El mailer de estado ya existe (`order-status-mailer.ts`, F-106) y se dispara en
`POST /orders/:id/transitions/admin`. PERO la UI admin (`apps/admin`) llama a la ruta de
cliente `POST /orders/:id/transitions`, que **no** envía email, ignora `trackingNumber` y
devuelve 404 si el backoffice no es el dueño del pedido. F-113 conecta la UI admin con la
ruta admin correcta para que el email realmente salga en procesando/enviado.
## Decisiones
1. **Courier persistido**: migración 039 añade `orders_orders.courier varchar(120) NULL`.
Se propaga por dominio (`Order.courier`), repositorio, `updateState`/`transitionAdmin`
(firma `courier?: string`), servicio y `serializeOrder`.
2. **Lista editable de couriers**: se guarda como JSON array en
`store_settings.shipping_couriers`. `GET/PATCH /admin/settings` exponen `couriers: string[]`
(default si no existe: Correos, SEUR, MRW, GLS, DHL, UPS). Validación zod:
array ≤30 items, cada string 1..60.
3. **SHIPPED exige tracking y courier**: en la ruta admin, `state==='SHIPPED'` requiere
`trackingNumber` (ya existía, 422 TRACKING_NUMBER_REQUIRED) y ahora también `courier`
(422 COURIER_REQUIRED). Ambos se pasan al mailer.
4. **Mailer**: `sendOrderStatusEmail` acepta `courier?: string | null`. En el cuerpo
(texto y HTML) del email, si hay courier se añade línea "Transportista: X" junto a
"Número de seguimiento: Y". Escape HTML ya presente.
5. **Email en procesando y enviado**: como la UI admin ya usa `transition()` para todos los
estados y la ruta admin envía email en cada transición, apuntar la UI a la ruta admin
garantiza email en PROCESSING y SHIPPED (y el resto). No se añade lógica de envío nueva,
solo se corrige el endpoint consumido.
## Admin UI (apps/admin)
- `api-client.ts`: `ordersApi.transition(id, state, trackingNumber?, courier?)`
`POST /api/orders/{id}/transitions/admin` (envía courier si está presente).
`StoreSettings.couriers?: string[]`.
- `types/index.ts`: `Order.courier?: string | null`.
- Página de pedido: al confirmar `SHIPPED`, mostrar selector de courier (desde ajustes)
además del tracking; ambos obligatorios. Mostrar courier en el detalle.
- Ajustes: nueva pestaña "Transportistas" con textarea (uno por línea) que edita la lista.
## Fuera de alcance
- No rediseñar los demás emails de estado.
- Sin integración con APIs externas de transportistas.