Menu
CMMS data model: assets, work orders, spare parts, meters

CMMS data model: assets, work orders, spare parts, meters

The field tables behind a CMMS: asset registry, work orders, PM plans, meters, spare parts, the keys that join them and worked MTBF and MTTR arithmetic.
CMMS data model: assets, work orders, spare parts, meters

Key takeaways

  • The core is eight objects: asset, meter, reading, PM plan, request, work order, part and part issue. Three more carry the detail: failure record, labour booking and the asset to part link.
  • How far you can go is decided by parent_asset_id on the asset and the downtime window on the work order.
  • Store location and cost centre as links, never as typed text. Free text kills every roll-up you will want later.
  • Worked below: 1,000 run hours, 3 failures, 520 downtime minutes gives MTBF 333.3 h, MTTR 173.3 min and availability 99.1%.
  • The CMMS owns the asset, the meter and the work order. The ERP owns the purchase order, the invoice and the book value.

The objects a CMMS needs

This is the reference schema for maintenance in a manufacturing plant: the tables, the fields and the keys that join them.

It covers maintenance work orders. For the orders that make parts, see our production work order data model.

The asset registry

The asset record is the spine. Every reading, plan, work order and part consumption points at it.

FieldWhat it holdsExample
asset_noUnique tag, never reusedFIL-01
parent_asset_idAsset one level upLINE-02
namePlain nameRotary filler 24 valve
asset_classType from a fixed listFILLER
manu­fac­turerMaker on the nameplateSee the plate
modelModel desig­na­tionRF-24/6
serial_noMaker's serial2019-8841
year_builtYear the machine was made2019
commissioning_dateHandover to production2019-11-04
location_idLink to a location recordP1-HALL-B-BAY3
cost_centreLink to the finance listCC-4120
crit­i­calityA, B or CA (stops the line)
statusLifecycle stateOperating
warranty_expiryLast day of cover2021-11-03
expected_life_yearsPlanning life15
document_idsManual, drawings, spare list3 files
qr_valueWhat the label encodesFIL-01

Two fields do the work. parent_asset_id builds the hierarchy (site, area, line, machine, component) and asset_class lets you compare every filler you own.

Set the tag scheme before the first import. Our asset hierarchy and naming convention guide covers the levels and the codes.

Criticality drives everything downstream

Criticality decides PM frequency, spare stocking and who gets called at night. Score it on consequence (safety, line stoppage, quality risk, repair cost, redundancy) and keep it to three classes.

Asset status and what triggers each state

StatusTriggered by
PlannedAsset created, not yet installed
Com­mis­sion­ingInstalled, tests running
OperatingHandover signed, commissioning_date set
StandbyAvailable but not in use (spare pump)
DownDerived, not stored: an open downtime window exists
StoredRemoved from the line, kept
De­com­mis­sionedRetired, history kept, no new work

Only the six stored states are lifecycle. Down is a condition you derive from open downtime, because storing it overwrites Standby or Commissioning and nothing tells you what to restore on repair.

Never delete an asset. Decommission it, because deleting takes every work order and cost with it.

The maintenance work order

The work order is the unit of work and the unit of cost. One work order, one asset.

FieldWhat it holdsExample
wo_noUnique numberWO-26-0412
asset_noAsset worked onFIL-01
wo_typeFixed listCorrective
plan_idPM plan behind it, else emptyEmpty
request_noRequest behind it, else emptyREQ-26-1188
priority1 to 41 (emergency)
statusLifecycle stateIn progress
requested_atWhen it was raised2026-06-18 21:40
planned_start / endScheduled window2026-06-18 22:00
actual_start / endWork really started, ended2026-06-18 22:05
requested_byPerson who reported itShift leader
assignee_idTechnician respon­si­bleEmployee 2231
tradeSkill neededMechanical
downtime_startAsset stopped2026-06-18 21:38
downtime_endAsset handed back2026-06-19 01:38
downtime_typePlanned or unplanned stopUnplanned
is_failureCounts for MTBFYes
failure_linesComponent plus problem, cause and remedy, one row each1 row
labour_hoursSum of time bookings7.0
parts_cost / labour_costDerived totals184 EUR / 280 EUR

Downtime and labour are different quantities. The filler was down 240 minutes, and two technicians each booked 3.5 hours, so labour_hours is 7.0.

Open downtime only when the asset really stopped, and set downtime_type to planned for shutdown and PM work. Without that flag every planned stop inflates your failure figures (see downtime reason code design).

The stop itself belongs to the asset, not to the work order, because a machine can stop with no job on it. Where a monitoring system already records machine states, treat that event as the source and let downtime_start and downtime_end point at it.

