Configuration¶
Everything in this page is admin-only. You need to be in maintenance.group_equipment_manager (Maintenance / Administrator) for stage edits, and the same group is what unlocks the audit log menu.
Inspect the five stages¶
There are two places to view stage definitions.
From the UI¶
- Enable developer mode (Settings → Developer Tools → Activate developer mode)
- Go to Maintenance → Configuration → Maintenance Stages
- The list view shows
Sequence,Default Responsible Role,SLA Hours, andIs Done Stage
From the seed file¶
The canonical source of truth is addons/roots/roots_hemms_workflow/data/maintenance_stage_data.xml. The file is marked noupdate="1" so admin edits to SLA hours or responsible roles survive module upgrades — you can safely tune in the UI without worrying that the next -u will overwrite you.
| XML ID | Name | Seq | SLA | Role | Done |
|---|---|---|---|---|---|
stage_submitted | ส่งซ่อม (Submitted) | 10 | 4.0 | ward_staff | — |
stage_pending_review | รอตรวจสอบ (Pending Review) | 20 | 8.0 | bme_manager | — |
stage_in_repair | กำลังซ่อม (In Repair) | 30 | 24.0 | bme_technician | — |
stage_awaiting_pickup | ซ่อมเสร็จ - รอรับ (Awaiting Pickup) | 40 | 8.0 | bme_manager_signoff | — |
stage_received | รับงานแล้ว (Received) | 50 | 0.0 | ward_staff | ✓ |
The Received stage has fold=True, done=True, roots_is_done_stage=True and SLA 0.0 — the SLA timer does not run on terminal stages.
Adjust SLA hours¶
The most common admin task. The breach badge fires whenever:
Tune per stage:
- Open the stage form (developer mode → Maintenance → Configuration → Maintenance Stages → click row)
- Edit SLA Hours
- Save
The change applies immediately to all open requests in that stage (the breach flag is a computed field, not stored). No need to restart cron or rebuild caches.
Recommended starting SLAs by hospital size
| Stage | Small (<200 beds) | Tertiary care |
|---|---|---|
| Submitted | 4 h | 2 h |
| Pending Review | 8 h | 4 h |
| In Repair | 24 h | 24 h |
| Awaiting Pickup | 8 h | 8 h |
Critical-equipment SLAs are a Q-future item — today, criticality only drives the badge color, not the threshold.
Set SLA to 0 to disable the timer
roots_sla_hours = 0.0 is the documented way to opt a stage out of SLA monitoring. The breach flag stays False forever in that stage. Do not delete the field or set it to a negative number.
The PM Scheduled entry point¶
The Preventive Maintenance module (roots_hemms_pm) — installed at the same time as the workflow module in any HEMMS install — adds a sixth stage:
| XML ID | Name | Seq | SLA | Role |
|---|---|---|---|---|
roots_hemms_pm.stage_pm_scheduled | PM Scheduled (ตามแผนบำรุงรักษา) | 5 | 72.0 | bme_technician |
Two things to know:
- Sequence 5 places it before
Submitted(seq 10) so PM and corrective tickets appear as parallel columns in the kanban. - PM requests created by the OCA
maintenance_plandaily cron land directly in PM Scheduled, not in Submitted. The technician acknowledges the PM job by moving it to Pending Review, then it joins the standard corrective flow.
This stage is owned by roots_hemms_pm — see Preventive Maintenance → Configuration for editing it.
Security groups¶
| Group | Audit log access |
|---|---|
base.group_user (every internal user) | read + create |
maintenance.group_equipment_manager (BME manager) | read + write + create; no delete |
The exact ACL from security/ir.model.access.csv:
access_hemms_stage_transition_log_manager,...,maintenance.group_equipment_manager,1,1,1,0
access_hemms_stage_transition_log_user,...,base.group_user,1,0,1,0
Note that nobody can delete audit log rows — perm_unlink=0 for both groups. This is intentional: the trail is meant to be immutable evidence for surveyors. To remove an audit entry you would need a database superuser working outside the ORM, and that action would be visible in ir.logging.
Do not give regular users perm_unlink
If you ever need to allow the manager to delete log rows (we don't recommend it), do it via a separate group, not by editing the seeded ACL. The seeded rows are protected by noupdate in ir.model.access, but a hospital admin who edits the CSV and re-installs the module will wipe their custom delete grant silently.
How transition rules are enforced¶
The short answer: they aren't. There's no _constrains blocking a specific stage hop. Any user with write access on a maintenance request can drag its card forward, backward, or sideways.
The longer answer: all transitions are observed, not gated. The write() override (see models/maintenance_request.py) runs the following sequence inside one transaction:
- Detect that
stage_idis being changed and is different from the current value (no-op writes skip everything) - Compute
time_in_previous_stage_hoursfor each record - Call
super().write(vals)— if Odoo raises (e.g., a permission error or a_constrainson another field), abort everything; no orphan log row is created - After the super succeeds, bulk-create the audit log rows and post chatter messages
Two design choices worth knowing about:
vals["roots_stage_entered_at"] = now()is set before the super, so the reset happens atomically with the stage change.- Initial-stage logging lives in the
create()override, which writes an audit row withfrom_stage_id=False. This is why the first row of the Audit Trail tab is always the create event.
If a hospital later wants to enforce a stricter flow (e.g., no backward moves), the recommended hook point is a @api.constrains on stage_id reading from the previous value via _origin — but that's downstream module work, not workflow-core.
Adding a custom stage¶
We do not recommend adding custom stages — the five-stage flow is the surveyor-evidence shape we ship for HA accreditation, and adding columns breaks the matching against industry SLA benchmarks. But sometimes you need to (e.g., a hospital with a separate "Awaiting Parts" column).
If you have to:
- Developer mode → Maintenance → Configuration → Maintenance Stages → New
- Fill the standard fields: Name (bilingual TH+EN in the same string, e.g.,
รอชิ้นส่วน (Awaiting Parts)), Sequence, Fold, Done - Fill the HEMMS extension fields:
- SLA Hours — set to
0if you don't want a timer - Default Responsible Role — pick from the four roles
- Description (Thai) — for the in-app stage tooltip
- Is Done Stage — leave False unless this is a terminal column
- SLA Hours — set to
- Save
Pick a sequence number between the existing ones (e.g., 25 to slot between Pending Review and In Repair). Avoid renumbering the seeded stages — the seeded XML IDs are how downstream modules reference them.
Custom stages do not survive a module reinstall by themselves
The seed is noupdate="1" so edits to seeded stages survive. But a brand-new stage you create in the UI lives only in ir.model.data if you create an XML ID for it manually. To make a custom stage stick across re-installs, package it in a small hospital-specific Layer 4 module. Ask Trinity Roots if you need one.
Tuning the audit log retention¶
There is no automatic retention. The hemms.stage.transition.log table grows by one row per stage transition per request, and the seeded ACL forbids deletion. For a hospital with 50 active requests per day averaging 4 transitions each, that's ~73,000 rows per year — the index on (request_id) and (transitioned_at) keeps queries fast well past that.
If you ever need to archive old rows for compliance or storage reasons, do it via a database-level pg_dump partitioning strategy, not via the ORM. The audit log is intentionally write-and-keep.
Related pages¶
- Workflows — what each stage means in practice
- Reporting — the surveyor-friendly views over the audit log
- Preventive Maintenance → Configuration — for the PM Scheduled stage and the OCA cron