Skip to content
All guides

Getting Started · Updated September 5, 2026

Roles & Permissions

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:

VerbMeaning
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.

RoleWhat it's for
AdminFull administrator. Holds every permission.
Finance / AccountsApproves supplier invoices, records settlements, reviews cash purchases, and has read access to inventory and recipes for cost visibility.
Procurement OfficerManages suppliers, the item master, purchase orders and requisitions.
Outlet ManagerFull 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.
StorekeeperReceives deliveries and empties collections, issues stock, raises requisitions, captures cash purchases, performs stock counts, records waste, and signs bottles out.
Chef / Kitchen LeadManages recipes, issues kitchen stock, counts the kitchen's shelves, records waste, raises requisitions, and captures market-run cash purchases.
Bar ManagerBar 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.
AuditorRead-only across the board (inventory, procurement, reports, compliance), for checking the work without being able to change it.
Stock-takerAn 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:

PermissionWhat it allowsHeld by
manage_shiftsOpen a bar shift, hand it over, close it, name who is responsibleBar Manager, Outlet Manager, Admin
sign_out_bottlesSign sealed bottles out to a table and close the sign-out afterwardsBar 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.