Checklists
A checklist is a reusable template — a list of items a tech must verify on a work order. Checklists exist to make sure routine work doesn't drift: every PM visit covers the same checks; every install verifies the same handoff steps.
Templates and instances
There are two layers worth keeping straight:
| Layer | What it is | Lives where |
|---|---|---|
| Checklist | The reusable template — the master list of items | Checklists admin page |
| Checklist instance | A snapshot of the template attached to a specific work order | The work order detail page |
When a checklist gets attached to a work order — auto or manual — TuffOps doesn't reference the template by ID. It snapshots the items into a new instance on the work order. This is the same logic as quotations and invoices snapshotting line items: once a tech has started checking off items, edits to the master template can't change the work the tech already verified.
A consequence worth knowing: if you fix a typo on a checklist template after a work order has already been started, the work order keeps the old typo. New work orders pick up the fix. To propagate the fix into an in-flight work order, detach and re-attach the checklist (which loses any in-progress checks).
The feature flag
Checklists are gated by feature_flag_checklists_enabled (default off). When the flag is off:
- The Checklists menu and admin pages are hidden.
- No auto-attach happens at work order creation.
- Existing checklist data stays intact but isn't surfaced.
Most operations turn this on once they have a few standard PM templates ready to use.
Auto-attach precedence
The most useful and most surprising behavior of checklists is auto-attach. When a work order is created against a unit, TuffOps walks a priority order to decide what to attach:
- Checklists linked directly to the unit that match the work order type (
Installation,Repair,Maintenance,Service,Other). - If none match at the unit level, checklists linked to the unit's device model that match the type.
The first level that produces a match wins — model-level checklists are skipped if any unit-level checklist matches. So a unit-level override beats the model default for that one piece of equipment.
If the work order has no unit, no checklist auto-attaches. Office staff can still attach one manually at any time before the work order is completed.
Manufacturer scoping (a filtering rule, not auto-attach)
Checklists carry an optional manufacturer_id. This does not make manufacturer-level checklists auto-attach. Manufacturer scoping does one thing only: it filters the dropdown in the manual Attach Checklist picker on a work order. The dropdown only shows:
- Checklists with no manufacturer (universal), or
- Checklists whose manufacturer matches the unit's device model's manufacturer.
This keeps the picker tidy when you have, for example, separate maintenance checklists for Mitsubishi and Daikin equipment. Both lists are filtered out of view when you're attaching to a Carrier unit.
To get a manufacturer-specific checklist to auto-attach, you still have to assign it to a device model or a specific unit. The manufacturer field is purely for the picker.
Frequency rules
A checklist item can carry a max_required_frequency_in_days value (any integer between 1 and 3650). The rule applies at the item level, not the checklist level. This lets a single checklist mix items that can be re-completed every visit with items that need a minimum gap (e.g. an annual coil clean set to 300 so it can't be marked done twice in a year).
The check looks at the most recent completion of the same template item across all work orders for the same unit. If fewer than the required number of days have passed, the Complete button is disabled with a tooltip showing when it was last done. Users with checklists.override_frequency see an enabled override checkbox in the completion form; the override is logged.
Items without a frequency value can be completed any time the work order is open.
Result types
Each checklist item declares one of five result types. The result type controls what the tech sees in the inline completion form and what data is recorded:
| Result type | Tech sees | Recorded value |
|---|---|---|
| (None) | A Mark Complete button only. | Just the completion timestamp. |
| Pass / Fail | Two segmented buttons. | result_pass_fail = pass or fail. |
| Text | A short text box (up to 5000 characters). | result_text. |
| Numeric | A single number input (decimals allowed). | result_numeric. |
| Photo | A Mark Complete button. | The completion timestamp; the data is the photos attached to the item. |
For a Photo item that is also Required, the server rejects completion if no photo is attached and the page reloads with "This item requires at least one photo before completion." — see Use a Checklist on a Work Order for the field flow.
The required-items gate
A required item (the Required item checkbox on the item template) hard-blocks the work order from leaving ongoing to waiting_approval or completed until it's done. The check runs across every attached checklist instance — any one incomplete required item blocks the transition.
The gate has a permission-based escape hatch: a user with checklists.bypass_required_items can complete or send the work order anyway. The bypass is logged in the work order's history.
Strict refrigerant (EPA) gating
When the Compliance feature is enabled, a checklist item can carry an additional is_regulatory_refrigerant flag. To mark such an item complete, one of two things has to be true on the same work order:
- A refrigerant charge line has been added that is linked to this checklist item, or
- A user with
checklists.override_strict_refrigerant_checklisthas applied a compliance override for this work order.
The gate runs server-side in WorkOrderComplianceTransitionGuard::assertChecklistItemMayComplete(), and it's deliberately strict — the EPA Section 608 audit trail depends on a 1:1 link between charge events and the checklist obligation. See Checklists in the EPA flow for the full picture.
Detaching a checklist
A checklist instance can be detached from a work order if it's not relevant. Detaching:
- The endpoint check is
work_orders.edit. The visible × button on the checklist card is additionally gated bychecklists.delete, so a user needs both to remove a checklist via the UI. - Discards any in-progress checks on that instance, including all completion data and photos.
- Doesn't affect the master template — only this work order's snapshot is removed.
Detach when you've auto-attached the wrong template (wrong work order type, wrong scope) and want to start clean. To replace, attach the correct template after detaching.