feat(F-153): completed feature
This commit is contained in:
@@ -1,32 +1,33 @@
|
||||
# F-152 — Product Spec
|
||||
# F-153 — Product Spec
|
||||
|
||||
## Problema
|
||||
Los clientes no reciben confirmación por email ni al crear la cuenta ni cuando su
|
||||
pedido se confirma tras el pago:
|
||||
- `POST /auth/register` crea el usuario pero no envía email de bienvenida.
|
||||
- El webhook de Stripe (`PaymentSucceeded`) pasa el pedido a `PAID` pero no
|
||||
notifica al cliente (el `OrderEventPublisher` inyectado en payments es no-op y
|
||||
`sendOrderStatusEmail` solo se llamaba desde transiciones admin).
|
||||
## Problem
|
||||
The order read model and its serialization (`serializeOrder`) do **not** expose
|
||||
the linked customer's email. `orders_orders.user_id` references `identity_users`
|
||||
(whose `email citext NOT NULL UNIQUE` always exists), but the order view carries
|
||||
only `userId` — never the email. Consequence:
|
||||
- Order detail (`/orders/:id`, `/orders/:id/admin`) and the admin order list
|
||||
(`/orders`) never display the customer email ("customer email missing ... displayed").
|
||||
- The admin force-transition (`POST /orders/:id/transitions/admin`) works around
|
||||
this with a fragile inline `SELECT email FROM identity_users WHERE id =
|
||||
order.userId`, which surfaces "El cliente no tiene email asociado" whenever the
|
||||
view itself doesn't carry the association.
|
||||
|
||||
## Objetivo
|
||||
Que los clientes reciban los dos emails transaccionales esenciales:
|
||||
1. **Welcome** al crear la cuenta (account_created).
|
||||
2. **Order confirmation** cuando el pago se confirma (PaymentSucceeded → PAID),
|
||||
reenviando el flujo ya existente de `sendOrderStatusEmail` (SMTP desde
|
||||
*Ajustes → SMTP / Email*).
|
||||
## Goal
|
||||
Associate the linked customer's email to the **order read model** and display it in
|
||||
serialization — detail, admin list, and the admin force-transition notification —
|
||||
using the order view as the single source of truth.
|
||||
|
||||
## Usuarios
|
||||
- Usuario principal: cliente que se registra / compra en la tienda.
|
||||
- Usuario secundario: operador (Ajustes SMTP) y admin (vee historial).
|
||||
## Scope IN
|
||||
- `orders/domain`: add `email` to `OrderView` (read model).
|
||||
- `orders/infrastructure` (pg-order-repository): JOIN `identity_users` to resolve
|
||||
`email` on every order read (`findById`, `findByIdAndUserId`, `findAll`, `search`).
|
||||
- `orders/api` (orders.routes): surface `email` in `serializeOrder` and consume
|
||||
`order.email` in the admin force-transition notification (removing the inline lookup).
|
||||
|
||||
## Alcance v1
|
||||
- In scope:
|
||||
- Welcome email on `POST /auth/register` (best-effort, nunca bloquea el registro).
|
||||
- Order confirmation email on `PaymentSucceeded` webhook (best-effort, nunca
|
||||
rompe la reconciliación de pagos).
|
||||
- Reusar el SMTP configurado en `store_settings` ya usado por admin transitions.
|
||||
- Tests unitarios de cuerpo/email y de best-effort.
|
||||
- Out of scope:
|
||||
- Verificación por enlace (gating de cuenta por email) — queda como hardening.
|
||||
- Reenvío de emails ya enviados (idempotencia garantizada por el webhook).
|
||||
- Cambiar el email de transición admin existente.
|
||||
## Scope OUT
|
||||
- No changes to the `identity` domain (no TS cross-import).
|
||||
- No new tables / migrations: `identity_users.email` already exists and is NOT NULL.
|
||||
- No new endpoints; no auth/RBAC change; no order state machine change.
|
||||
|
||||
## Risk / Priority
|
||||
- Priority: high. Risk: med (additive read-model field; backward compatible).
|
||||
|
||||
Reference in New Issue
Block a user