Setup · roles & access
Who can do what.
Opticops has ten roles. Each one matches a real job on an optical-retail team. Permissions are enforced at the database — not just hidden in the UI — so a curious staffer can't bypass them by guessing a URL.
The eight roles
From most powerful to most specialised:
| Role | Scope | What they typically do |
|---|---|---|
| Org owner | Whole organisation | Sees everything, configures everything. The first user when an org is onboarded. |
| Regional manager | Multiple branches | Watches a cluster of branches. Reads margin & cost. Can dispatch transfers between their branches. |
| Branch manager | Single branch | Runs the day-to-day at one shop. Can receive stock, transfer to other branches, approve refunds. |
| Optometrist | Single branch | Captures prescriptions and runs eye-tests. Can also process sales. |
| Branch staff | Single branch | Frontline counter — sells, takes payment, prints invoices. Cannot see cost. |
| Accountant | Whole organisation | Reads everything finance: cost, margin, expenses, P&L, royalty. Cannot write transactions. |
| Lab technician | Single branch | Runs inbound + outbound QC on the production queue. No prices, no customer phone numbers. |
| Warehouse / Job Card | Single hub | Works the job-card queue — places lens orders with vendors, records tracking, dispatches finished lenses to branches. No prices, no customer PII. |
| Fitter | Single branch | Edges and fits lenses into frames from the fitting queue. No prices, no customer PII. |
| Auditor | Read-only across org | External CA or franchisor auditor. Reads everything but writes nothing. |
Cost visibility (the big one)
Cost prices are revoked at the column level for branch staff and optometrists. This means a curious staff member can't even SELECT the cost column via the API — the database refuses. Only the org owner, regional manager, branch manager, and accountant see cost. Auditor and platform admin see it too.
This applies everywhere cost appears: inventory pages, P&L, margin reports, weighted-average-cost variance warnings on the receive form, the per-SKU history page. None of it leaks.
Multi-branch rules
A user is attached to one default branch. Whether they see other branches depends on their role:
| Role | Sees other branches? |
|---|---|
| Org owner | Yes — all of them. |
| Regional manager | Yes — all branches in the org (cluster filter coming soon). |
| Branch manager | Only their own branch. |
| Optometrist | Only their own branch. |
| Branch staff | Only their own branch. |
| Accountant | Yes — all branches, read-only writes. |
| Lab technician | Only their own branch. |
| Warehouse / Job Card | Only their own warehouse / hub. |
| Fitter | Only their own branch. |
| Auditor | Yes — read-only. |
Franchisee data sovereignty
If you're a parent brand with FOFO franchisees, your view into a franchisee's data is intentionally limited. You see:
- Aggregated brand metrics (footfall, total revenue volume)
- Inventory state (you supplied the stock, you should know where it is)
- Royalty-relevant sales totals (you need to bill them)
You do not see (without the franchisee's explicit consent):
- Individual customer names, phones, or prescriptions
- Sales broken down by customer
- The franchisee's rent, salary, or operating expenses
- Their staff records
How to add a user
Org owners and regional managers can invite team members from Settings → Team & access → Open team page → (or visit /settings/team directly).
- Click + Invite team member.
- Type the email, pick the role and (for branch-scoped roles) a default branch. Optionally type the full name so it pre-fills nicely.
- Pick Send email (default — Supabase emails a magic link) or Generate link (you get a URL to share via WhatsApp / SMS).
- The new user clicks the link, signs in (magic link or Continue with Google), and lands on the dashboard with their role + branch already attached.
Managing existing members
Same page. Click Edit → on any row to change their role, change their default branch, or remove them from the team. Removed users lose sign-in access immediately (their next protected action 401s).
Two guardrails:
- Last-owner protection. You can't remove or demote the only remaining org owner — the form blocks the action with “promote someone else to owner first”.
- Self-edit limits. You can change your own default branch but not your own role (prevents accidental self-lockout).
Pending invites (people you've invited but who haven't accepted yet) show in a separate amber-bordered list with a Cancel action. Cancel pulls back the invite — if they later click the email link, sign-in fails politely.
Every change is logged
Invites, role changes, branch reassignments, and removals all write to audit_log with the actor user, the target user, the old + new values, and a timestamp. Visible to any org owner / regional manager / auditor in the org. We don't hide what we do.
Next up
Selling at the counter →Once your team is set up, this is the workflow they'll live in every day.