3.4 KiB
3.4 KiB
F-143 — API contract documentation
Resumen del cambio (user-facing)
New backoffice Reporting module delivering the shared filter contract + RBAC foundation (F-142 §3/§6/§9). No report data is read here (F-144+ owns report data); GET /reporting/filters/schema only introspects the contract. Two new endpoints, both backoffice-only (backoffice_session or storefront mdv_session via combinedAuth):
Nuevas rutas
| Method | Path | Auth (authenticate) | Permission | Body / Query | 200 response |
|---|---|---|---|---|---|
| GET | /reporting/filters/schema |
combinedAuth (authenticated) |
REPORTING_VIEW |
n/a | { filterSchema, comparison, dataAvailability, permissions } |
| GET | /reporting/filters/validate |
combinedAuth (authenticated) |
REPORTING_SALES |
?from&to&compare?… (see below) |
{ ok, filters, comparison: { range } } |
RBAC
Permissions are role-based (no permission table yet — F-142 §9 "compatibilidad inicial"):
admin— all reporting permissions (incl.REPORTING_FINANCIAL,REPORTING_EXPORT).editor— VIEW + SALES + PRODUCTS + CUSTOMERS + INVENTORY + DISCOUNTS + REFUNDS + TAXES.pos_manager— VIEW + SALES + PAYMENTS + CASH.pos_cashier— VIEW + SALES.customer— none (403 on both routes).
A future backoffice_permissions migration keeps the requireReportingPermission(user, perm) call-site signature.
Filter schema (shared contract)
Query params parsed by reportingFiltersSchema via parseJson (→ 400 VALIDATION_ERROR on bad input):
from(string, required) — ISO 8601 datetime, inclusive start.to(string, required) — ISO 8601 datetime, exclusive end.from < toenforced (400 on inversion).compare— enumnone | previous_equal | previous_calendar(defaultnone).channel— enumall | ecommerce | pos | admin(defaultall).storeId,terminalId,cashierId,paymentMethodId,productId,categoryId,brandId— repeatable UUID (single value or repeated).customerId— single UUID (optional; walk-in POS has none).state— repeatable string (e.g.PAID,SHIPPED).groupBy— enumday|week|month|hour|store|channel|terminal|cashier|payment.page(int, default 1),pageSize(int, 1..200, default 50).sort— string (e.g.-revenue).
Comparison range (comparisonRange, [from,to))
none→comparison.range = null.previous_equal→ exact-duration mirror; prior window ends exactly atrange.from.previous_calendar→ UTC-aligned prior window ofceil(span_days)days.
dataAvailability (metadata only — F-142 §4 baseline)
Never converted to 0. Currently:
available:grossSales,discounts,tax,unitsSold,orders,customers.unavailable:netSales,margin,paymentMethod,refunds,shipping.
Errores
401— unauthenticated (authenticaterejects).403— authenticated but lacks the route permission.400— malformed query (zod →VALIDATION_ERROR), incluyendofrom >= toe UUIDs inválidos.
Implementación
project/src/modules/reporting/(domain/application/api + tests + index).project/src/app/build-app.ts—registerReportingRoutesregistered insideif (deps.pool)withcombinedAuth. No migrations, no DB schema or data-table changes in this ticket.