Skip to content

Roles (Vendor)

Vendor roles configure module access levels (read, write, manager) and optional capabilities (e.g. product.view_cost) for employees. /dashboard/roles remains owner-only in the SPA; store managers use the employee module at manager level to CRUD staff.


GoalsNon-goals
Least-privilege module assignmentSSO group mapping
Clarify read vs write patterns for POS/orderAudit every permission string variant

  • ownerOnly: true in ROUTE_RULES for role management.
  • Default roles per store: when a store is created (or when the owner opens Roles), the backend seeds four built-in templates with role_preset: Admin, Manager, Employee, and Cashier. Owners can assign employees immediately without creating roles first.
  • Custom roles: POST /vendor/roles creates rows with role_preset = null; name, store, modules, and status are fully editable; delete is allowed.
  • Built-in presets: identified by role_preset (admin, manager, employee, cashier). Name and store cannot be changed; modules and status can be tuned; delete returns preset_role_not_deletable.
  • Cashier shell: Electron cashier login expects vendor_type = employee and, when role_preset is present, role_preset = cashier (legacy employees without a preset still work until roles are backfilled).
  • Slugs: pos, order, inventory, finance, product, customer, coupon, store, notification, settings, dashboard (see body).
  • Cashier preset seeds pos write, order / product / customer read (POS workspace + catalog visibility without store-wide order management).
  • Manager preset includes employee manager and product.view_cost; order manager unlocks store-wide shifts and order lists.

  1. Owner opens Roles → create role → assign modules + module_permissions.
  2. Assign employees to role in Employees.
  3. Employee logs in → ProtectedRoute enforces access.

  • Separation of duties: avoid owners sharing credentials; use roles for day-to-day.

IDScenarioExpected
VR1Employee opens /dashboard/rolesBlocked/redirect
VR2Owner edits roleSaves; employee JWT updates on next session
VR3Remove pos writePOS workspace sale blocked
VR4Cashier preset only posCan use /point-of-sale/*; dashboard order list still allowed via API alias when order is absent

  • /dashboard/roles, /dashboard/roles/create, /dashboard/roles/:id, /dashboard/roles/:id/edit

/dashboard/roles/:id is now the detail page with:

  • Subscribed customers = users/employees assigned to that role.
  • Plan details = permission modules and total permission count.

Access: routes under /dashboard/roles are owner-only in vendor-module-access.ts (ROUTE_RULES with ownerOnly: true). Employees cannot manage roles in the SPA even if they have other modules.

These string slugs appear in VendorProfile.modules and are referenced by route rules and ProtectedRoute:

pos, order, inventory, finance, product, customer, coupon, store, notification, settings, dashboard.

  • POS workspace (/point-of-sale/*) is gated by pos (read for browse, write for sale). Legacy roles that still list order / settings / finance without pos keep access on those paths until roles are updated.
  • Dashboard POS (/pos) and manual create order still use order with write.
  • Levels: each enabled module stores { module, level } where read < write < manager. Scoped modules (order, employee, settings, finance, dashboard) use manager for store-wide data; read/write on those modules stay self-scoped (own orders, own shifts).
  • Legacy JSON: bare strings or { module } without permissions map to write; permissions: ["manage"] maps to manager; permissions: ["read"] maps to read.
  • Capabilities: product.view_cost gates avg_purchase_price / last_purchase_unit_cost in product APIs and cost fields in the item form.

Role CRUD is implemented in apps/vendor-web/src/services/vendor-user-management.api.ts:

  • GET /vendor/roles — list (with optional params)
  • GET /vendor/roles/:id — one role
  • GET /vendor/roles/:id/detail — role detail with subscribedCustomers and planDetails.permissionModules
  • POST /vendor/roles — create
  • PUT /vendor/roles/:id — update
  • DELETE /vendor/roles/:id — delete

The role editor UI loads and saves through these endpoints; exact payload fields follow the DTOs used by the vendor backend module.