View Mode
View Mode is the read-only window a user gets onto the stores below their
own in the supply-chain hierarchy (parentStoreId — see
Data Model). A district user can look at their facilities,
a province user their whole province, a Central Warehouse user anything. It
is not a separate service — it's a target-org-unit parameter on endpoints
that would otherwise run against the caller's own store.
Two backends, one rule
Reports go through the generic report engine.
POST /generic-report-profiles/{id}/execute takes an optional
organizationUnitId; resolveReportOrgTarget()
(generic-report-org-target.ts) resolves it:
- omitted, or equal to your own org → runs against your own org, unchanged;
- another org unit → allowed only when
orgUnitIsAtOrBelow(target, caller)is true, else403.
That one function is the whole authorisation story for report View Mode.
?format=csv and ?format=pdf honour the same parameter, so a View Mode
export carries the target store's name, not the caller's.
Order / issuing / stock browsing goes through ReadController
(/read/*). Every method there is:
@RequirePermissions(Permission.VIEW_REPORTS);- guarded by
ReportStoreScopeGuard, which rejects astorequery param that isn't the caller's own org or one below it (sameat-or-belowcheck,403).
| Endpoint | Backs |
|---|---|
GET /read/viewStoreUnderUnit | the "Viewing as" store selector — every org unit below store |
GET /read/viewStoreStock, GET /read/viewStoreStockByProduct | the View Mode Overview stock cards |
GET /read/ViewCurrentSocks, GET /read/getMyInventory | the View Mode Inventory screen |
GET /read/ViewRequesition, ViewDistribution, ViewReturn, ViewDispensing | the View Mode order / issuing browsing lists |
The four viewStore* / snapshot endpoints take ViewStoreSnapshotDto
(store + optional productId, no date range — they return current
on-hand stock). The four order-history ones take ViewDateRangeDto.
Why there are no write endpoints
There is no "act as another store" API, by design. Every mutating
order / issuing / return method checks X.Organization.id === user.organization.id and throws otherwise (see
Order Lifecycle). The frontend's View Mode screens
are correspondingly stripped of every submit / approve / acknowledge / add
control — earlier versions rendered them, but they only ever produced a
400. To act on a store's behalf you need an account at that store.
History
ReadController used to also carry 8 report-only endpoints
(ViewArrivalReport, ViewIssueReport, ViewWastageReports, …). They were
removed once every View Mode report page moved onto the generic engine —
the engine's organizationUnitId does the same job for any report profile,
seeded or custom. If you find a reference to /read/View*Report anywhere,
it's stale.