MEGAINFRA-WEBAPP/docs/setup/user-access.md
DEls 0857e56cb3 fix(workforce): let site managers add and rename employers and brokers
The employer and broker API routes were never named in the path
classifier, so they fell through to its admin-only default and the
proxy rejected a site manager with a bare {"error":"Not allowed"}
before the route handler ran. The handler's own site scoping was
always correct — it just never got the chance to apply.

- classify /api/employers as dashboard and map it to the workforce
  module, mirroring /api/employees
- let the site dropdown be chosen when adding an employer; only a
  rename freezes the site, which preserves assignment history
- cover the rename path, which had no route test, and the proxy
  gate for the site-manager role
2026-10-19 16:33:41 +02:00

109 lines
5.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# User access
Named PocketBase sign-in is required in every environment. Shared PIN sign-in
is disabled, even if an old environment file still contains `AUTH_MODE=pin`.
| Account type | App access | Site scope | User settings |
| --- | --- | --- | --- |
| Admin | Dashboard, Management, Ask, Clock | All sites | Manage users |
| Site manager | Operational dashboard and Clock | Assigned sites | Create and list clock accounts within assigned sites |
| Clock user | Clock only: worker/fleet scans and document uploads | Assigned sites | None |
| Office user | Selected dashboard sections with full actions | Assigned sites or All sites | None |
## Management planning
Management sits below Overview in the navigation. Its Turnover Overview,
Project Planning, Retention and Year Comparison pages require a named admin
account. Operational Site manager accounts and Office accounts have no access,
including those with all sites or every operational section enabled. Future
pages under `/dashboard/management/` inherit this restriction.
The initial data comes from `src/lib/management/workbook-snapshot.json`,
extracted from the supplied turnover workbook. `data.ts` checks access before
loading it; `model.ts` calculates results independently. Replace the provider's
source when connecting the planning database. Never import the snapshot into
a client component or put it in `public/`.
The overview preserves the workbook's reported monthly income and cost
summaries, including its manual values. Project detail remains separate and
the reconciliation shows the differences. All amounts are ZAR. The main plan
follows March–February headings from the workbook titled 2025, without
inventing exact reporting years. Retention releases have their own supplied
November 2025–October 2026 dates. Year Comparison follows the source's actual
January–December 2024 and 2025 columns, including its estimates. There is no
live actuals feed or planning editor yet.
## Office setup
In Settings · Users, choose Office user, then an access preset (Workforce,
Financial, or Plant) or select sections individually. At least one section is
required. Each section includes its overview, details, imports, corrections,
and future pages under the same section. Selecting several sections gives the
user all of those sections. Overview is the company dashboard overview;
Workforce's own overview is included with Workforce.
Choose All sites for head-office work spanning the company. This includes new
sites automatically. Turn it off to select specific sites. Site lookup names
and the site selector still work within enabled sections; this does not grant
access to the Sites management section. Ask and user administration remain
unavailable to Office users.
For Julius, after deploying this version, edit his existing account:
- Account type: Office user.
- Preset: Workforce (only Workforce selected).
- Site scope: All sites if his remit covers the company; otherwise select his sites.
He starts on Workforce overview and sees Workers, Timesheets, and On site now.
Worker details, rates, sick notes, calendars, timesheet imports/upload status,
and missed-clock-out corrections inherit Workforce access. Other section URLs
redirect him to his first enabled section, and other section APIs return 403.
## Schema rollout
Existing account roles, passwords, and sites are preserved. The additive
migration adds `office_user` to `users.role`, multi-select `users.modules`
(`overview`, `sites`, `workforce`, `plant`, `financial`), and Boolean
`users.all_sites`. Existing non-office accounts retain their role defaults;
office fields do not grant extra access to clock or site-manager accounts.
Run the dry run against the configured PocketBase environment:
```bash
python3 scripts/migrations/2026-10-07-office-access.py --env-file .env.local
```
Apply before deploying Office user settings, passing the exact URL printed by
the dry run as `--confirm-host`:
```bash
python3 scripts/migrations/2026-10-07-office-access.py --env-file .env.local --apply --confirm-host "$POCKETBASE_URL"
```
This changes only the users collection fields. It does not convert Julius or
any other existing account. Convert accounts after deploying, because the old
app rejects the new Office user role.
## Extending sections
`src/lib/auth/path-access.ts` is the shared page/API policy used by the proxy,
navigation, and operation guard. New nested pages inherit their section root.
Workforce employee, employer/broker and `/api/workforce/` routes inherit
Workforce access. Unknown API routes remain admin-only until assigned an
explicit category.
**A new `/api/...` route is invisible to site managers until it is named in
`src/lib/auth/proxy-path.ts`.** The proxy runs before the route handler, so an
unclassified API path falls through to the admin-only default and the handler's
own `requireApiUser` / `canAccessSite` checks never run. Symptom: an admin
succeeds, a site manager gets `{"error":"Not allowed"}` (403) from the proxy,
and the route's own tests still pass because they mock the guard. Name the
route in `classifyAuthPath` and map it to a module in `moduleForPath` in the
same change. `npx vitest run src/lib/auth` covers the policy.
Use `getSiteScope(searchParams, "workforce")` (or the relevant section) before
loading page data, and `requireApiUser("workforce")` in its handlers. Shared
operation routes use `authorizeOperation(request)` plus record/site checks.
The only Financial handlers permitting clock accounts are document uploads
and purchase-order upload status/retry; new dashboard operations must not use
`allowClock: true`.