Files
mercadodevida/spec/tech.md
2026-08-22 11:43:42 +02:00

43 lines
2.8 KiB
Markdown

# F-143 — Technical spec
## Module layout
```text
src/modules/reporting/
domain/filters.ts — pure types (ReportingFilters, ComparisonMode, GroupBy, ComparisonRange, DataAvailability, ReportingFilterMeta)
domain/permissions.ts — ReportingPermission + REPORTING_ROLE_PERMISSIONS + requireReportingPermission (imports shared/auth + shared/errors)
application/filters.ts — reportingFiltersSchema (zod), parseReportingFilters, comparisonRange, REPORTING_FILTER_META (imports domain)
api/reporting.routes.ts — registerReportingRoutes(app, deps:{authenticate}) (imports application + domain + shared)
index.ts — public surface (re-exports only)
tests/filters.test.ts — unit: parser + comparisonRange
tests/permissions.test.ts— unit: role matrix + requireReportingPermission
api/reporting.routes.test.ts — HTTP: schema/validate + RBAC (mirror security.routes.test.ts)
```
## Boundaries (R1/R2)
- `reporting` importa SOLO `shared/*` + `zod`**ningún otro módulo**. ✓ R1.
- `src/app/build-app.ts` importa `registerReportingRoutes` (+ tipos) desde `modules/reporting/index.js` (R2). ✓.
- Registrado dentro de `if (deps.pool)` con `authenticate: combinedAuth` (backplane backoffice), junto al resto de módulos backoffice.
## Filter contract (F-142 §6)
- Rango `[from,to)`: `from` inclusivo, `to` exclusivo → evita doble conteo.
- `from`/`to`: ISO datetime with offset → `z.string().datetime({ offset: true })`.
- Arrays repetibles de UUID aceptan single OR array vía `z.preprocess((v)=>Array.isArray(v)?v:v===undefined?undefined:[v], z.array(z.uuid()).optional())`.
- `compare` default `none`; `channel` default `all`; `page` (1..); `pageSize` (1..200, default 50).
- `from > to` → refine → AppError(400) (mapeado por `parseJson`).
## Comparison (`comparisonRange`)
- `none``null`.
- `previous_equal` → shift ventana atrás por la duración exacta (`[start-duration, start)`).
- `previous_calendar` → shift atrás por los días calendario transcurridos, alineado a UTC (`00:00`) → `[prevStart, prevStart+spanDays)`. Documented como aproximación calendar-aligned.
## RBAC (role-based, F-142 §9)
- admin → todos los `REPORTING_*`.
- editor → VIEW+SALES+PRODUCTS+CUSTOMERS+INVENTORY+DISCOUNTS+REFUNDS+TAXES (sin FINANCIAL/EXPORT/ADMIN).
- pos_manager → VIEW+SALES+PAYMENTS+CASH.
- pos_cashier → VIEW+SALES.
- customer → [] (403).
- `requireReportingPermission(user, permission)` lanza AppError(403). Futuro: tabla `backoffice_permissions`; la firma no cambia.
## Data availability (F-142 §4 baseline, server-truth)
`grossSales/discounts/tax/unitsSold/orders/customers = available`; `netSales/margin/paymentMethod/refunds/shipping = unavailable`. Se expone via `REPORTING_FILTER_META.dataAvailability` (no cálculos aún — F-144+).