Workflows¶
This page covers the full lifecycle of a HA Annual PM Report, the parallel-facet signing flow, and the rejection / unwind path.
The 3-state lifecycle¶
stateDiagram-v2
[*] --> draft
draft --> in_progress: action_start() / wizard generate
in_progress --> submitted: action_submit() (freezes snapshot)
submitted --> archived: action_archive_report()
submitted --> draft: action_reset_to_draft() (manager only)
in_progress --> draft: action_reset_to_draft() (manager only)
archived --> [*] Three states, not four — no separate signed state. Signing is captured by the roots_signed_pdf field + the 🟢 ลงนามครบ badge, not by a state transition (matches the Q4 e-Signature parallel-facet decision).
State semantics¶
| State | Description | Lines | Signers | |
|---|---|---|---|---|
| Draft | Just created; not yet active | editable | editable | live data |
| In Progress | Engineer reviewing + filling exceptions | editable | editable | live data |
| Submitted | Locked + snapshotted, awaiting/post-signing | frozen | editable | snapshot |
| Archived | Year complete, locked permanently | frozen | locked | snapshot |
Generate → Review → Submit¶
The happy path:
- Generate Wizard — picks dept + FY, builds the report at
In Progress. Lines are pre-populated with planned/actual counts;result='pass'is pre-seeded on 100%-compliance lines. - Review — engineer scans the Equipment Lines tab, fills
result+exception_reasonfor below-plan lines. - Submit — clicks ส่งรายงาน (Submit). The system:
- Builds the immutable JSON snapshot containing every line's data, the full MoPH device-class table at this moment, and a SHA-256 paperformat fingerprint.
- Sets
state = submitted,submitted_date = now(). - Posts a chatter message:
📋 Report submitted by [User] — snapshot #N frozen with M lines.
Submit guards¶
The submit refuses when:
- State is not
In Progress. - The report has zero lines (forces wizard-use or manual line creation).
- Any line marked
fail/not_doneis missing an exception reason (raised by a@api.constrainson the line model).
Send for Signature¶
After submit, the header gains a 📝 Send for Signature button (visible only when sign_request_count == 0).
Pre-flight¶
The system calls _check_ha_signers_resolvable() first. It refuses when any of the three signer slots is empty or has no linked partner, with an actionable error like:
Cannot send for signature: • Engineering Head — not set • Hospital Director (Dr. Smith) — no linked partner; cannot resolve a sign_oca signer
The signing request¶
When pre-flight passes, the system:
- Renders the live QWeb PDF (snapshot-aware — reads from the frozen snapshot since state == submitted).
- Creates a
sign.oca.requestwith three signers: - BME Head —
role_bme_head, partner frombme_head_id - Engineering Head —
role_engineering_head, partner fromengineering_head_id - Hospital Director — Q4's existing
role_hospital_signer, partner fromdirector_id - Opens the request form so the engineer can review and click "Send".
Signing UX¶
- Internal signers — see a pencil icon in their systray notifying them of pending signatures. Click → in-app signing.
- Portal signers (typical for the off-site Hospital Director) — receive an email with a signing link that opens the portal. Sign there.
Completion → signed PDF¶
When the third signer completes, sign_oca transitions the request to 2_signed state. The Q5.9 _check_signed super-extension fires:
super()._check_signed()runs Q4's chain (handles inspection + contract cases).- This extension catches
record_ref._name == 'hemms.ha.report'. - Calls Q4's
_roots_attach_signed_pdf_to_parent(request, record)helper (inherited via the sign.oca.request inheritance chain) — writesdata(the composited signed PDF) toroots_signed_pdf+ posts the PDF to chatter as an attachment.
On the report header:
- The 🟡 รอลงนาม badge flips to 🟢 ลงนามครบ.
- The signed PDF appears in chatter.
- The state stays
Submitted— parallel-facet semantics.
Rejection + re-sign¶
sign_oca 18.0 doesn't ship in-flow rejection (it's on the upstream roadmap). Until then, operator-driven rejection is the path:
- Manager opens the report.
- Clears the signed PDF — either via the smart button on the sign.oca.request itself (
active = False) or directly:
report.sudo().write({
"roots_signed_pdf": False,
"roots_signed_pdf_filename": False,
})
old_request.active = False
sign.oca.request spawns. The pre-flight runs again so the manager can fix signer slots if needed. There is no cycle cap — a report can be rejected and re-sent indefinitely (matches the Q3 + Q4 patterns). All previous requests remain in history (active=False) for audit.
Reset to Draft (manager-only)¶
When a more fundamental change is needed (typo in lines, wrong exception reason, late-arriving PM event), only Reset to Draft unfreezes the report:
- Manager clicks คืนสู่ Draft (Reset) in the header (visible only with
maintenance.group_equipment_manager). - The system:
- Deletes the linked snapshot record.
- Sets
state = draft,submitted_date = False,snapshot_id = False. - Posts a chatter message:
♻️ Reset to draft by [User] — snapshot cleared. - Lines become editable again.
- On the next
Submit, a fresh snapshot is built from current data.
Signing implications. If signatures had already been collected, resetting to draft does NOT automatically reject the existing signature request — you should archive (
active = False) thesign.oca.requestmanually before resetting. Otherwise the chatter may end up with two signed PDFs from different snapshots.
Archive¶
When the next fiscal year arrives, archive the previous year's report:
- Click จัดเก็บ (Archive) on the submitted report.
- State transitions
Submitted → Archived. - The header gains an "Archived" web_ribbon as a visual cue.
- The report stays fully readable and printable — just locked permanently.
Archive is purely organisational — it doesn't delete or alter data. Surveyor reprints work just as well on archived reports as on submitted ones (both render from the snapshot).
Next steps¶
- Reporting — PDF anatomy + reprint guarantees
- Configuration — risk-tier setup