Skip to content

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

  1. Enable developer mode (Settings → Developer Tools → Activate developer mode)
  2. Go to Maintenance → Configuration → Maintenance Stages
  3. The list view shows Sequence, Default Responsible Role, SLA Hours, and Is 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:

time_in_current_stage_hours > stage.roots_sla_hours
   AND
stage.roots_sla_hours > 0

Tune per stage:

  1. Open the stage form (developer mode → Maintenance → Configuration → Maintenance Stages → click row)
  2. Edit SLA Hours
  3. 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:

  1. Sequence 5 places it before Submitted (seq 10) so PM and corrective tickets appear as parallel columns in the kanban.
  2. PM requests created by the OCA maintenance_plan daily 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:

  1. Detect that stage_id is being changed and is different from the current value (no-op writes skip everything)
  2. Compute time_in_previous_stage_hours for each record
  3. Call super().write(vals) — if Odoo raises (e.g., a permission error or a _constrains on another field), abort everything; no orphan log row is created
  4. 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 with from_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:

  1. Developer mode → Maintenance → Configuration → Maintenance Stages → New
  2. Fill the standard fields: Name (bilingual TH+EN in the same string, e.g., รอชิ้นส่วน (Awaiting Parts)), Sequence, Fold, Done
  3. Fill the HEMMS extension fields:
    • SLA Hours — set to 0 if 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
  4. 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.