# Roles & Permissions

Access is decided by **three independent checks** on every request. All three must pass:

1. **Permission**: does the user's role grant the action (e.g. `work-order.complete`)?
2. **Tenant scope**: does the record belong to an organization the user may access?
3. **Record rule**: does the action fit the record's state and the user's row restrictions? Examples: a seller
   login can only reach its own customers; a completed work order can't be edited; a user can't manage
   someone of equal or higher level.

Code always checks **permissions**, never role names (`App\Enums\Permission`, 42 permissions, each registered as
a Gate ability). Policies in `app/Policies` combine the three checks. Every route also has a `can:` middleware.

---

## 1. Role model

| Attribute | Meaning |
|---|---|
| One role per user | Simple and auditable. A user's organization plus role define everything they can do. |
| `level` (1–100) | Privilege level. Users only manage users and roles **below** their own level. 100 is reserved for Super Admin. |
| `scope` | Who may hold the role: `platform` (no organization), `main_reseller`, `sub_reseller` or `any` organization. Checked when a role is assigned. |
| `restrict_to_own_records` | Row restriction for sales agents. Users only see customers and work orders linked to **their own seller profile**. Such users must be linked to a seller of the same organization. |
| `is_system` | The six default roles. They can't be deleted, and their level and scope are fixed. Their permissions can be adjusted, except Super Admin, which is locked. |

**Platform admins** (Super Admin, no organization) implicitly hold every permission.

## 2. Default roles

| Role | Level | Scope | Row restriction | Intended for |
|---|---|---|---|---|
| **Super Admin** | 100 | platform | — | Platform owner: all resellers, Service Master, roles, global settings |
| **Main Reseller Admin** | 80 | main reseller | — | Runs the main reseller: catalog (vendors, packages), sub-resellers, all their operations, users |
| **Sub-Reseller Admin** | 60 | sub-reseller | — | Runs one sub-reseller: customers, sellers, work orders, reports, users |
| **Operations** | 40 | any | — | Processes customers and work orders: status updates, scheduling, assignments |
| **Seller** | 20 | any | **own records** | Sales agent login: own customers, draft/submit own work orders |
| **Viewer / Auditor** | 10 | any | — | Read-only: records, reports, audit log |

## 3. Data scope by user type

| User | Organizations visible | Notes |
|---|---|---|
| Platform admin | All | |
| Main reseller user | Own + all its sub-resellers | Can filter by sub-reseller in lists, dashboard and reports |
| Sub-reseller user | Own only | Sees the parent's catalog, limited to the vendors allowed for it |
| Seller login | Own organization, **own seller rows only** | Customers and work orders whose `seller_id` is their seller profile. Cannot see other sellers. |

A record outside the scope returns **404** (its existence is not confirmed). An action without permission returns **403**.

## 4. Permission matrix (default roles)

| Permission | Meaning | Super | Main Admin | Sub Admin | Ops | Seller | Viewer |
|---|---|:-:|:-:|:-:|:-:|:-:|:-:|
| **Dashboard** | | | | | | | |
| `dashboard.view` | View dashboard | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| **Customers** | | | | | | | |
| `customer.view` | View customers | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| `customer.create` | Create customers | ✓ | ✓ | ✓ | ✓ | ✓ |   |
| `customer.update` | Edit customers & change status | ✓ | ✓ | ✓ | ✓ | ✓ |   |
| `customer.delete` | Delete customers (no work orders only) | ✓ | ✓ | ✓ |   |   |   |
| `customer.export` | Export customers | ✓ | ✓ | ✓ |   |   |   |
| **Vendors** | | | | | | | |
| `vendor.view` | View vendors | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| `vendor.create` | Create vendors | ✓ | ✓ |   |   |   |   |
| `vendor.update` | Edit vendors & change status | ✓ | ✓ |   |   |   |   |
| `vendor.delete` | Delete unused vendors | ✓ | ✓ |   |   |   |   |
| **Service Master** | | | | | | | |
| `service.view` | View Service Master | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| `service.manage` | Manage Service Master | ✓ |   |   |   |   |   |
| **Packages** | | | | | | | |
| `package.view` | View packages & pricing | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| `package.create` | Create packages | ✓ | ✓ |   |   |   |   |
| `package.update` | Edit packages & pricing | ✓ | ✓ |   |   |   |   |
| `package.delete` | Delete unused packages | ✓ | ✓ |   |   |   |   |
| **Sellers** | | | | | | | |
| `seller.view` | View sellers | ✓ | ✓ | ✓ | ✓ |   | ✓ |
| `seller.create` | Create sellers | ✓ | ✓ | ✓ |   |   |   |
| `seller.update` | Edit sellers & change status | ✓ | ✓ | ✓ |   |   |   |
| `seller.delete` | Delete unused sellers | ✓ | ✓ | ✓ |   |   |   |
| **Work Orders** | | | | | | | |
| `work-order.view` | View work orders | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| `work-order.create` | Create work orders | ✓ | ✓ | ✓ | ✓ | ✓ |   |
| `work-order.update` | Edit work orders | ✓ | ✓ | ✓ | ✓ | ✓ |   |
| `work-order.submit` | Submit draft work orders | ✓ | ✓ | ✓ | ✓ | ✓ |   |
| `work-order.change-status` | Change work order status (progress, schedule, hold, fail) | ✓ | ✓ | ✓ | ✓ |   |   |
| `work-order.complete` | Complete work orders | ✓ | ✓ | ✓ | ✓ |   |   |
| `work-order.cancel` | Cancel work orders | ✓ | ✓ | ✓ | ✓ |   |   |
| `work-order.reopen` | Reopen cancelled/failed work orders (recovery) | ✓ | ✓ |   |   |   |   |
| `work-order.assign` | Assign work orders to operations users | ✓ | ✓ | ✓ | ✓ |   |   |
| `work-order.note` | Add internal notes | ✓ | ✓ | ✓ | ✓ | ✓ |   |
| `work-order.attachment.upload` | Upload attachments | ✓ | ✓ | ✓ | ✓ | ✓ |   |
| `work-order.attachment.delete` | Delete attachments | ✓ | ✓ | ✓ |   |   |   |
| `work-order.export` | Export work orders | ✓ | ✓ | ✓ |   |   |   |
| **Reports** | | | | | | | |
| `report.view` | View reports | ✓ | ✓ | ✓ | ✓ |   | ✓ |
| `report.export` | Export & print reports | ✓ | ✓ | ✓ |   |   |   |
| **Administration** | | | | | | | |
| `organization.view` | View resellers / sub-resellers | ✓ | ✓ | ✓ |   |   | ✓ |
| `organization.manage` | Manage resellers / sub-resellers | ✓ | ✓ |   |   |   |   |
| `user.view` | View users | ✓ | ✓ | ✓ |   |   | ✓ |
| `user.manage` | Manage users (create, edit, activate, reset password) | ✓ | ✓ | ✓ |   |   |   |
| `role.manage` | Manage roles & permissions | ✓ |   |   |   |   |   |
| `audit.view` | View audit logs | ✓ | ✓ | ✓ |   |   | ✓ |
| `settings.manage` | Manage system settings | ✓ |   |   |   |   |   |

