Making a hire fleet measurable across three systems
A hire business makes money by keeping machines out on hire. Three systems each held part of the truth about any given machine, and none of them could answer the question the business most needed: which units earn, and which sit.
- The goal
- Answer utilisation and revenue per machine — and do it without asking three systems to each become the system of record for something they are not built to own.
- What it was costing
- Fourteen dashboards existed and none produced a reliable baseline. Fleet performance was the whole point of the phase, and the data model underneath could not support it: the join between commercial history and service history did not hold.
Client anonymised — an industrial equipment hire company, delivered under another company. Company names, fleet numbers, partner systems and record identifiers are withheld or changed. Every figure below is real.
- Custom properties in scope
- 146 custom properties mapped
- Orphaned records found
- ~14,930 placeholder line items from one import
- Baseline established
- 4,372 deals across nine months
- Systems arbitrated
- Hire system, telematics, CRM
Three systems, one machine, no answer
The hire system runs the commercial process. A telematics platform reads the machines. The CRM holds the sales and service relationship. Each is correct about its own domain, and together they could not tell the business what a given unit earned or how hard it worked.
The obvious response is to nominate one system as the single source of truth and force the others to conform. That fails here for a practical reason: servicing genuinely belongs in telematics, the hire process genuinely belongs in the hire system, and neither is going to move. The architecture question was not which system wins. It was which system owns which part of an asset's life, and what single pipe carries readings between them.
The route agreed was explicit about that division: telematics owns servicing, the hire-system middleware is the single pipe pushing meter readings, and the CRM owns days-based utilisation and the KPI dashboards on top. Utilisation is measured in days out rather than hours run, because that is what the commercial model actually charges for.
Every machine existed twice, and that was correct
Inside the CRM, each unit appeared twice: as a product, which is what a line item on a deal points at, and as an asset on a custom object, which is what a service ticket points at. Same machine, two records.
Proving they were duplicates rather than two genuinely different things took direct comparison on specific units — matching fleet number, matching internal identifier — confirmed on more than one unit, because a single coincidence is not a data model.
The reflex is consolidation, and it would have been wrong. Both records exist because two different processes need to attach different things to a machine. The problem was never that there were two. It was that nothing reliably tied them together.
The join key everyone was using could not hold
The obvious key was the stock number: human-readable, present on both objects, and filthy. Real values in the fleet included the same number with a trailing underscore, the same number with a suffix, one with an embedded space, and several records literally named as unallocated with a counter appended.
Joining on that string produces a result that looks plausible and is quietly wrong for part of the fleet — the worst kind of wrong, because the report still renders and nobody questions it.
A better key was already present on all three objects, numeric and system-generated rather than typed by a person. Identifying it turned an unreliable string match into an exact join, and made every downstream utilisation question tractable.
The number that looked duplicated but was two measurements
Machine hours appeared in two places, which looked like the same redundancy in a different costume. It is not, and getting this wrong would have destroyed the data.
The asset carries a current reading and the date it was taken — lifetime state, the odometer. The line item carries a start reading, an end reading and hours used — a per-hire event, the trip meter. They share a unit and measure different things.
Pushing per-hire hours onto the asset would mean concurrent hires overwriting each other. The asset needs its own meter because off-hire, standdown, workshop and breakdown tickets carry no line item at all, and service intervals are calculated against lifetime hours. Recognising that a number appearing twice is sometimes two numbers is most of the work in a model like this.
What the mapping exposed
With the model settled, an association between line items and assets that already existed but had never been populated could finally be filled, and the client's own developer ran the catalogue refresh and backfill against it — which is the right division of labour, since he owns that middleware.
The mapping also surfaced problems that had been invisible. Roughly fourteen thousand nine hundred orphaned placeholder line items from a single historical import, carrying no deal, no product reference and a zero value. Service tickets carrying two different association labels to the same asset — a double-count waiting to happen in any report that groups by them. Meter readings that are obviously junk, including one reading that is almost certainly kilometres from a different gauge.
Separately, a nine-month activity export across calls, meetings and 4,372 deals gave the business its first reliable baseline of what the fleet and the sales team were actually doing, against fourteen existing dashboards that had been audited into a fix list rather than trusted.
One decision deliberately left un-automated
The ticket creation form needed to guarantee an asset gets attached. The flexible option allowed any association label; the strict option required one specific label every time.
Strict won, on the client's call, and the reasoning is worth keeping. The ticket pipeline already distinguishes breakdowns from customer repairs and warranty jobs. Carrying that same distinction in the association label as well would duplicate the meaning in two places, and two sources of one truth diverge eventually.
That is the same principle running through the whole engagement, applied at a smaller scale: decide where a fact lives, once, and make everything else reference it.
What changed
- Commercial history and service history can be joined reliably for the first time, on a system-generated key rather than a hand-typed string that fails silently on part of the fleet.
- Lifetime machine state and per-hire consumption are modelled as separate measurements, so utilisation and service intervals can both be calculated without one overwriting the other.
- Ownership is settled across three systems — servicing in telematics, one pipe carrying readings, utilisation and KPIs in the CRM — so no system is asked to be the record for something it does not own.
- Roughly 14,930 orphaned line items, duplicate ticket associations and junk meter readings were identified with evidence, giving the client a defined cleanup list instead of unexplained variance in reports.
- A nine-month activity baseline across 4,372 deals replaced fourteen dashboards that nobody could reconcile.
Where it stands
Ongoing. The model and the join are settled and the client-side backfill is verified. Fleet utilisation reporting is being built on top; the orphaned line items, the duplicate ticket associations and the telematics data feed remain open and sequenced with the client's own developer.
Questions people ask about this
How do you decide which system is the source of truth?
Usually you do not pick one. Decide which system owns which part of the lifecycle, and what single pipe carries data between them. On this engagement servicing stayed in the telematics platform, the hire-system middleware became the one pipe pushing meter readings, and the CRM owned utilisation and reporting on top — because none of those three was going to give up the domain it genuinely owns.
Should you deduplicate records that represent the same physical asset?
Not automatically. Two records for one machine can be correct when two different processes need to attach different things to it — commercial history on one, service history on the other. The real problem is usually not the duplication but that nothing reliably joins them.
What makes a bad join key between systems?
Anything a human types. A stock or fleet number looks ideal because it is readable and appears everywhere, but real data contains trailing underscores, suffixes, embedded spaces and placeholder values. Joining on it produces a report that still renders while being wrong for part of the fleet. Prefer a system-generated numeric identifier present on every object.
Why would machine hours be stored in two places?
Because they are two different measurements. The asset carries a lifetime reading — the odometer — while a hire line item carries start, end and hours used for that hire, which is the trip meter. Collapsing them breaks both: concurrent hires overwrite each other, and service intervals need lifetime hours from tickets that have no line item at all.
How is hire fleet utilisation best measured?
By days out rather than hours run, when that is what the commercial model charges for. Hours matter for servicing and for machine wear, but if a business bills by the day then days out and not standing down is the number that maps to revenue.
When three systems each hold part of the truth, the answer is rarely to pick a winner. It is to decide what each one owns and what carries between them — before building the reports.
Strategic Planning & KPI Blueprint