feat(F-144): completed feature

This commit is contained in:
chattie
2026-08-22 12:40:23 +02:00
parent 32489107ab
commit 2ea628fd62
14 changed files with 494 additions and 106 deletions

View File

@@ -1,30 +1,36 @@
# F-143Reporting: contracts, filters and RBAC
# F-144Historias de producto
## Estado
Diseño aprobado (ver `work/artifacts/F-143/architect.md`). Implementación backend-only.
## Reporting: snapshots de tienda, IVA, coste y envío
## Producto (alcance)
El módulo `reporting` expone el **contrato compartido** de filtros de reporte y la **matriz de permisos `REPORTING_*`** como código, para que los endpoints de reporte futuros (F-144+) consuman un parser único y estén consistentes. **No genera reportes ni lectura de datos** (esas son F-144+).
**Alcance:** backend-only. Añade las columnas mínimas de *snapshot* que el
módulo `reporting` (F-143↑, F-146 services) necesita para reportar ventas
multi-tienda sin reescribir la historia (REPORTING_ARCHITECTURE.md §5).
## Alcance (entregable)
- Schema zod reusable `reportingFiltersSchema` (`from`, `to`, `compare`, `channel`, `storeId`, `terminalId`, `cashierId`, `paymentMethodId`, `productId`, `categoryId`, `brandId`, `customerId`, `state`, `groupBy`, `page`, `pageSize`, `sort`) con validación `from<to` y semántica `[from,to)`.
- Parser `parseReportingFilters` + helper `comparisonRange` (`none | previous_equal | previous_calendar`).
- Metadato `REPORTING_FILTER_META` (campos, modos de comparación, groupBy, paginación, `dataAvailability`) servido a `GET /reporting/filters/schema`.
- Permisos backend `REPORTING_*` basados en roles (`requireReportingPermission`) con matriz `admin/editor/pos_manager/pos_cashier/customer`.
- Rutas: `GET /reporting/filters/schema` (requiere `REPORTING_VIEW`), `GET /reporting/filters/validate` (requiere `REPORTING_SALES`).
## Historias
## Fuera de alcance (F-144+)
- Lectura de datos transaccionales, agregaciones, dashboards, exportaciones, tabla de permisos en BD (F-142 §9 la marca como migración futura; hoy es role-based con los mismos call sites).
- Como **analista multi-tienda**, quiero que cada pedido (`orders_orders`)
tenga `store_id` (tienda donde se vendió), para poder filtrar y agrupar
reportes por tienda sin reescribir histórico.
- Como **reportista de márgenes**, quiero que el importe del envío
(`shipping_cents`) esté separado del `total_cents`, para poder exponer
“ventas de mercancía” y “net sales” con nombres correctos (§5.6).
- Como **analista financiero**, quiero un *snapshot* del tipo de IVA
(`vat_rate`) y del coste (`cost_at_sale_cents`) por línea, para poder
calcular margen histórico e IVA por tipo (§5.2). Estos snapshots son
**nullable**: hasta que se poblén, margen/IVA-por-tipo permanecen
`unavailable` (nunca se publican como 0).
## API (contrato HTTP)
- `GET /reporting/filters/schema``{ filterSchema, comparison, dataAvailability, permissions: { role, grants } }`. Requiere `REPORTING_VIEW`.
- `GET /reporting/filters/validate?from=...&to=...&compare=...&...``{ ok, filters, comparison: { range } }`. Requiere `REPORTING_SALES`.
## Non-goals (fuera de F-144)
- Poblar `cost_at_sale_cents`/`vat_rate` en el flujo de venta (F-146 /
reporting-service snapshots).
- Crear la tabla de payment lines ni refunds (F-145 y el roadmap §5.3-4).
- Cualquier endpoint de reporte ni cálculo de métricas (F-146).
- Resolver explícitamente la tienda en el checkout (writes app-layered) — se
usa el DEFAULT de columna como mínimo; F-146 introduce la resolución de
tienda por request.
## Seguridad (F-143 es parte de éste)
- La matriz RBAC se impone en backend (`requireReportingPermission` sobre `CurrentUser.role`). El frontend no es autoridad.
- `customer` → 0 permisos → 403 siempre.
- Futuro: migrar a tabla `backoffice_permissions` sin cambiar `requireReportingPermission(user, permission)`.
## Referencias
- Arquitectura Reporting (F-142): `docs/reporting/REPORTING_ARCHITECTURE.md` (§4§11).
- Patrones: `src/modules/pricing/api/pricing.routes.ts`, `src/shared/auth.ts`, `src/shared/http-input.ts`.
## Fuente de verdad
`orders_orders` y `orders_items` en PostgreSQL (`postgres:16`). La tienda
default (`id = 00000000-0000-0000-0000-000000000001`) es sembrada por
`043_pos_basics` y reutilizada desde `src/modules/inventory/index.ts`
(`DEFAULT_STORE_ID`).