Work order status and what triggers each state

StatusTriggered by
NewCreated from a request or a plan
ApprovedApprover signs off, if approval is on
ScheduledAssignee and planned window set
In progressFirst time booking, actual_start set
WaitingBlocked: part, access or shutdown
CompletedWork done, hours entered, codes where required
ClosedCosts settled, no more bookings
CancelledWithdrawn before any booking

Waiting needs a reason and a timestamp. Without them, a job waiting six days for a seal looks like a job nobody started.

For the list behind wo_type, see work order types explained.

Failure coding: problem, cause, remedy

Three code fields answer three questions: what was seen, why it happened, what was done.

Give each list three levels, so a technician picks from a short menu at every step instead of scrolling 200 codes.

FieldLevel 1 to level 3
component_idFiller, Valve block, Seat seal (a child asset row)
problem_codeLeak, External leak, Product at valve
cause_codeWear, Normal wear, Seal life exceeded
remedy_codeReplace, Replace part, Seal kit fitted

Make the codes mandatory only where they pay off: corrective work orders with downtime, on class A and B assets.

Two questions justify the scheme: which component fails most often, and which cause returns after the same remedy.

Keep the codes in a separate failure record with its own key, not as four columns on the work order. One job can find two failed components, and four columns force the technician to pick the more impressive one.

The request that precedes a work order

An operator reporting a leak is not yet a work order. Keep the request separate, so rejected and duplicate reports stay out of your work order history.

FieldExample
request_noREQ-26-1188
asset_noFIL-01 (scanned from the QR label)
reported_by / atOperator 118, 2026-06-18 21:40
descrip­tionProduct leaking at valve 7
asset_stoppedYes
urgencySafety, Line stopped, Normal
statusNew, Accepted, Rejected, Duplicate
wo_noWO-26-0412, set on acceptance
accepted_at2026-06-18 21:52

reported_at and accepted_at give you reaction time: 12 minutes in the row above.

Reaction time is usually the real complaint behind "maintenance is slow". It stays invisible while requests arrive by phone.

PM plans and schedules

A PM plan is a rule, not a task. It says when work falls due on one asset and which task list to copy into the work order.

FieldWhat it holdsExample
plan_idUnique planPM-FIL-01-500H
asset_noAsset coveredFIL-01
task_list_idTemplate of stepsTL-FILL-500H
trigger_typeCalendar, meter or conditionMeter
meter_idCounter watchedMTR-FIL-01-H
intervalValue and unit500 h
lead_time_daysHow early work appears7
basisFloating or fixedFloating from last done
last_done_dateLast completion2026-07-18
last_done_meterCounter at completion13,000 h
next_due_meterDerived13,500 h
tradeSkill neededMechanical
parts_kitParts normally usedKIT-FIL-500H
shutdown_requiredNeeds the line stoppedYes
TriggerFires when
Calendarlast_done_date plus interval is inside lead_time_days
MeterLatest reading reaches next_due_meter minus lead_time_days times average daily usage
ConditionA measured value crosses a limit (vibration, tem­pera­ture)

At 13,480 h against a due point of 13,500 h, the plan above is 20 run hours away, which is about two days at roughly 11 run hours a day.

The 7 day lead converts to about 77 run hours, so this work order was raised at 13,423 h and is already on the list. A lead expressed in days always needs that conversion, and a plant that skips it schedules meter based PM by accident.

Floating vs fixed: a floating plan counts from the last completion, so a late job pushes the next one, while a fixed plan keeps the original calendar (preventive maintenance schedule).

Meters and readings

A meter belongs to an asset. A reading belongs to a meter, and readings are never overwritten.

FieldExample
meter_idMTR-FIL-01-H
asset_noFIL-01
meter_type / unitRun hours, h
sourcePLC, sensor or manual entry
directionIncreasing only
rollover_at100,000 (5 digit counter)
offsetAdded after a counter swap
reading_id / value / at884213, 13,480.0, 2026-08-31
wo_noSet when read during a job

The rollover rule

A counter that wraps looks like a huge negative jump, and a plan watching it stops firing silently.

Write the rule once, and guard it: treat a lower value as a wrap only when the last reading sits near rollover_at and the counter was not swapped. Then delta = (rollover_at minus last value) plus new value.

  • Last reading 99,942 h, next reading 58 h, rollover_at 100,000.
  • Delta = (100,000 minus 99,942) + 58 = 116 h, not minus 99,884.
  • Lifetime hours = previous lifetime + delta, so usage always comes from deltas.

