Key takeaways
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 record is the spine. Every reading, plan, work order and part consumption points at it.
| Field | What it holds | Example |
|---|---|---|
| asset_ | Unique tag, never reused | FIL-01 |
| parent_ | Asset one level up | LINE-02 |
| name | Plain name | Rotary filler 24 valve |
| asset_ | Type from a fixed list | FILLER |
| manufacturer | Maker on the nameplate | See the plate |
| model | Model designation | RF-24/6 |
| serial_ | Maker's serial | 2019-8841 |
| year_ | Year the machine was made | 2019 |
| commissioning_ | Handover to production | 2019-11-04 |
| location_ | Link to a location record | P1-HALL-B-BAY3 |
| cost_ | Link to the finance list | CC-4120 |
| criticality | A, B or C | A (stops the line) |
| status | Lifecycle state | Operating |
| warranty_ | Last day of cover | 2021-11-03 |
| expected_ | Planning life | 15 |
| document_ | Manual, drawings, spare list | 3 files |
| qr_ | What the label encodes | FIL-01 |
Two fields do the work. parent_asset_id builds the hierarchy (site, area, line, machine, component) The levels, the fields that belong at each one and the rules for moving a machine are in our asset hierarchy data model. 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 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.
| Status | Triggered by |
|---|---|
| Planned | Asset created, not yet installed |
| Commissioning | Installed, tests running |
| Operating | Handover signed, commissioning_ |
| Standby | Available but not in use (spare pump) |
| Down | Derived, not stored: an open downtime window exists |
| Stored | Removed from the line, kept |
| Decommissioned | Retired, 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 work order is the unit of work and the unit of cost. One work order, one asset.
| Field | What it holds | Example |
|---|---|---|
| wo_ | Unique number | WO-26-0412 |
| asset_ | Asset worked on | FIL-01 |
| wo_ | Fixed list | Corrective |
| plan_ | PM plan behind it, else empty | Empty |
| request_ | Request behind it, else empty | REQ-26-1188 |
| priority | 1 to 4 | 1 (emergency) |
| status | Lifecycle state | In progress |
| requested_ | When it was raised | 2026-06-18 21:40 |
| planned_ | Scheduled window | 2026-06-18 22:00 |
| actual_ | Work really started, ended | 2026-06-18 22:05 |
| requested_ | Person who reported it | Shift leader |
| assignee_ | Technician responsible | Employee 2231 |
| trade | Skill needed | Mechanical |
| downtime_ | Asset stopped | 2026-06-18 21:38 |
| downtime_ | Asset handed back | 2026-06-19 01:38 |
| downtime_ | Planned or unplanned stop | Unplanned |
| is_ | Counts for MTBF | Yes |
| failure_ | Component plus problem, cause and remedy, one row each | 1 row |
| labour_ | Sum of time bookings | 7.0 |
| parts_ | Derived totals | 184 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_
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_
| Status | Triggered by |
|---|---|
| New | Created from a request or a plan |
| Approved | Approver signs off, if approval is on |
| Scheduled | Assignee and planned window set |
| In progress | First time booking, actual_ |
| Waiting | Blocked: part, access or shutdown |
| Completed | Work done, hours entered, codes where required |
| Closed | Costs settled, no more bookings |
| Cancelled | Withdrawn 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.
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.
| Field | Level 1 to level 3 |
|---|---|
| component_ | Filler, Valve block, Seat seal (a child asset row) |
| problem_ | Leak, External leak, Product at valve |
| cause_ | Wear, Normal wear, Seal life exceeded |
| remedy_ | Replace, 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.
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.
| Field | Example |
|---|---|
| request_ | REQ-26-1188 |
| asset_ | FIL-01 (scanned from the QR label) |
| reported_ | Operator 118, 2026-06-18 21:40 |
| description | Product leaking at valve 7 |
| asset_ | Yes |
| urgency | Safety, Line stopped, Normal |
| status | New, Accepted, Rejected, Duplicate |
| wo_ | WO-26-0412, set on acceptance |
| accepted_ | 2026-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.
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.
| Field | What it holds | Example |
|---|---|---|
| plan_ | Unique plan | PM-FIL-01-500H |
| asset_ | Asset covered | FIL-01 |
| task_ | Template of steps | TL-FILL-500H |
| trigger_ | Calendar, meter or condition | Meter |
| meter_ | Counter watched | MTR-FIL-01-H |
| interval | Value and unit | 500 h |
| lead_ | How early work appears | 7 |
| basis | Floating or fixed | Floating from last done |
| last_ | Last completion | 2026-07-18 |
| last_ | Counter at completion | 13,000 h |
| next_ | Derived | 13,500 h |
| trade | Skill needed | Mechanical |
| parts_ | Parts normally used | KIT-FIL-500H |
| shutdown_ | Needs the line stopped | Yes |
| Trigger | Fires when |
|---|---|
| Calendar | last_ |
| Meter | Latest reading reaches next_ |
| Condition | A measured value crosses a limit (vibration, temperature) |
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).
A meter belongs to an asset. A reading belongs to a meter, and readings are never overwritten.
| Field | Example |
|---|---|
| meter_ | MTR-FIL-01-H |
| asset_ | FIL-01 |
| meter_ | Run hours, h |
| source | PLC, sensor or manual entry |
| direction | Increasing only |
| rollover_ | 100,000 (5 digit counter) |
| offset | Added after a counter swap |
| reading_ | 884213, 13,480.0, 2026-08-31 |
| wo_ | Set when read during a job |
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.
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).
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.
| Field | What it holds | Example |
|---|---|---|
| part_ | Your number | SEAL-24V-EPDM |
| description | Plain text | Valve seat seal EPDM |
| manufacturer_ | Catalogue number | As on the OEM list |
| uom | Unit | pcs |
| bin_ | Link to the storeroom place | B-12-3 |
| qty_ | Derived from movements | 14 |
| reorder_ | Usage × lead time + safety | 10 |
| max_ | Order up to this level | 24 |
| unit_ | Current value | 18.40 EUR |
| lead_ | Supplier time | 12 |
| vendor_ | Preferred supplier | Vendor record |
| Issue line field | Example |
|---|---|
| issue_ | 770118 |
| wo_ | WO-26-0412 |
| part_ | SEAL-24V-EPDM, B-12-3 |
| qty | 10 (negative for a return) |
| unit_ | 18.40 EUR |
| issued_ | 2026-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.
Most plants skip this table, and then a technician spends 20 minutes in the store guessing.
| Field | Example |
|---|---|
| asset_ | FIL-01 + SEAL-24V-EPDM (the key) |
| component_ | Valve block |
| qty_ | 24 |
| is_ | Yes, hold stock |
| superseded_ | Newer part number, if any |
| source | OEM spare list or past usage |
Allow the link on asset_class too. One entry then covers all six fillers instead of six.
| Table | Primary key | Foreign keys |
|---|---|---|
| asset | asset_ | parent_ |
| meter | meter_ | asset_ |
| reading | reading_ | meter_ |
| pm_ | plan_ | asset_ |
| request | request_ | asset_ |
| work_ | wo_ | asset_ |
| wo_ | failure_ | wo_ |
| labour_ | booking_ | wo_ |
| part | part_ | bin_ |
| part_ | issue_ | wo_ |
| asset_ | asset_ | component_ |
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.
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 order | Downtime |
|---|---|
| WO-26-0412 (seal leak) | 240 min |
| WO-26-0455 (capper drive) | 185 min |
| WO-26-0501 (sensor fault) | 95 min |
| Total | 520 min (8.667 h) |
Mean time to repair = total downtime ÷ number of failures.
Mean time between failures = operating time ÷ number of failures.
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.
The split is clean if you ask one question per field: is this about the physical machine, or about money and procurement?
| CMMS owns | ERP owns |
|---|---|
| Asset register and hierarchy | Book value and depreciation |
| Meters and readings | Purchase orders and goods receipt |
| PM plans and task lists | Supplier master and invoices |
| Work orders, downtime, failure codes | Ledger and cost centre list |
| Storeroom bins, min and max | Inventory valuation |
| Technician time on the job | Payroll 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.
"Hall B, near the palletiser" cannot be grouped or counted. Make location a record with a parent and put the wording in a note.
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.
The first wrap turns usage negative and the meter based plans stop firing. Nobody notices until a bearing does.
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.
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.
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.
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.
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.
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.
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.
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.
Zakažite sastanak KSNUMKS-to-KSNUMKS sa našim stručnjacima ili se direktno upišite u naš besplatni plan.
Nije potrebna kreditna kartica!