Rentals
Quote, order, and fulfil rental jobs — the rental lifecycle, line-item editor, quote versioning, and state transitions.
Overview
An rental is the central document of a rental job — it begins life as a quote and matures into a confirmed order that is allocated, dispatched, and returned. Every rental carries a customer, a warehouse, dates, line items, costs, and a running set of totals, and records its full history as an event stream so nothing is ever lost.
Rentals use a two-axis model: a high-level state (Enquiry → Quote → Order) and a per-state status (for example Quote/Reserved or Order/On Hire). The platform UI drives the same action classes the Rentals API uses, so the web and API paths stay in lock-step — validation, authorisation, audit logging, and rental.* webhooks apply identically wherever a change originates.
Route: /rentals
Rentals live under the Rental Planning section of the main navigation, gated on the rentals.access permission.
The Lifecycle
A rental moves through three states, each with its own set of statuses:
| State | Statuses |
|---|---|
| Enquiry | Open |
| Quote | Provisional, Reserved, Lost, Dead, Postponed |
| Order | Active, Dispatched, On Hire, Returned, Checked, Complete, Cancelled |
A new rental starts as a Enquiry. You build it up — add the customer, dates, and line items — then convert it to a quotation to send to the customer. Once accepted, you convert it to an order, at which point its equipment is committed and can be allocated, dispatched, and returned. The state and status are shown as badges in the page header throughout.
State-transition actions
The transition controls live in the split-button beside the page header. Only the actions legal for the current state and status are offered (the page asks the same available_actions engine the API exposes, so an illegal move is never presented and a raced move surfaces a clear message rather than an error):
| Action | Moves | Notes |
|---|---|---|
| Convert to Quote | Enquiry → Quote | Promotes a draft into a sendable quote. |
| Convert to Order | Quote → Order | Commits the job. Gated by the warehouse's shortage policy — a short order may be blocked, warned, or allowed. |
| Reinstate | Lost / Dead / Postponed / Cancelled → re-opened | Brings a closed rental back to life. |
| Revert to Quote | Order → Quote | Only for an open order with no dispatched assets — an order that has equipment out cannot be reverted. |
| Unlock Rates | — | Releases the FX and tax locks (see FX & tax locks). |
Within a state you can also change status (for example mark a quotation Reserved or an order On Hire) without changing the overall state.
Note: Allocation, dispatch, and return are driven from the order's line items and assets once it is an order — see the Asset allocation & dispatch section below.
Creating an Rental
Route: /rentals/create
The create form captures the rental header:
| Field | Description |
|---|---|
| title | A short title for the job (required). |
| Account | The customer the job is for (searchable picker). |
| Warehouse | The warehouse the job is booked against — drives availability and numbering. |
| Currency | The transaction currency; the exchange rate is snapshotted at creation. |
| Dates | The hire start and end dates. |
| Charge dates | The billable period, which may differ from the physical hire window. |
| Reference | A customer reference such as a PO number. |
| Tags | A free-text tag list. |
| Prices include tax | Whether entered prices are tax-inclusive (set at create time only). |
| Custom fields | Any custom fields defined for rentals, grouped in the right-hand column. |
On save the form builds the same CreateRentalData DTO the API uses, runs it through the CreateRental action, and redirects to the new rental's detail page. The rental number follows the format configured in Rental Settings.
The Rental Detail Page
Route: /rentals/{id}
The detail page presents the rental across tabs:
| Tab | Contents |
|---|---|
| Items | The line-item editor (see below). |
| Costs | Ad-hoc costs added to the job. |
| Versions | Quote revisions and alternatives. |
| Shortages | The shortage panel and resolvers. |
| Activities | The job's activity timeline. |
| Custom Fields | User-defined field values. |
| Files | Attached documents and images. |
The page header carries breadcrumbs, the state and status badges, an Edit button, and the split-button of transition actions. Availability badges on the page update live as bookings, returns, and stock movements occur elsewhere.
The Line-Item Editor
The Items tab is the heart of a rental. It is an editable, grouped, sortable table of everything the customer is hiring or buying, with live totals.
Adding catalogue items
Each add row carries a two-tier catalogue item search. As you type, an instant client-side index returns matches immediately; a debounced server search then fills in anything newer in the catalogue, with a "new" badge marking server-only hits. The server tier uses PostgreSQL trigram (pg_trgm) matching for fuzzy, typo-tolerant results.
A quick-add bar parses a quantity and a term together — typing 6 spiider adds six of the matched catalogue item in one step. Keyboard navigation (up/down to move, Enter to add, Esc to dismiss) keeps the whole flow hands-on-keyboard.
Grouping: custom sections and automatic nesting
Lines are organised into collapsible groups, with subtotals rolled up per group:
- Custom sections — you can create named sections (for example "Lighting" or "Day 1") and assign lines to them. Sections are managed per rental.
- Automatic catalogue item grouping — any line not assigned to a custom section is grouped automatically by its catalogue item group, following the catalogue item-group tree (parent group → the catalogue item's own group).
You can reorder lines by dragging the row handles (wire:sort), including dragging a line between groups or sections — dropping a line into a section assigns it there, and dropping it back into the automatic area clears the assignment.
Accessories
CatalogueItems that carry accessories show their accessories as sub-rows nested beneath the principal catalogue item line. Each accessory is a real, persisted line item (added via the same event-sourced ItemAdded path as any catalogue item line) that hangs under its principal and inherits the principal's quote-version scope. Accessory quantities ripple from the parent line's quantity by their configured ratio. Accessories are priced through the rate engine and generate availability demand in their own right — they are not free or presentational, though an accessory configured at a zero rate naturally totals to nil.
Per-line edits
Each line has an edit action exposing:
- Quantity — changing it re-rolls the accessory ripple and recomputes totals.
- Price override — set a manual unit price for the line.
- Discount — apply a per-line discount.
- Dates — override the line's own charge dates where they differ from the header.
- Optional — flag a line as optional (quoted but not committed).
- Substitute / Remove — swap the line's catalogue item or remove the line entirely.
Live totals
Every edit flows through the event-sourced line-item actions (add, change quantity, override price, set discount, toggle optional, substitute, remove), and the editor then re-reads the rental so the line, sub-group, group, and grand totals recompute live. Totals follow the framework's ex-tax model: line and group totals are net, and tax is computed finally, grouped by tax type, via the rate and tax engines.
Status chips
Each line shows an availability status chip (available / reserved / booked-out) drawn from the availability engine, so you can see at a glance whether the equipment on a line is free for the job's dates.
Quote Versioning
The Versions tab lets you maintain multiple versions of a quote and pick the one the customer accepts. Two kinds of version exist:
- Revisions — a successive iteration of the quote; activating a revision supersedes its parent.
- Alternatives — a parallel option presented alongside others (for example a budget vs premium package).
Versions are shown as a version tree, with the active version marked and each node showing its type, status, total (net), author, and what it revises. The per-version actions mirror the version lifecycle and are only offered when legal:
| Action | Description |
|---|---|
| Create version | Start a revision or alternative, optionally cloning an existing version's items. |
| Activate | Make a version the active one. Confirm-gated, because activating swaps the engine's reserved demand to the newly active version's lines. |
| Send | Mark a version as sent to the customer. |
| Accept / Decline | Record the customer's decision (decline captures a reason). |
| Rename | Change a version's label. |
| Delete | Remove a version (not allowed for the active version while others depend on it). |
Select any two versions to produce a diff — a table of added, removed, and changed lines with the net total delta between them.
When you convert to an order, the accepted version (or the active one if none is explicitly accepted) becomes the order, and the rest are superseded; only the confirmed version's demand carries into the order.
FX and Tax Locks
When a rental is committed, its exchange rate and tax treatment are locked so later rate or tax-rule changes don't silently alter an agreed deal. The page header shows when locks are active, and a user with the rentals.unlock_rates permission can release them with the Unlock Rates action — for example to re-rate an order after a deliberate FX or tax change. Releasing the locks lets subsequent edits pick up current rates again.
The locks freeze the rate and tax treatment, not the line set. You can still add, remove, or re-date lines on a committed order — the totals recompute, but always at the frozen exchange rate (and tax stays fixed while the tax lock is active). So a locked order's total can still change when its lines genuinely change; what the locks prevent is a later FX-rate or tax-rule change re-pricing the deal you already agreed.
Asset Allocation & Dispatch
Once a rental is an order, its line items are allocated, dispatched, and returned:
- Barcoded catalogue items (one physical unit per asset) are allocated to specific assets, then booked out and checked back in. Batch actions let you allocate, prepare, book out, and check in many assets at once.
- Bulk catalogue items are dispatched and returned by quantity rather than by individual asset.
These movements drive the order's statuses (Active → Dispatched → On Hire → Returned → Checked → Complete) and feed the availability and shortage engines in real time.
Pricing pipeline
Rental totals run through the four-stage pricing pipeline:
- Price category tier from the rate engine
- Discount rules — apply a category manually or rely on auto-apply rules
- Surcharge groups — toggle optional surcharges; override per line
- Deal price — flat headline or proportional override; mutually exclusive with price lock
Use Recalculate to refresh lines from the pipeline. Deal price and price lock cannot both be active — checkout requires a consistent snapshot.
API: apply_discount_category, toggle_surcharge_group, override_item_surcharge, deal_price — see Rentals API.
Invoicing and checkout
The Invoices tab (/rentals/{id}/invoices) generates draft invoices from the active quote version. Checkout freezes lines, costs, surcharges, and tax — later rental edits do not alter issued documents.
Standard, deposit, and pro-forma checkout types are supported. Partial-quantity invoicing bills only part of a line. See Invoicing & Payments and POST /api/v1/rentals/{id}/invoices.
Documents tab
The Documents tab (/rentals/{id}/documents) attaches generated PDFs, previews documents, and manages public share links. Templates render through the Document Engine.
Related
- Rentals API — the REST endpoints behind this UI.
- Invoicing & Payments — checkout and billing lifecycle.
- Documents & Templates — PDF generation on rentals.
- Pricing & Discounts — pipeline stages and admin configuration.
- Equipment Availability — the availability calendar and demand timeline.
- Shortages — detecting and resolving inventory shortfalls.