Two other cases also produce a lower value and must not be read as a wrap: a replaced counter starting at zero (store an offset) and a wrong entry (post a new reading, never edit the old one). Without the guard above, the rule adds a phantom 100,000 h of life to the asset and every meter based plan goes quiet for years.

Readings from a machine monitoring system remove the typing errors, and a condition trigger needs them anyway (condition based maintenance).

Spare parts, stock and consumption

The part record holds the identity and the stock rule. The issue line holds what was used, and it always belongs to a work order.

FieldWhat it holdsExample
part_noYour numberSEAL-24V-EPDM
descrip­tionPlain textValve seat seal EPDM
manufacturer_part_noCatalogue numberAs on the OEM list
uomUnitpcs
bin_idLink to the storeroom placeB-12-3
qty_on_handDerived from movements14
reorder_point (min)Usage × lead time + safety10
max_qtyOrder up to this level24
unit_costCurrent value18.40 EUR
lead_time_daysSupplier time12
vendor_idPreferred supplierVendor record
Issue line fieldExample
issue_id770118
wo_noWO-26-0412
part_no / bin_idSEAL-24V-EPDM, B-12-3
qty10 (negative for a return)
unit_cost_at_issue18.40 EUR
issued_at / by2026-06-18 22:30, store

Ten seals at 18.40 EUR is the 184 EUR parts cost on that work order. Keep unit_cost_at_issue on the line, because a price change must not rewrite last year's repairs.

qty_on_hand is the result of movements, never a typed number. The reorder point arithmetic is worked through in our tool crib data model and our spare parts management guide.

Which part fits which asset

Most plants skip this table, and then a technician spends 20 minutes in the store guessing.

FieldExample
asset_no + part_noFIL-01 + SEAL-24V-EPDM (the key)
component_idValve block
qty_per_asset24
is_criticalYes, hold stock
superseded_byNewer part number, if any
sourceOEM spare list or past usage

Allow the link on asset_class too. One entry then covers all six fillers instead of six.

Keys and foreign keys

TablePrimary keyForeign keys
assetasset_noparent_asset_id, location_id, cost_centre
metermeter_idasset_no
readingreading_idmeter_id, wo_no (nullable)
pm_planplan_idasset_no, meter_id, task_list_id
requestrequest_noasset_no, reported_by
work_orderwo_noasset_no, plan_id, request_no, assignee_id
wo_failurefailure_idwo_no, component_id, problem, cause, remedy
labour_bookingbooking_idwo_no, employee_id
partpart_nobin_id, vendor_id
part_issueissue_idwo_no, part_no, bin_id
asset_partasset_no + part_nocomponent_id

Three joins earn their keep: asset to work order (history and cost), work order to part issue (what a repair cost) and meter to plan (usage based PM).

Six supporting lists sit behind these keys and each needs its own table: location, cost centre, bin, vendor, task list and employee. Point at them, because a code list kept as typed text rebuilds the free text problem one column further down.

Worked example: MTBF and MTTR for one filler

Asset FIL-01, period 1 June to 31 August 2026. The run hour meter read 12,480 h at the start and 13,480 h at the end.

Three corrective work orders carry is_failure = yes. The two preventive jobs are excluded, because planned work is not a failure.

Work orderDowntime
WO-26-0412 (seal leak)240 min
WO-26-0455 (capper drive)185 min
WO-26-0501 (sensor fault)95 min
Total520 min (8.667 h)

1. The definitions used

  • Operating time = meter delta = 13,480 minus 12,480 = 1,000.0 h. The counter runs only while the machine runs, so downtime is already excluded.
  • Downtime per failure = downtime_end minus downtime_start, not labour hours.
  • Failure = a work order on this asset with is_failure = yes.

2. MTTR

Mean time to repair = total downtime ÷ number of failures.

  • 520 ÷ 3 = 173.3 minutes, or 2.89 h.
  • Wrench time instead of downtime gives a smaller number, so state which one you mean (mean time to repair).

3. MTBF

Mean time between failures = operating time ÷ number of failures.

  • 1,000.0 ÷ 3 = 333.3 operating hours.
  • Strictly, "between" failures includes the repair: (1,000.0 + 8.667) ÷ 3 = 336.2 h.
  • The gap is 0.9% here, and it grows on assets with long repairs, so put the definition in the report header (mean time between failures).

4. Availability, and which one it is

  • Use the MTBF that excludes repair: 333.3 ÷ (333.3 + 2.89) = 99.1%. Feeding the 336.2 h figure into the same formula counts the repair twice.
  • It is the same arithmetic as 1,000.0 ÷ 1,008.667, so treat that as a restatement, not an independent check.
  • This is inherent availability: uptime against uptime plus repair time, with no idle time and no planned maintenance in it.

