---
name: evolution_permission_system
description: "Where evolution's staff module-permission system is defined, set, and enforced in code"
metadata: 
  node_type: memory
  type: reference
  originSessionId: bc0c41e9-a496-45b9-a31e-0d940754b513
---

Evolution module permissions are per-staff session values: `$_SESSION['mod_sales' | 'mod_pur' | 'mod_ap' | 'mod_ar' | 'mod_gl' | 'mod_jc' | 'mod_inv' | 'mod_pr' | 'mod_rep' | 'mod_cf' | 'mod_timesheet']` plus `$_SESSION['administrator']` (Super User). Values: `0` Denied, `1` Allowed, `2` Admin. (Timesheets shows "Not Allowed" for 0.)

Flow:
- **Set** in `staffedit.inc` (Permissions tab, admin-only) → saved to the `permissions` table.
- **Loaded** at login by `login2save.php` — it reads the staff's `permissions` row and does `foreach ($permissions as $k => $v) $_SESSION[$k] = $v;`. So **permission changes only take effect after the user logs out and back in.**
- **Enforced** per-screen by checking these session vars. Menu visibility is gated in `app/templates/menu.php` (`>= "1"`). Admin-only actions check `== "2"` (e.g. un-commit invoices/bills, approve timesheets, edit inventory true cost, delete records, sensitive reports). `$_SESSION['administrator'] == "1"` bypasses all checks (`userCanAccessSource()` in `functions.php`).

Cross-module quirk: payroll leave-request *approval* is gated by `mod_ap == 2`, not `mod_pr`.

End-user documentation of all this: `docs/admin/setting-permissions.php` (see [[docs_authoring]]).
