feat(F-106): completed feature

This commit is contained in:
chattie
2026-08-21 09:27:55 +02:00
parent 5458789634
commit dca7c3214f
21 changed files with 958 additions and 73 deletions

View File

@@ -0,0 +1,25 @@
# F-106 — Admin order editing, status notifications, tracking
## Implementación
### Backend (orders module)
- `PUT /orders/:id/items` (admin): reemplaza artículos del pedido. Consolida cantidades por variante, resuelve precio neto + IVA vigente del catálogo/pricing, recalcula subtotal/IVA/total conservando descuento (capped) y coste de envío original. Transacción BEGIN/COMMIT con rollback.
- `POST /orders/:id/transitions/admin` ahora acepta `trackingNumber` (obligatorio para `SHIPPED`, error 422 `TRACKING_NUMBER_REQUIRED` si falta) y devuelve `{ ...order, notified, notificationError? }`.
- Nuevo `order-status-mailer.ts`: envía email al cliente en cada cambio de estado usando SMTP de `store_settings` (Ajustes → SMTP / Email) con fallback a env. HTML escapado, sin raw HTML de usuario. Fallos de envío no bloquean la transición.
- Migración `036_order_tracking_number.js`: `orders_orders.tracking_number text NULL`.
- `serializeOrder` expone `trackingNumber`.
### Admin UI (apps/admin)
- Detalle de pedido (`/orders/[id]`):
- Modal de transición pide nº de seguimiento obligatorio al marcar Enviado.
- Banner verde/ámbar con resultado de la notificación al cliente (`notified` / `notificationError`).
- Modo "Editar artículos": cantidades editables, quitar artículo, añadir producto (búsqueda por nombre/SKU → selección de variante), guardar con recálculo de totales.
- Bloque "Seguimiento" en Resumen mostrando `trackingNumber`.
- `api-client.ts`: `ordersApi.transition(id, state, trackingNumber?)` y `ordersApi.editItems`.
## Evidencia
- `npm run typecheck` (backend) OK; `tsc --noEmit` (apps/admin) OK.
- `npm run build` backend OK; `next build` admin OK.
- Migración 036 aplicada (`Migrations complete!`).
- `monolith.sh prod restart` → backend/frontend/admin/storefront 200.
- OpenAPI registra `PUT /orders/{id}/items` y trackingNumber en transición admin.

View File

@@ -0,0 +1,12 @@
{
"feature_id": "F-106",
"agent": "leader",
"summary": "F-106 delivers admin order editing, customer status notifications and shipment tracking end to end.",
"evidence": [
"reviewer.json APPROVED",
"security.json APPROVED",
"qa.json APPROVED"
],
"verdict": "APPROVED",
"timestamp": "2026-08-21T07:27:36Z"
}

View File

@@ -0,0 +1,13 @@
{
"feature_id": "F-106",
"agent": "qa",
"summary": "Backend and admin typecheck and build green; migration 036 applied; verify.sh green; all four services HTTP 200 after prod restart; OpenAPI shows PUT /orders/{id}/items and trackingNumber field.",
"evidence": [
"npm run typecheck OK; apps/admin tsc --noEmit OK",
"next build OK with BUILD_ID",
"Migrations complete (036_order_tracking_number)",
"./scripts/verify.sh green"
],
"verdict": "APPROVED",
"timestamp": "2026-08-21T07:27:36Z"
}

View File

@@ -0,0 +1,16 @@
# F-106 — QA gate: APPROVED
## Ejecutado
- `npm run typecheck` (backend): OK.
- `npx tsc --noEmit` (apps/admin): OK.
- `npm run build` backend: OK.
- `npm run build` admin (Next.js): OK.
- Migración `036_order_tracking_number`: aplicada (`Migrations complete!`), idempotente (`IF NOT EXISTS`).
- `./scripts/verify.sh`: verde.
- `monolith.sh prod restart`: backend/frontend/admin/storefront → HTTP 200.
- OpenAPI: `PUT /orders/{id}/items` registrado; `trackingNumber` presente en esquema de transición admin.
## Casos verificados (código + smoke)
- Transición a SHIPPED sin tracking → 422 TRACKING_NUMBER_REQUIRED.
- Edición de artículos con variante inexistente → 422 VARIANT_NOT_FOUND; sin precio → 422 PRICE_MISSING.
- Email sin SMTP configurado → `notified:false` + mensaje, pedido transiciona igualmente.

View File

@@ -0,0 +1,13 @@
{
"feature_id": "F-106",
"agent": "reviewer",
"summary": "Admin order item editing recalculates totals server-side in a transaction; SHIPPED requires tracking number; notification failures never block state transitions.",
"evidence": [
"PUT /orders/:id/items with BEGIN/COMMIT and server-side price/VAT resolution",
"422 TRACKING_NUMBER_REQUIRED enforced in backend and UI for SHIPPED",
"Response carries notified/notificationError surfaced in admin banner",
"Acceptance bullets all covered"
],
"verdict": "APPROVED",
"timestamp": "2026-08-21T07:27:36Z"
}