The availability factor in OEE is a different number: run time divided by planned production time, which does include planned stops. The same filler can honestly show 99.1% on the reliability report and 90% on the OEE dashboard.

None of it works without is_failure, the downtime window and a meter reading at each end of the period. Drop one and you are counting jobs, not reliability.

What belongs in the CMMS and what in the ERP

The split is clean if you ask one question per field: is this about the physical machine, or about money and procurement?

CMMS ownsERP owns
Asset register and hierarchyBook value and depre­ci­a­tion
Meters and readingsPurchase orders and goods receipt
PM plans and task listsSupplier master and invoices
Work orders, downtime, failure codesLedger and cost centre list
Storeroom bins, min and maxInventory valuation
Technician time on the jobPayroll and labour rates

Two fields must be identical on both sides: part_no and cost_centre. If they drift, no maintenance cost report will reconcile.

Costs cross the line in one direction only. The CMMS holds a unit cost and a labour rate for reporting, copied from the ERP, while the ERP keeps the valuation and the payroll figure of record.

The rule of thumb: the CMMS raises the demand for a part, the ERP turns it into a purchase order. Our CMMS vs ERP comparison goes further, and what is a CMMS covers the basics.

Five modelling mistakes that cost you later

1. Location as free text

"Hall B, near the palletiser" cannot be grouped or counted. Make location a record with a parent and put the wording in a note.

2. No parent asset

A flat list of 900 machines cannot answer "what did line 2 cost last year". You will not retro-fit it later, because nobody re-tags 900 rows.

3. Meter readings without a rollover rule

The first wrap turns usage negative and the meter based plans stop firing. Nobody notices until a bearing does.

4. Parts booked outside the work order

Issue a seal to a cost centre instead of WO-26-0412 and stock stays right while history stays empty. You know the plant spent 30,000 EUR on spares, not which asset ate it.

5. Deleting instead of closing

Deleting a cancelled work order or a retired asset erases the evidence behind your MTBF, your backlog and your budget. Use a status and keep the row.

How Fabrico helps

Fabrico is an OEE platform with a full CMMS built in, and the working core of this model is in it out of the box. The deeper reference pieces here, such as meter rollover handling, three level failure code trees and an asset to part compatibility table, are a specification to compare against, not a feature list.

Assets carry a hierarchy, files, history and QR codes, and your team scans a machine or a part from the iOS, Android or web app to report a problem or open a work order.

Work orders run through a status workflow with approvals, shifts, assignment and time tracking, and preventive plans use recurring templates and conditional tasks. Inventory holds min and max levels, deliveries and stock-takes, with consumption booked against the work order.

Machine data from PLC connections, IoT sensors and AI cameras puts real run time and downtime beside the maintenance history, and the analytics include MTTR and MTBF by machine.

What it does not do: your team creates every work order, failure forecasting is not a standard feature, and there is no formal root cause investigation and no production planning. Problem, cause and remedy codes are never forced on a technician.

Want your asset register and work order history in one place? Book a 30 minute demo with a Fabrico consultant, no commitment, or contact us with your questions.

Frequently asked questions

What fields does a CMMS asset registry need?

Identity first: asset_no, parent_asset_id, name, asset_class, manufacturer, model and serial_no. Then context and money: location_id, cost_centre, criticality, status, commissioning_date, warranty_expiry, expected life, document links and the QR value.

How do you calculate MTBF and MTTR from work order records?

MTBF = operating hours ÷ number of failure work orders, MTTR = total downtime minutes ÷ the same count. Above, 1,000 hours and 3 failures give 333.3 h, and 520 downtime minutes give 173.3 minutes.

What is the difference between a maintenance request and a work order?

A request is a report that something is wrong, and it can be rejected or merged as a duplicate. A work order is authorised work on one asset, with an assignee, costs and a history.

Should spare part consumption be booked on the work order?

Yes, always. Booking a part to a cost centre alone keeps stock correct but loses the link to the asset, the failure and the repair cost.

What belongs in the CMMS and not in the ERP?

The asset hierarchy, meters and readings, PM plans, work orders with downtime and failure codes, and storeroom bins with min and max. The ERP keeps purchase orders, invoices, valuation, depreciation and payroll.

Latest from our blog

Define Your Reliability Roadmap
Validate Your Potential ROI: Book a Live Demo
Define Your Reliability Roadmap
By clicking the Accept button, you are giving your consent to the use of cookies when accessing this website and utilizing our services. To learn more about how cookies are used and managed, please refer to our Privacy Policy and Cookies Declaration