Skip to content

Architecture

HEMMS is not a single monolithic Odoo addon — it is a stack of three layers that compose around Odoo's built-in maintenance module. Each layer answers a different question and can evolve independently.

The three-layer model

┌─────────────────────────────────────────────────────────────────┐
│ Layer 3 — roots_hemms_* (Hospital lens)                          │
│ ───────────────────────────────────────                          │
│ Governance, compliance, Thai-specific workflow, HA Thailand audit │
│   • roots_hemms_workflow        request lifecycle + SLA + audit  │
│   • roots_hemms_criticality     value × risk matrix              │
│   • roots_hemms_asset_master    brand/model + Ref. No.           │
│   • roots_hemms_asset_inspection annual stock-check (ปจป.)        │
│   • roots_hemms_pm              hospital PM kinds + stage         │
└─────────────────────────────────────────────────────────────────┘
                              │ extends
┌─────────────────────────────────────────────────────────────────┐
│ Layer 2 — OCA building blocks                                     │
│ ──────────────────────────────                                    │
│ Generic, configurable, community-maintained                       │
│   • maintenance_plan           PM scheduling engine               │
│   • maintenance_stock          spare parts ↔ stock                │
│   • maintenance_partner        external service providers         │
│   • sign_oca                   e-signature engine (OCA/sign)      │
│   • ...vendored modules in addons/oca/ (see addons/oca/README.md) │
└─────────────────────────────────────────────────────────────────┘
                              │ extends
┌─────────────────────────────────────────────────────────────────┐
│ Layer 1 — Odoo Base                                               │
│ ──────────────────                                                │
│ maintenance.request, maintenance.equipment, maintenance.stage,    │
│ res.partner, res.users, hr.employee, hr.department                │
└─────────────────────────────────────────────────────────────────┘

Layer 1 — What Odoo Base gives us

Odoo Community Edition's maintenance app:

  • maintenance.request — the work item
  • maintenance.equipment — the asset
  • maintenance.stage — workflow stages (4 default: New / In Progress / Repaired / Scrap)
  • Kanban / list / form views
  • Basic team assignment via maintenance.team

What's missing for a hospital:

  • No bilingual labels
  • No SLA tracking per stage
  • No structured audit log (only mail.message mixed with chatter)
  • No criticality/risk model
  • No annual asset inspection workflow
  • No government Ref. No. (รหัสครุภัณฑ์)
  • No PM scheduling

Layer 2 — What OCA building blocks add

The OCA maintenance repo (vendored under addons/oca/) provides domain extensions, each as a separate, opt-in module. Think of them as Lego bricks — pick what you need.

Module Adds Why a hospital cares
maintenance_plan maintenance.plan + maintenance.kind + cron Auto-create PM requests on calendar interval
maintenance_stock links maintenance.request to stock.picking Track spare parts consumption per repair
maintenance_partner links to res.partner Track external service vendors per equipment
sign_oca (OCA/sign) sign.oca.template / request / role + audit log e-Signature engine for ปจป. inspection + service contracts (Q4)
maintenance_equipment_ref adds ref field Government inventory Ref. No. (รหัสครุภัณฑ์)
maintenance_equipment_category_hierarchy parent/child categories Hierarchical equipment grouping
... see addons/oca/README.md for the full vendored list ... ...

OCA modules are generic and configurable — they don't impose a hospital workflow. They give us building blocks; we compose them.

Layer 3 — What roots_hemms_* layers add

The roots_hemms_* modules wrap OCA building blocks with hospital-specific governance and Thai compliance. They are the "Hospital Lens" on top of generic maintenance.

Module Adds Governance concern
roots_hemms_workflow 5-stage Thai BME workflow + SLA + audit log Operational: who did what, when, was SLA met
roots_hemms_criticality Value × Risk matrix → criticality color Prioritization: which equipment matters most
roots_hemms_asset_master Brand / Model master + current_condition Master data: what equipment do we own
roots_hemms_asset_inspection Annual stock-check workflow (ปจป.) Compliance: HA Thailand annual audit
roots_hemms_pm Hospital PM kinds + PM Scheduled stage Compliance: scheduled preventive maintenance

How the layers compose: a PM request example

Worked example showing all three layers in action.

