JOURNAL / / MODELLING

Modelling a hire fleet: assets, availability and the booking that cannot double up

How Opus Hire represents units, categories, calendars, holds and returns, and why availability is a query over the ledger rather than a grid you keep up to date.

Every hire business has an availability problem, and almost every one of them has solved it with a picture. A whiteboard with the week across the top. A spreadsheet with a tab per month. A wall calendar with a pencil on a string. The picture is a record of what someone believed at the moment they drew it. The problem is that the belief and the fleet drift apart, and the drift is where the double booking lives.

This entry is about how Opus Hire models a fleet so that the picture cannot drift, because there is no picture. There is a ledger of work, and availability is a question you ask it.

Units and categories are different things

The first modelling decision is the one most off-the-shelf booking tools get wrong. A customer does not book a 6x4 box trailer. They book one of your 6x4 box trailers, and they do not care which. The yard cares which, because unit 3 has a bent drawbar and unit 1 is due for a service on Thursday.

So the model has two entities where a calendar has one:

  • A category is the thing the customer sees: "6x4 box trailer, $55 a day". It has a rate card, a description, terms, and a set of units.
  • A unit is the physical asset: registration, condition, location, service history, and its own calendar.

A booking is made against a category and allocated to a unit. At booking time the question is "does this category have any unit free for these dates", not "is this specific trailer free". Allocation can happen immediately, or the morning of pick-up when the yard knows which units actually came back clean. The customer's booking is unaffected either way.

This one split removes most of the reasons people give for keeping a whiteboard. The whiteboard was doing allocation by hand, and conflating it with availability.

Availability is a query, not a grid

In our systems there is no availability table. There is a table of holds: a unit, a start, an end, and a reason. A confirmed booking is a hold. A service window is a hold. "Unit 3 is at the panel beater" is a hold. A quote that the customer has fifteen minutes to pay is a hold, and it expires.

Availability for a category over a date range is then a query:

units in category
  minus units with any hold overlapping [start, end)
  = available units
count(available units) > 0  →  bookable

The grid you see on a booking page is drawn from that query at the moment the page loads and re-checked at the moment the customer pays. Two people can look at the same Saturday at the same time; only one of them can create the hold, because the database enforces that no two holds on the same unit overlap. The second one is told, politely, that the trailer went ten seconds ago and here is the next one.

That constraint is the whole point. It is not a rule in the application code that someone might forget to call from the admin screen. It is a constraint on the table, so a phone booking typed in by the counter staff and an online booking made at 11pm are subject to the same law.

Opus Hire flow: availability, booking, terms, deposit, return, ledger, accounting FIG. 02 — OPUS HIRE AVAILABILITY · 14 DAYS · 5 UNITS AVAILABILITY a query, not a grid BOOKING dates · unit · customer TERMS signed at booking DEPOSIT card · hold on bond RETURN condition · charges LEDGER every cent, one line ACCOUNTING Xero · QBO · MYOB A BOOKING CANNOT BE CREATED FOR A UNIT THAT IS HELD. THE GRID IS A VIEW OF THE LEDGER, NOT THE OTHER WAY ROUND.
The availability grid is a view of the holds table. The pipeline to its right is what a hold turns into.

Time is half-open and local

Two small decisions that save a great deal of grief later.

Holds are stored as half-open intervals, [start, end). A trailer returned at 4pm and picked up again at 4pm is not a conflict. Closed intervals produce phantom clashes at the boundary and someone ends up "fixing" them by hand.

Times are stored in UTC and displayed in the yard's timezone. This sounds like pedantry for a business in Logan until daylight saving in another state, an interstate customer, or, eventually, a customer in Singapore. The model does not care where the yard is. The display does.

Returns close the loop

A booking does not end when the calendar says so. It ends when a unit is returned, and the return is its own record: who, when, condition photos, fuel, kilometres, and a list of charges. The return record is what releases the hold, which is why a late return automatically extends availability blocking on that unit rather than silently double-booking the next customer.

The return is also where the money side starts to matter, which is the subject of the next entry. For this one, the point is that "returned" is a fact with a timestamp and a signature, not a tick on a whiteboard that someone remembers to make.

What this looks like in practice

When we set up Opus Hire, week one is mostly this model. We take the fleet list as it exists, usually a spreadsheet or a photograph of the wall, and load it as categories and units. We load the last few months of bookings as holds so the history is there. Then we stand next to the counter and ask about the exceptions: the regular who keeps a trailer over the weekend, the unit that only goes out to people the owner knows, the category that is really two categories.

By Friday the owner can make a booking on a private address against their own fleet, see the hold appear, try to double-book it, and fail. That moment, watching the system refuse, is usually when they stop asking about the whiteboard.