Bohari IMS controls who can see and do what through permissions, grouped into roles. Every new company starts with a set of built-in roles shaped around how stock actually moves through a restaurant, bistro or café, and you can adjust them from Settings.
Access is permission-based, not name-based. A role like "Storekeeper" is just a label attached to a bundle of permissions. What actually decides whether someone can approve a purchase order or accept a stocktake is the individual permission, for example manage_purchase_order or approve_stock_count. This matters because you can rename or re-scope a role for your company without breaking anything, and you can build your own roles by mixing and matching the same permission set.
This guide covers the built-in roles, how permissions are named, what each role can do out of the box, and how permissions decide what you see in the app and what the API allows.
How permissions are named
Every permission is a verb paired with the part of the platform it applies to: view_inventory, manage_purchase_order, approve_stock_count, record_waste. The verb tells you what kind of access it grants:
| Verb | Meaning |
|---|---|
view_ | Read-only access to a module's records |
manage_ | Full create / update / delete control over a module |
approve_ | Deciding on something someone else raised (a stocktake, a purchase order, a supplier invoice) |
perform_, record_, capture_, create_ | Doing the underlying action: counting, logging waste, buying, raising a requisition |
post_, reverse_, receive_ | Direct stock-ledger operations |
export_ | Downloading records |
So view_procurement lets you see the procurement screens, manage_purchase_order lets you create and edit purchase orders, and approve_purchase_order lets you sign off on one somebody else raised. Three different permissions, deliberately, because raising an order and approving it are two different jobs. The full permission list is defined once in the backend seed and mirrored to the web and mobile apps, so the names in code are exactly the names that gate the screens.
Built-in roles
When a company is created, Bohari IMS provisions these system roles automatically. Treat them as a starting point: you can edit their permissions, rename them, or add roles of your own.
| Role | What it's for |
|---|---|
| Admin | Full administrator. Holds every permission. |
| Finance / Accounts | Approves supplier invoices, records settlements, reviews cash purchases, and has read access to inventory and recipes for cost visibility. |
| Procurement Officer | Manages suppliers, the item master, purchase orders and requisitions. |
| Outlet Manager | Full control of a single outlet: stock movements and reversals, accepting counts and waste, recipes, sales imports, requisition and purchase order approval, receiving, cash purchases, bar shifts and bottle sign-outs. |
| Storekeeper | Receives deliveries and empties collections, issues stock, raises requisitions, captures cash purchases, performs stock counts, records waste, and signs bottles out. |
| Chef / Kitchen Lead | Manages recipes, issues kitchen stock, counts the kitchen's shelves, records waste, raises requisitions, and captures market-run cash purchases. |
| Bar Manager | Bar stock movements and reversals, counts the bar, opens and hands over bar shifts, signs bottles out, records waste, receives deliveries and returns empties, views recipes (pour specs), raises requisitions, and captures cash purchases. |
| Auditor | Read-only across the board (inventory, procurement, reports, compliance), for checking the work without being able to change it. |
| Stock-taker | An intentionally narrow allowlist: essentially nothing but perform_stock_count. No inventory visibility, no procurement, no reports. This is what makes blind counting possible; more on that below. |
Three deliberate separations of duty
A few of these roles are shaped the way they are on purpose, not by omission.
- A Storekeeper can receive and issue stock, but cannot approve a supplier invoice or record a settlement. Finance / Accounts holds those instead. The person confirming a delivery arrived is never the same person confirming the bill for it, and the person confirming the bill is never the one who could have invented the delivery it's approving.
- A Stock-taker cannot see inventory data at all. Not stock on hand, not procurement, not reports. They can only open and fill in a count sheet. A count from this role is strong evidence precisely because the counter had no way to know what number they were "supposed" to find. The roles that run a room can also count it (Storekeeper, Chef / Kitchen Lead and Bar Manager), but a count from someone who could see the expected figures beforehand carries less weight, and the system records which kind of count it was. The Outlet Manager is deliberately not among them: that role accepts counts, and a manager who counted could not approve their own figures.
- The person who asks for a measurement correction cannot approve it. Changing an item's unit of measure after trading has started moves balances between two item identities, so it is raised by whoever manages the item master and decided by a separate holder of
approve_item_replacement, which only Admin carries out of the box. See Replacing & Retiring Items.
The bar grants
Two permissions belong to the Bar Suite, and neither of them reaches a quantity, which is why holding them does not make anybody's counts sighted:
| Permission | What it allows | Held by |
|---|---|---|
manage_shifts | Open a bar shift, hand it over, close it, name who is responsible | Bar Manager, Outlet Manager, Admin |
sign_out_bottles | Sign sealed bottles out to a table and close the sign-out afterwards | Bar Manager, Outlet Manager, Storekeeper, Admin |
The Bar Manager holds both because they run the bar. The Outlet Manager holds both for the night the bar manager is off and, more importantly, for closing the sign-out or the shift somebody left open. The Storekeeper signs bottles out because in a small venue they are the person at the counter. The Stock-taker holds neither and cannot reach the shift report at all, because that report carries quantities.
Screens behind these two also need the BAR module, which the Enterprise plan carries. A company on a smaller plan sees no Bar Suite section at all, which is a plan choice rather than a permission problem.
Platform admins
Platform admins are a different thing from company roles. A platform admin is flagged at the account level and bypasses every permission check, on both the API and the UI. This is the level that manages platform-wide settings, per-company module licensing, and cloud cost analytics. Most companies never have a platform admin of their own. Inside a single company, the highest role is Admin, which holds all company permissions but does not bypass tenant isolation. An Admin still only ever sees their own company's data.
How permissions gate the app
The same permission strings drive both the screens you see and the requests the server accepts. They're checked in two places.
In the UI
The web app uses a usePermissions() hook that exposes hasPermission, hasAnyPermission, and hasAllPermissions. Components and navigation items check a permission before they render, so a user only sees what they can actually use. The "Approve" button on a purchase order, for example, only appears for someone holding the relevant approve_purchase_order* permission.
Whole pages are protected by a route guard. If you lack the permission for a page, you're redirected rather than shown an empty screen.
<PermissionRoute permissions={[MANAGE_PURCHASE_ORDER]}>
<PurchaseOrderCreatePage />
</PermissionRoute>
By default a route or check passes if you hold any one of the listed permissions (OR logic). UI gating is a convenience, not the security boundary. The server is the real gate.
In the API
Every protected endpoint declares the permission it needs with a @RequirePermission(...) decorator, and a guard enforces it on each request:
@RequirePermission('approve_stock_count')
@Post('stock-counts/:id/decision')
decide(...) { ... }
When a request arrives, the guard looks up the signed-in user's permissions for the current company and allows the call only if they hold at least one of the required permissions. Listing more than one permission on an endpoint means OR logic, the same as the UI. Platform admins skip the check entirely.
Customizing roles
System roles are created per company, so editing them affects only your company. From Company Settings → Roles & Permissions, an Admin (or anyone with manage_role) can:
- Adjust which permissions a role holds.
- Create new roles tailored to your structure.
- Assign roles to users.
Because access is decided by permissions rather than by the role's name, renaming a role is safe. The screens and endpoints a user can reach follow the permissions attached to their role, whatever you choose to call it.
FAQ
Why can't a user see a page I expected them to?
They're missing the permission that gates that page. Check the role assigned to them under Company Settings → Roles & Permissions and confirm it includes the relevant view_ or manage_ permission. They may need to sign out and back in for a permission change to take effect.
Can a Storekeeper approve their own goods receipt into an invoice?
No, by design. That's the receiving/approving separation described above. Approving a supplier invoice needs approve_supplier_invoice, which Finance / Accounts holds and Storekeeper deliberately does not.
What makes a stocktake "blind," and does it matter which role performed it?
Yes, it matters. A count performed by someone with no path to inventory data beforehand (the Stock-taker role) is recorded as blind, which is stronger evidence that the physical count is independent of the system's expectation. A count from a role that could see stock figures first, like Storekeeper, Chef / Kitchen Lead or Bar Manager, is recorded as not blind: each of those runs a room and holds view_inventory because receiving and reordering need balances. The Outlet Manager is never the example here, because that role does not count at all, which is exactly what leaves it free to approve. Both kinds are valid counts; the system just tells you which kind you're looking at.
If I rename a role, will anything break? No. Permissions, not role names, control access. Renaming changes only the label shown in the app.
Who can change roles and permissions?
Admins, and anyone granted manage_role. Be deliberate about who you give that to, since it effectively grants control over everyone else's access.
Do these roles apply to the mobile app too? Yes. The web and mobile apps share the same permission constants, so a user's access is identical on both.