Skip to main content

Technician Permissions

The technician role is intentionally narrow. A technician can work the jobs assigned to them, log what they did, and submit for approval. Almost everything else — assignment, approval, billing, compliance overrides, catalog edits — is reserved for supervisors and office staff.

This page lists what the role grants, what it explicitly doesn't, and where each gate fires in the UI.

What the technician role grants

The technician role’s default web permissions come from config/permissions.php (roles.technician) and are synced by SettingsPermissionsSeeder. There is no role editor in the Settings UI — only Settings → Users (assign role + optional extra direct permissions per person). Mobile adds separate permission strings via MobilePermissionsSeeder without wiping web permissions.

The table below matches the intended technician experience; your database may include extra *.view grants (for example units.view / customers.view) added directly on Users → Edit so techs can open customer and unit pages from a work order.

PermissionWhat it lets a technician do
work_orders.viewSee work orders in the Work Orders list. The list is automatically scoped to the technician's own jobs.
work_orders.editEdit work orders assigned to them — items, photos, notes, checklist responses.
work_orders.assign (rare)Reassign a work order to themselves or another tech. Most orgs leave this off. Not in the stock technician template — grant on Users → Edit if needed.
units.view (common add-on)See unit records. Often granted on Users → Edit; stock roles.technician in config may omit it.
customers.view (common add-on)See customer records. Often granted on Users → Edit; stock roles.technician in config may omit it.
compliance.record_chargeSave refrigerant charge events on their own work orders.
checklists.viewSee and complete checklists attached to their work orders.
photos.createAttach photos to work orders.
notes.createAttach notes to work orders.

Permissions that are intentionally not granted to the technician role:

Missing permissionWhy
work_orders.superviseLifts the per-tech scope and exposes every work order. Reserved for supervisors.
work_orders.approveMoves a work order from waiting_approval to completed (the Approve & Complete button), or back to ongoing. Supervisors decide.
work_orders.billMoves a completed work order to billed (the Mark as Billed button). Office only.
work_orders.pricingShows price columns on items. Most techs don't see prices.
compliance.viewOpens the compliance section (overrides, leak repair, chronic leakers, exports). Supervisors only.
compliance.approve_overrideDecides on an override request. Reserved by EPA's four-eyes rule — must be a different user from the requester.
customers.editEditing customer records. Office only.
units.editEditing unit specs, retiring, tying. Office only.
checklists.editDesigning checklist templates. Office only.
quotations.*The quotation flow is office-side.
invoices.*The invoice flow is office-side.
users.*, settings.*, reports.viewAdmin functions.

Where each gate fires

The permission system runs in three places, and the technician notices each one differently:

1. Navigation visibility

The top navigation bar only shows tabs the user has permission to open. A technician sees Home, Work Orders, and a few helper links. The compliance, quotations, and invoices tabs simply aren't there.

2. Action button visibility

Inside a work order, the buttons that control status transitions are filtered by permission. The available technician transitions are hard-coded:

  • From pending: Start Work Order.
  • From ongoing: Pause Work Order and Complete Work Order (which moves to waiting_approval, despite the verb).
  • From paused: Start Work Order (resume).

Technicians never see Approve & Complete, Mark as Billed, or Cancel — those transitions are reserved for users with work_orders.supervise. There is no org-config knob that gives a tech Cancel; the gate is in WorkOrder::getAllowedTransitions().

3. Server-side enforcement

Even if a tech could craft the right URL, the controller middleware re-checks permission. Trying to load a route the tech doesn't have permission for returns HTTP 403. This matters for deep links — a compliance-linked work order shows a View episode pill that opens the leak-repair episode page, but a tech without compliance.view gets a 403 if they follow it.

Per-record scoping

Two permissions also enforce per-record scoping on top of the basic gate:

  • work_orders.view / work_orders.edit — without work_orders.supervise, the work order list is filtered to technician_id = current_user, and the controller blocks loading a work order that isn't theirs. This is enforced in WorkOrdersController::filter() and authorizeAccess().
  • work_orders.bill — even with the permission, the controller only allows the transition from waiting_approval to completed, not any other status. The button is hidden for techs and the controller rejects the request even if a tech tried.

Per-record scoping is why a tech sees an empty work order list when there's nothing assigned, instead of every job in the org.

Why the role is narrow

EPA's four-eyes rule, the cost of a misplaced approval, and the audit trail all push toward a separation: the person who did the work is not the same person who signed off on it. A narrow technician role makes that separation enforceable in the database, not just in policy.

If a technician genuinely needs to do something the role doesn't allow — approve a non-compliant charge event, mark a work order billed, edit a customer record — that's a sign the work needs a supervisor's eyes. The override-request flow exists for exactly that hand-off; see Compliance Overrides.

Custom roles

Permissions are not tied to role names. An org can clone the technician role and add work_orders.pricing for a "Lead Tech," or add compliance.approve_override for a "Working Supervisor." The patterns above describe the default set; the actual enforcement is per-permission, not per-role.