Skip to content

Spare Parts: Workflows

PM workflow end-to-end

This is the primary workflow — the PM cron generates a request, a draft picking is automatically created, the technician adjusts and validates it, and stock is decremented.

┌──────────────────────────────────────────────────────────────────────────┐
│  PM Plan (maintenance.plan)                                              │
│  "Calibration every 3 months"                                            │
└───────────────────────────────┬──────────────────────────────────────────┘
                                │ OCA cron fires (daily)
┌──────────────────────────────────────────────────────────────────────────┐
│  maintenance.request created                                             │
│  maintenance_plan_id = plan, maintenance_kind_id = "Calibration"        │
└───────────────────────────────┬──────────────────────────────────────────┘
                                │ roots_hemms_spare_parts create() hook
                                │ (equipment.allow_consumptions = True AND
                                │  equipment.roots_auto_generate_picking = True)
┌──────────────────────────────────────────────────────────────────────────┐
│  stock.picking created (draft, Consumption type)                         │
│  roots_request_no = "SR/2026/00001"                                      │
│  Moves: lines where kind_ids is empty OR "Calibration" ∈ kind_ids        │
└───────────────────────────────┬──────────────────────────────────────────┘
                                │ Technician receives notification
┌──────────────────────────────────────────────────────────────────────────┐
│  Technician reviews draft picking                                        │
│  • Can increase qty (e.g., found more battery drain than expected)       │
│  • Can remove lines not needed on this visit                             │
│  • Can add ad-hoc lines (parts found broken during inspection)           │
└───────────────────────────────┬──────────────────────────────────────────┘
                                │ Technician clicks "Validate"
┌──────────────────────────────────────────────────────────────────────────┐
│  stock.move → Done                                                       │
│  On-hand qty decremented in the source location                          │
│  roots_pending_move_count on the picking → 0                             │
│  Pending SR stat button on maintenance.request → 0                       │
└──────────────────────────────────────────────────────────────────────────┘

