Files
mercadodevida/work/artifacts/F-143/implementer.md
2026-08-22 11:43:42 +02:00

4.3 KiB
Raw Permalink Blame History

F-143 — Implementer evidence

What

Establishes the shared reporting filter contract + RBAC foundation (reporting module, backend-only, no DB reads, no migration). New routes GET /reporting/filters/schema (REPORTING_VIEW) and GET /reporting/filters/validate (REPORTING_SALES); zod reportingFiltersSchema ([from,to) semantics), comparisonRange helper, and a role→permission map (REPORTING_ROLE_PERMISSIONS) + requireReportingPermission. Wired in build-app.ts inside if (deps.pool) with combinedAuth.

Design recap (architect-approved — see architect.md)

  • reporting/ layered api→application→domain (mirrors pricing): pure types domain/filters.ts (const arrays REPORTING_COMPARISON/CHANNELS/GROUP_BY derive their union types DRY); zod schema + parser + comparisonRange + REPORTING_FILTER_META in application/filters.ts; registerReportingRoutes in api/reporting.routes.ts; public surface in index.ts.
  • From/to are z.string().refine(!isNaN(Date.parse)) (zod-v4-safe; .datetime() API moved in zod v4). Range semantics [from,to) via refine(from < to) → 400 on inversion. Repeatable UUID fields use z.preprocess (single string ↔ array) because Fastify querystring yields string vs array.
  • RBAC: no permission table exists → role→permission map + requireReportingPermission(user, perm) (future table migration keeps call-site signature). FINANCIAL/EXPORT admin-only; editor/pos_* least-privilege.
  • dataAvailability is metadata only (F-142 §4 baseline); never converts unavailable→0; no report data computed (F-144+).
  • Routes have NO 200 response schema (broad passthrough, matching security.routes.ts precedent) so the dynamic DTO isn't stripped by fast-json-stringify; error responses use the shared errorSchema.

Files created

  • project/src/modules/reporting/domain/filters.ts — const arrays + pure types (ReportingFilters, ReportingFilterMeta, DateRange, ComparisonRange, DataAvailability, etc.).
  • project/src/modules/reporting/domain/permissions.tsReportingPermission, REPORTING_ROLE_PERMISSIONS: Record<Role, ReportingPermission[]>, requireReportingPermission, userReportingPermissions.
  • project/src/modules/reporting/application/filters.tsreportingFiltersSchema, parseReportingFilters, comparisonRange, REPORTING_FILTER_META, REPORTING_PAGE_SIZE_MAX.
  • project/src/modules/reporting/api/reporting.routes.tsregisterReportingRoutes + ReportingRoutesDeps { authenticate }.
  • project/src/modules/reporting/index.ts — public re-exports.
  • project/src/modules/reporting/tests/filters.test.ts, tests/permissions.test.ts, api/reporting.routes.test.ts.
  • EDIT project/src/app/build-app.tsimport { registerReportingRoutes } from '../modules/reporting/index.js' + register block inside if (deps.pool) using combinedAuth.

Tests

  • NEW reporting/tests/filters.test.ts (9): defaults, repeatable uuid (single+array), from>=to→throw, invalid datetime, pageSize ceiling, invalid uuid→throw, comparisonRange (none/previous_equal/previous_calendar).
  • NEW reporting/tests/permissions.test.ts (6): admin all-grants, pos_cashier least-privilege, customer none, requireReportingPermission 403 for denied, FINANCIAL admin-only (AC6), matrix covers every role.
  • NEW reporting/api/reporting.routes.test.ts (7): schema route admin 200 + contract; customer 403 (×2); validate 200 + comparison invariants; defaults; inverted→400; missing→400; customer 403. Mirrors security.routes.test.ts (minimal Fastify + mock authenticate, no DB) and installs the build-app errorHandler/serializer so AppError(400/403) surface as real status codes.

Verification

  • npx tsc --noEmit0 errors (strict, noUncheckedIndexedAccess).
  • npx vitest run (full suite) → 229 passed | 57 skipped (DB itests skipped w/o TEST_DATABASE_URL); reporting contributes +22 (15 unit + 7 route); 0 regressions vs F-138 baseline (209 passed).
  • node project/scripts/check-module-boundaries.mjs project/src0 NEW violations for reporting/ (imports only shared/* + zod). The sole remaining violation (security/routes.ts → log-broadcaster, an R1 deep-import) is pre-existing (introduced by F-154), untouched by F-143 — confirmed out of scope.
  • ./scripts/verify.sh → green (F-143 in_progress is runtime-consistent; no pending/done mismatch).