feat(reports): saved custom-report definitions (PMS-457 phase 1) #340
No reviewers
Labels
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
psa-systems/mokosh-server!340
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/PMS-457-saved-reports"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Adds tenant-scoped CRUD over
saved_reports, the persistence layer the SPA's report-builder UI needs to save a user's "filtered, columned, grouped, sorted" definition between sessions. The execution runtime that compiles a row into a SELECT and streams results is Phase 2 of the ticket; phase 1 just persists the workflow.Definition is JSONB on purpose:
filtersmirrors the existing ticket / billing / time-entry list filters so the Phase 2 compiler can reuse those service methods rather than hand-rolling a query compiler;columnsis an ordered list of{field, header}so reordering an export is a no-op edit at the DB layer;group_by/sortare small structured arrays.entity_typeis the discriminator the Phase 2 runtime uses to pick which service to compile against (tickets / time_entries / invoices / ...). VARCHAR(50) rather than an enum so a new entity (assets / projects) does not need a CHECK-widening migration; the runtime rejects unknown values at execution time.Visibility model, pinned by
tests/saved_reports.rs:?scope=mineor default?scope=any);is_shared=truereports are visible to every user in the same tenant via the default scope and?scope=shared;?entity_type=narrows the list so an entity-page sidebar can offer "saved reports targeting this entity" in one round trip.Phase 2 (PMS-457 follow-up):
saved_reportsrow + a tenant and runs the underlying SELECT (paginated, streamed);/api/v1/reports/saved/{id}/executeendpoint that returns the result set;Phase 3 (
scheduled_reports): cron + delivery target referencing asaved_reports.id. Out of scope here.#PMS-457
Adds tenant-scoped CRUD over `saved_reports`, the persistence layer the SPA's report-builder UI needs to save a user's "filtered, columned, grouped, sorted" definition between sessions. The execution runtime that compiles a row into a SELECT and streams results is Phase 2 of the ticket; phase 1 just persists the workflow. Definition is JSONB on purpose: - `filters` mirrors the existing ticket / billing / time-entry list filters so the Phase 2 compiler can reuse those service methods rather than hand-rolling a query compiler; - `columns` is an ordered list of `{field, header}` so reordering an export is a no-op edit at the DB layer; - `group_by` / `sort` are small structured arrays. `entity_type` is the discriminator the Phase 2 runtime uses to pick which service to compile against (tickets / time_entries / invoices / ...). VARCHAR(50) rather than an enum so a new entity (assets / projects) does not need a CHECK-widening migration; the runtime rejects unknown values at execution time. Visibility model, pinned by `tests/saved_reports.rs`: - the author always sees their own reports (`?scope=mine` or default `?scope=any`); - `is_shared=true` reports are visible to every user in the same tenant via the default scope and `?scope=shared`; - only the AUTHOR may mutate or delete a report; a non-author hitting PATCH/DELETE on a shared row gets 404 (not 403), matching the absent-or-off-limits posture used elsewhere in the API; - `?entity_type=` narrows the list so an entity-page sidebar can offer "saved reports targeting this entity" in one round trip. Phase 2 (PMS-457 follow-up): - the actual compiler that takes a `saved_reports` row + a tenant and runs the underlying SELECT (paginated, streamed); - a `/api/v1/reports/saved/{id}/execute` endpoint that returns the result set; - export-to-CSV via the same compile path. Phase 3 (`scheduled_reports`): cron + delivery target referencing a `saved_reports.id`. Out of scope here. #PMS-457