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