feat(F-154): completed feature
This commit is contained in:
@@ -1,33 +1,30 @@
|
||||
# F-153 — Product Spec
|
||||
# F-154 — Admin: separate customers from internal users
|
||||
|
||||
## 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.
|
||||
El panel admin muestra usuarios mezclados. `GET /users` (módulo `users`) devuelve
|
||||
TODOS los identity_users (clientes + backoffice) y `GET /admin/users` (módulo `security`)
|
||||
por defecto también devuelve todos. La página Customers llama a `/api/users` y la página
|
||||
Users llama a `/api/admin/users`; como ambos devuelven todo, ambos listados aparecen
|
||||
mezclados (conceptos de identity/storefront con backoffice en un mismo listado).
|
||||
|
||||
## 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.
|
||||
Customers muestra SOLO clientes storefront (`role = 'customer'`); Users muestra SOLO
|
||||
usuarios internos/backoffice (`role != 'customer'`). Separación forzada en el backend
|
||||
(single source of truth), no solo filtrado cliente.
|
||||
|
||||
## 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).
|
||||
- `project/src/modules/users` (`listCustomers` / `GET /users`): filtrar `role = 'customer'`.
|
||||
- `project/src/modules/security` (`GET /admin/users`): default `role != 'customer'`;
|
||||
`?role=admin|editor` sigue afinando dentro de internos.
|
||||
- `project/apps/admin/.../users/page.tsx`: quitar opción `customer` del dropdown (Users = backoffice).
|
||||
- Tests unitarios (mock pool, sin DB) + actualizar itest AC2/AC3.
|
||||
|
||||
## 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.
|
||||
- No se crea `/customers` (el cliente ya consume `/users`).
|
||||
- `/users/:id`, `/users/:id/addresses` (owner-or-admin) siguen sin filtro por rol (un admin
|
||||
ve el perfil de cualquier usuario; un cliente ve el suyo).
|
||||
- No migración (identity_users.role ya existe, NOT NULL con default 'customer').
|
||||
- Frontend Customer page: sin cambio (ya llama /users → ahora customer-only).
|
||||
|
||||
## Risk / Priority
|
||||
- Priority: high. Risk: med (additive read-model field; backward compatible).
|
||||
## Type
|
||||
fix — high priority / high risk.
|
||||
|
||||
Reference in New Issue
Block a user