Step-by-step: technician completing a PM picking

  1. Open :menuselection:Maintenance --> Maintenance Requests and find the PM request (it's in the "PM Scheduled" stage)
  2. Click the Pending SR stat button (shows count of in-progress pickings)
  3. The draft picking opens — review the pre-filled lines:
    • Check Product and Demand (the default_qty from the template)
    • The On Hand column shows current stock — check nothing is at zero
    • Adjust quantities if needed (tap the qty field)
    • To add a line: click :guilabel:Add a line → search for product → set qty
    • To remove a line: click the trash icon
  4. When ready, click :guilabel:Validate
  5. Odoo will ask to confirm; click :guilabel:Apply
  6. The picking moves to Done state and stock is decremented

Backorder handling

If a product is out of stock and you validate with a partial quantity, Odoo will offer to create a backorder for the remaining demand. Accept the backorder to keep tracking the unfulfilled quantity, or decline to write off the shortfall.

What if the equipment has no spare-part template lines?

If roots_spare_line_ids is empty when the PM cron fires, _roots_auto_create_spare_picking() returns without creating a picking — no error is raised. The PM request is created normally; the technician can open a picking manually if needed.

What if the template has lines but none match the request's kind?

If all lines have kind_ids set to specific kinds and none of them match the request's maintenance_kind_id, the filtered set is empty and no picking is created. Add a universal line (empty kind_ids) or add the relevant kind to an existing line to fix this.

Corrective request: manual picking

For unplanned corrective requests (no maintenance_plan_id), no picking is auto-created. The technician creates one manually:

  1. Open the maintenance request form
  2. Click :guilabel:Create Picking (if the button is available) or go to :menuselection:Inventory --> Operations --> Transfers → click :guilabel:New
  3. Set Operation Type to the Consumption picking type of the equipment's warehouse
  4. Set Maintenance Request to the open corrective request
  5. Add product lines for the parts to be consumed
  6. Save — the stock.picking.create() hook assigns an SR number immediately
  7. Validate when parts are physically pulled from the storeroom

SR number on corrective requests too

The SR number (SR/yyyy/nnnnn) is assigned to any picking linked to a maintenance request — not just PM-generated ones. This means every spare-part consumption, whether planned or corrective, gets a sequential reference for the audit trail.

Stock Request numbers

Where SR numbers appear

The roots_request_no field is visible in three places:

  1. Picking list view — :menuselection:Maintenance --> Operations --> Stock Requests — the SR No. column in the list
  2. Picking form header — next to the picking reference and origin field
  3. Maintenance request form — via the Pending SR stat button's linked list

Why SR numbers matter for audit

Hospitals subject to HA Thailand or JCI auditing need to demonstrate that spare-part consumption is traceable to a specific work order. The SR number creates a direct link:

  • SR/2026/00042 was consumed for maintenance request "MAINT-00153" (Calibration on Vital Monitor #3, 2026-05-15)
  • The picking shows: 2 × ECG cable, 1 × calibration adapter — validated by technician "Somchai K." at 14:32

This chain — PM plan → PM request → SR picking → stock move — satisfies HA 3.3.2 (Preventive Maintenance Records) traceability requirements.

SR numbers are never re-used

The ir.sequence implementation uses no_gap=False (standard mode), so numbers may have gaps if pickings are cancelled. If your audit policy requires gap-free numbering, change the sequence implementation to no_gap (see Configuration → Stock Request sequence).

Low-stock activity

What triggers it

A scheduled action (ir.cron) runs daily with the code hemms.equipment.spare.line._cron_check_critical_low_stock(). It searches for all spare lines where:

[("roots_is_critical", "=", True), ("qty_available", "<=", 0)]

For each matching line, it raises a warning activity on the equipment record — but only if an identical activity (same product name in the note) doesn't already exist on that equipment.

Who the activity is assigned to

The activity is assigned to equipment.technician_user_id — the person listed in the Responsible Technician field on the equipment form. If no technician is assigned, the activity falls back to the user who ran the cron (typically the system administrator admin).

Assign a Responsible Technician to each piece of equipment

Without a technician set, low-stock alerts go to admin and are easily missed. Go to :menuselection:Maintenance --> Equipment, open each equipment record, and fill in Responsible Technician.

What the activity looks like

The scheduled activity appears on the equipment's chatter and in the technician's Activities view (:menuselection:Discuss --> Activities):

Warning — Critical spare low stock: ECG Cable (5-lead)

ECG Cable (5-lead) on Vital Signs Monitor #3 has 0.0 units on hand.

The activity is of type Warning (orange icon). The technician can Mark Done once they've reordered the part, or Schedule Activity to follow up with a Purchase Order reference.

Duplicate suppression

Before creating an activity, the cron checks for an existing open warning activity on the same equipment whose note contains the product name. If one already exists, a second alert is not created. This prevents the chatter from flooding on consecutive daily cron runs.

The deduplication clears automatically when the technician marks the existing activity as done.

Cron schedule

The low-stock cron is defined in addons/roots/roots_hemms_spare_parts/data/ir_cron_data.xml and runs at midnight daily (server timezone). To change the schedule:

  1. :menuselection:Settings --> Technical --> Scheduled Actions
  2. Find "HEMMS: Check critical spare low stock"
  3. Adjust Interval Number and Interval Type (e.g., every 6 hours for a hospital with frequent high-priority PM)

Edge cases

Equipment archived — alerts still fire?

Yes. The cron searches hemms.equipment.spare.line without filtering on equipment active state. If you archive an equipment, its spare lines remain and the cron will still alert if critical stock is zero. To stop alerts, either delete the spare lines or archive the products.

Product with zero on-hand but a pending purchase order

The cron does not check open POs or incoming receipts — it reads raw qty_available (on-hand only). If you have stock on order, you can dismiss the activity manually with a note ("PO-00123 arriving 2026-05-28").

Multiple equipments sharing the same critical spare

Each equipment gets its own activity if the shared product is at zero. This is intentional — the technician needs to understand which equipment is affected, not just which product is low.

Next steps