Generated from `App\Support\Rbac\DefaultRoles`, the single source for the seeded defaults.

## 5. Record rules beyond permissions

These rules apply even when a role holds the permission, e.g. a custom role:

| Area | Rule |
|---|---|
| **Catalog** (vendors, packages) | Owned by the main reseller. Only platform admins and users **of the owning main reseller** can create, edit or delete. Sub-reseller users get read-only access to the vendors allowed for them, even with a catalog permission. Records in use are deactivated, not deleted. |
| **Service Master** | Global list. `service.manage` only. Services are deactivated, never deleted, so packages and work order history keep their references. |
| **Organizations** | Platform admins create main resellers and sub-resellers. Main reseller users create and manage their own sub-resellers. No one can deactivate their **own** organization (prevents lock-out). Vendor availability for a sub-reseller is set by its parent or the platform, never by the sub-reseller. |
| **Users** | Only users with a **lower** level can be created, edited, reset or deactivated. Assigned roles must be below the actor's level and fit the organization type (role `scope`). Seller-restricted roles require a seller profile of the same organization. Users change their own account only in **My Profile** (not role, organization or status). |
| **Roles** | Super Admin is locked. A role can't be created or edited at or above the actor's level. System roles and roles in use can't be deleted. **Delegated role managers can only grant or revoke permissions they hold themselves.** Saving changed permissions signs out every user with that role. |
| **Work orders** | Editable while not finished (Completed, Cancelled and Failed are read-only). Vendor/package can change only while Draft, Submitted or On Hold. Every status change needs the permission for that target (see [WORK_ORDER_FLOW.md](WORK_ORDER_FLOW.md)). Assignees must be active, non-restricted users of the order's organization or its main reseller who hold `work-order.change-status`. |
| **Seller logins** | Customers and work orders are created for their **own** seller profile (the seller field is locked). They can edit only **Draft** orders, can only submit (Draft → Submitted), can't change customer status, and can't see the seller list or reports. |
| **Attachments** | Download needs `view` on the parent work order. Delete needs `work-order.attachment.delete` plus access to the order. Attachments are soft-deleted and audited. |
| **Audit log** | Entries of accessible organizations only. Platform-level entries are visible to platform admins only. Read-only for everyone. |
| **Settings** | Global (all tenants), so `settings.manage` is a platform permission by default. |

## 6. Two-factor enforcement

When *Settings → Security → Require two-factor authentication for administrators* is on, users holding
`user.manage`, `role.manage`, `organization.manage` or `settings.manage` must enrol in TOTP before using the portal.

## 7. Custom roles

*Administration → Roles & Permissions → New role*: choose a name, level (below your own), scope, optional
"Restrict to own records" and the permissions. Scope and row restriction can't be changed later, because users
are already assigned under them. All role changes are audited (`role.created`, `role.permissions_changed`,
`role.deleted`).

## 8. For developers: adding a permission

1. Add a case to `App\Enums\Permission` with its `label()`. The group comes from the prefix.
2. Grant it to the right default roles in `App\Support\Rbac\DefaultRoles`.
3. Protect the route with `->middleware('can:<permission>')` and check it in the policy.
4. Run `php artisan db:seed --class=RolesAndPermissionsSeeder`. New permissions are created. Existing roles
   keep administrator changes; only new roles and the locked Super Admin role are re-synced.
5. Add tests for allowed and denied cases.
