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