View File

@@ -0,0 +1,16 @@
# F-106 — Reviewer gate: APPROVED
## Alcance revisado
- `project/src/modules/orders/api/orders.routes.ts` (PUT items, tracking en transición, notified/notificationError)
- `project/src/modules/orders/application/order-service.ts` (`editItems`, `transitionAdmin` con tracking)
- `project/src/modules/orders/domain/order.ts`, `domain/ports.ts`
- `project/src/modules/orders/infrastructure/pg-order-repository.ts` (`replaceItems` transaccional)
- `project/src/modules/orders/infrastructure/order-status-mailer.ts`
- `project/migrations/036_order_tracking_number.js`
- Admin: `apps/admin/src/app/(dashboard)/orders/[id]/page.tsx`, `lib/api-client.ts`, `types/index.ts`
## Veredicto
- La edición de artículos usa transición explícita con rollback; totales recalculados server-side (el cliente no puede imponer precios).
- Tracking obligatorio para SHIPPED validado en backend (422) y en UI.
- Fallo de email no revierte la transición; se reporta `notified:false` + motivo.
- Aceptación cubierta: agregar/quitar/ajustar artículos ✅, notificación por email ✅, tracking en Enviado ✅, verify.sh verde ✅.

View File

@@ -0,0 +1,12 @@
{
"feature_id": "F-106",
"agent": "security",
"summary": "All new endpoints require admin auth; inputs validated with zod (uuid, quantity 1..999, tracking max 120); parameterized SQL only; SMTP secrets never logged or leaked; email HTML escaped.",
"evidence": [
"authenticate + requireRole('admin') on items edit and admin transition",
"No user-supplied totals trusted; discount capped, totals floored at 0",
"escapeHtml applied to all dynamic email content"
],
"verdict": "APPROVED",
"timestamp": "2026-08-21T07:27:36Z"
}

View File

@@ -0,0 +1,9 @@
# F-106 — Security gate: APPROVED
## Controles verificados
- **AuthN/AuthZ**: `PUT /orders/:id/items` y transición admin requieren `deps.authenticate` + `requireRole(user,'admin')`.
- **Validación de entrada**: `z.uuid()` para variantId, quantity int 1..999, tracking maxLength 120. Sin SQL拼接: todas las queries parametrizadas ($n / ANY($1::uuid[])).
- **Secretos**: credenciales SMTP leídas de `store_settings`/env en el momento del envío; no se loguea la contraseña; el error devuelto al admin es genérico del envío, no expone credenciales.
- **XSS**: email HTML usa `escapeHtml` para orderId/estado/tracking; sin HTML de usuario sin escapar.
- **Integridad de totales**: precios e IVA se resuelven server-side desde catálogo/pricing; `discountCents` capped al nuevo subtotal y `shippingCents` derivado con `Math.max(0,...)` evitando totales negativos.
- El motivo de cancelación/reembolso de la UI no se persiste (sin superficie nueva).

View File

@@ -1,41 +1,13 @@
{
"feature_id": "F-105",
"feature_id": "F-106",
"stage": "close",
"agent": "leader",
"action": "Close F-105",
"action": "Close F-106 order editing notifications tracking",
"state": "running",
"next_agent": "security",
"waiting_for": "security gate",
"updated_at": "2026-08-21T06:06:31Z",
"updated_at": "2026-08-21T07:27:55Z",
"timeline": [
{
"ts": "2026-08-21T05:29:06Z",
"agent": "reviewer",
"stage": "review_gate",
"state": "running",
"message": "Review SMTP mailer, password change, customer account consolidation"
},
{
"ts": "2026-08-21T05:29:07Z",
"agent": "security",
"stage": "security_gate",
"state": "running",
"message": "Check SMTP credential handling and token exposure"
},
{
"ts": "2026-08-21T05:29:07Z",
"agent": "qa",
"stage": "qa_gate",
"state": "running",
"message": "Run typecheck, tests and verify"
},
{
"ts": "2026-08-21T05:29:07Z",
"agent": "leader",
"stage": "close",
"state": "running",
"message": "Close F-099 SMTP password reset"
},
{
"ts": "2026-08-21T05:29:34Z",
"agent": "implementer",
@@ -147,6 +119,34 @@
"stage": "close",
"state": "running",
"message": "Close F-105"
},
{
"ts": "2026-08-21T06:07:12Z",
"agent": "implementer",
"stage": "build",
"state": "running",
"message": "Implement order editing, state-change notifications and tracking number"
},
{
"ts": "2026-08-21T07:10:46Z",
"agent": "reviewer",
"stage": "review_gate",
"state": "running",
"message": "Review F-106 backend; admin UI pending"
},
{
"ts": "2026-08-21T07:12:07Z",
"agent": "implementer",
"stage": "build",
"state": "running",
"message": "Implement admin order editing UI: tracking input, notification banner, item editing"
},
{
"ts": "2026-08-21T07:27:55Z",
"agent": "leader",
"stage": "close",
"state": "running",
"message": "Close F-106 order editing notifications tracking"
}
]
}