41 lines
2.5 KiB
Markdown
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.
|