feat(notif-inventory-alerts): admin bell: add inventory alerts (low stock, out of stock, expired/expiring, low margin)
This commit is contained in:
99
work/artifacts/NOTIF-INVENTORY-ALERTS/documenter.md
Normal file
99
work/artifacts/NOTIF-INVENTORY-ALERTS/documenter.md
Normal file
@@ -0,0 +1,99 @@
|
||||
# NOTIF-INVENTORY-ALERTS — Documenter notes
|
||||
|
||||
> Documenter: document · Estado: ready for close
|
||||
|
||||
## Cambio de contrato
|
||||
|
||||
### Nuevo endpoint
|
||||
|
||||
**`GET /api/inventory/admin/notifications`**
|
||||
|
||||
- **Auth:** requiere rol `admin` (HTTP 403 si no).
|
||||
- **Response:**
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"items": [
|
||||
{ "id": "low_stock", "count": 7, "href": "/inventory?filter=low_stock", "label": "Stock bajo", "tone": "amber" },
|
||||
{ "id": "out_of_stock", "count": 3, "href": "/inventory?filter=out_of_stock", "label": "Sin stock", "tone": "red" },
|
||||
{ "id": "expired", "count": 2, "href": "/inventory?filter=expired", "label": "Caducados", "tone": "red" },
|
||||
{ "id": "expiring_soon", "count": 5, "href": "/inventory?filter=expiring", "label": "Por caducar (≤30d)", "tone": "amber" },
|
||||
{ "id": "low_margin", "count": 4, "href": "/inventory?filter=low_margin", "label": "Margen <30%", "tone": "red" }
|
||||
],
|
||||
"total": 21
|
||||
}
|
||||
```
|
||||
|
||||
- **Reglas:**
|
||||
- Solo se incluyen items con `count > 0`.
|
||||
- `total` es la suma de los counts de items incluidos.
|
||||
- Items NO incluidos en respuesta cuando su count es 0 (no aparecen como `{count: 0}`).
|
||||
|
||||
### Cambio en filtro existente
|
||||
|
||||
`GET /api/inventory/admin/overview?filter=...` ahora acepta `expired` además
|
||||
de los filtros anteriores (`all`, `in_stock`, `low_stock`, `out_of_stock`,
|
||||
`expiring`, `low_margin`).
|
||||
|
||||
- **`expired`:** productos activos cuya `expiration_date < current_date`.
|
||||
Ordenados por `expiration_date DESC NULLS LAST` (caducados más recientes primero).
|
||||
|
||||
### Cambio en UI
|
||||
|
||||
`OrderNotifications` (campanita del header admin) ahora tiene 3 tabs en lugar
|
||||
de 2:
|
||||
|
||||
| Tab | Icono | Color del badge | Datos |
|
||||
|---|---|---|---|
|
||||
| Pendientes | 📦 | amber | stale orders (PENDING/SHIPPED >24h) |
|
||||
| Esperando pago | 💳 | purple | orders AWAITING_PAYMENT |
|
||||
| **Inventario** | 🏷️ | emerald | alertas operativas de inventario (este feature) |
|
||||
|
||||
- Solo visible para `user.role === 'admin'`.
|
||||
- Polling cada 5 min vía `Promise.all([staleNotifications, awaitingPaymentNotifications, inventoryApi.notificationCounts])`.
|
||||
- Badge total suma los 3 totales.
|
||||
|
||||
## Quién se ve afectado
|
||||
|
||||
- **Operador admin del backoffice:** ahora ve alertas de inventario en la campanita sin entrar a `/inventory`.
|
||||
- **No-admin (customer, cashier):** sin cambios (no ven la campanita).
|
||||
- **API consumers externos:** nuevo endpoint disponible (auth admin). Sin breaking change en endpoints existentes.
|
||||
|
||||
## Comportamiento observable
|
||||
|
||||
| Escenario | Antes | Después |
|
||||
|---|---|---|
|
||||
| Admin abre campanita | 2 tabs (Pendientes, Esperando pago) | 3 tabs (Pendientes, Esperando pago, **Inventario**) |
|
||||
| Producto caduca | Sin notificación visible | Aparece en tab Inventario como "Caducados" (rojo), badge se incrementa |
|
||||
| Producto entra en stock bajo (<5) | Sin notificación visible | Aparece en tab Inventario como "Stock bajo" (amber), badge se incrementa |
|
||||
| Producto baja de margen 30% | Sin notificación visible | Aparece en tab Inventario como "Margen <30%" (rojo), badge se incrementa |
|
||||
| Click en cualquier alerta de inventario | n/a | Navega a `/inventory?filter=...` con la lista filtrada |
|
||||
|
||||
## Notas operacionales
|
||||
|
||||
- **Umbrales hardcoded:** 5 unidades (low_stock), 30 días (expiring), 30% (low_margin). Consistente con los filtros existentes. Si en el futuro el operador pide flexibilidad, se mueven a `store_settings` en otro ticket.
|
||||
- **Polling:** 5 minutos. Si la campanita está abierta, el polling refresca los 3 endpoints. Latencia añadida: <50ms (3er endpoint).
|
||||
- **No hay emails:** las alertas son solo in-app. Si en el futuro se piden emails, sería un nuevo ticket en el módulo `notifications`.
|
||||
|
||||
## Notas de release (sugeridas)
|
||||
|
||||
```
|
||||
feat(notif-inventory-alerts): admin bell shows inventory alerts (low stock, out of stock, expired, expiring, low margin)
|
||||
|
||||
Adds GET /api/inventory/admin/notifications (admin role) with 5 count types.
|
||||
Extends OrderNotifications header bell with third tab "Inventario".
|
||||
Extends inventory filter enum with 'expired' (expiration_date < current_date).
|
||||
5/5 unit tests for the new endpoint; tsc + next build green.
|
||||
```
|
||||
|
||||
## Cross-references
|
||||
|
||||
- architect.md §2 — diseño de las 3 piezas (backend, helper, frontend).
|
||||
- implementer.md §3 — detalle de los cambios y evidencia.
|
||||
- reviewer.json — 10/12 checks PASS + 2 PASS_WITH_NOTE.
|
||||
- security.json — 10/10 checks PASS.
|
||||
- qa.json — 8/9 AC + 4/4 integration + 3/3 regression.
|
||||
|
||||
## Próximo stage
|
||||
|
||||
→ **close (leader)**: ejecutar `scripts/close_feature.py NOTIF-INVENTORY-ALERTS`.
|
||||
Reference in New Issue
Block a user