Step 1 — Setup (admin, one-time)

  1. Layer 1 — Equipment record exists for "Vital Signs Monitor #5"
  2. Layer 3roots_hemms_criticality computes criticality_color = red (high value × high risk because ICU equipment)
  3. Layer 2maintenance_plan admin attaches a plan: kind = Calibration, interval = 6 months, start_date = today
  4. Layer 3roots_hemms_pm provides the "Calibration" kind as seed data (admin doesn't have to type it from scratch)

Step 2 — Scheduling (cron, daily)

  1. Layer 2maintenance_plan._cron_generate_requests() runs at 06:00
  2. For each plan, the cron walks forward by interval until it hits the planning_horizon window
  3. For each date in the window, it creates a maintenance.request with maintenance_type = "preventive" and maintenance_plan_id set
  4. Layer 3roots_hemms_pm.maintenance.request.create() overrides the stage assignment: if maintenance_plan_id is set, route to the "PM Scheduled" stage instead of the default "Submitted"

Step 3 — Execution (technician, daily ops)

  1. Layer 1 — Request appears in kanban under "PM Scheduled" column
  2. Layer 3roots_hemms_workflow propagates equipment criticality to the request, so the card shows a red dot
  3. Layer 3 — Technician moves stage: PM Scheduled → Pending Review → In Repair → Awaiting Pickup → Received
  4. Layer 3 — Every stage transition creates a row in hemms.stage.transition.log (audit trail)
  5. Layer 3 — If a stage exceeds roots_sla_hours, the request appears in [("roots_is_sla_breached", "=", True)] search

Step 4 — Reporting (manager, end of period)

  1. Layer 3roots_hemms_workflow provides SLA dashboards (operational metric)
  2. Layer 3roots_hemms_asset_inspection provides annual stock-check PDF reports (compliance metric)
  3. Layer 3roots_hemms_criticality provides red/yellow/green classification for prioritization

Why this layering matters

1. We don't fight Odoo or OCA

By extending instead of replacing, we get:

  • Bug fixes from Odoo SA flow into our code automatically
  • New features from OCA are opt-in (we can add another module without rework)
  • Standard Odoo training applies to our users (kanban, list, search, etc.)

2. Hospital concerns live in one layer

If we ever need to support a non-hospital deployment (e.g., manufacturing), we can swap roots_hemms_* for roots_manufacturing_* — Layers 1+2 stay the same. Hospital logic is not entangled into the maintenance request model.

3. Compliance is auditable

Every Layer 3 module is small, focused, and easy to map to a compliance requirement:

  • "Show me the audit trail for this request" → hemms.stage.transition.log
  • "Show me all overdue PM on critical equipment" → join criticality_color + roots_is_sla_breached + maintenance_type=preventive
  • "Show me last year's annual inspection report" → hemms.asset.inspection with state = submitted

4. Each module can be versioned independently

A change to PM kinds doesn't force a release of the workflow module. A schema migration in roots_hemms_workflow doesn't ripple into the inspection module unless explicitly depended on.

Field naming convention

Hospital-specific fields use a roots_ prefix to avoid collision with Odoo or OCA fields, and to make them easy to spot in fields_get() output.

# ✅ Hospital-specific
roots_value_tier = fields.Selection(...)
roots_criticality_color = fields.Selection(...)
roots_sla_hours = fields.Float(...)
roots_stage_entered_at = fields.Datetime(...)

# ✅ Inherited / standard (no prefix)
name = fields.Char(...)
state = fields.Selection(...)
maintenance_kind_id = fields.Many2one(...)   # comes from OCA

# ❌ Avoid
hospital_priority = ...   # too vague; not prefix-discoverable
priority_v2 = ...         # version in a field name is a smell

When inheriting a base Odoo / OCA model and adding fields, always prefix roots_. When defining a brand-new model owned by HEMMS, the _name is prefix-enough; individual fields don't need redundant prefixing.

OCA vs roots_hemms_* decision matrix

When adding a new feature, ask:

Question Layer
Is this generic maintenance functionality? (e.g., link to inventory, time tracking) Look for an existing OCA module first
Is this hospital-specific compliance? (e.g., HA Thailand audit, Thai BME workflow) Build in roots_hemms_*
Is this a UI-only tweak? Inherit the view, don't write a new model
Is this configurable per installation? Use ir.config_parameter or a setting, not a hardcoded constant

Rule of thumb

Default to OCA. Only build new in roots_hemms_* when OCA doesn't have it AND the requirement is hospital/Thai-specific AND it's worth the maintenance burden.

Past examples of this decision:

  • "We need PM scheduling" → ✅ OCA maintenance_plan (don't build)
  • "We need spare parts tracking" → ✅ OCA maintenance_stock (don't build)
  • "We need 5-stage Thai BME workflow with SLA breach detection" → ✅ Build roots_hemms_workflow (OCA only has 4 generic stages without SLA)
  • "We need annual ปจป. PDF report in Thai format" → ✅ Build roots_hemms_asset_inspection (no equivalent in OCA)
  • "We need bilingual maintenance kinds" → ✅ Build roots_hemms_pm to extend OCA's maintenance.kind with roots_name_th (lightweight wrapper)

Module dependency graph

                    maintenance (Odoo base)
                     base_maintenance (OCA)
                    maintenance_plan (OCA)
                              │   ┌──────────────────────────┐
                              ├───┤ roots_hemms_asset_master │
                              │   └──────────────────────────┘
                              │                  ▲
                              │                  │
                              │   ┌──────────────────────────┐
                              ├───┤ roots_hemms_criticality  │
                              │   └──────────────────────────┘
                              │                  ▲
                              │                  │
                              │   ┌──────────────────────────┐
                              ├───┤ roots_hemms_workflow     │
                              │   └──────────────────────────┘
                              │                  ▲
                              │                  │
                              │   ┌──────────────────────────────┐
                              ├───┤ roots_hemms_asset_inspection │
                              │   └──────────────────────────────┘
                              │                  ▲
                              │                  │
                              │   ┌──────────────────────────┐
                              └───┤ roots_hemms_pm           │  (Q1)
                                  └──────────────────────────┘

Glossary

Term Meaning
Layer 1 Odoo CE base maintenance module
Layer 2 OCA-vendored building blocks (addons/oca/)
Layer 3 HEMMS hospital extensions (addons/roots/)
BME Biomedical Engineering — hospital department maintaining medical equipment
HA Thailand Healthcare Accreditation Institute (Thailand) — sets hospital accreditation standards
ปจป. Annual asset inspection report (ตรวจสอบพัสดุประจำปี)
SLA Service Level Agreement — max hours a request should spend in a stage
Criticality Equipment importance — value × risk → red/yellow/green