Sub-Hire & Procurement
Purchase orders, virtual stock intakes, sub-hire shortage resolution, cost apportionment, and supplier returns.
Overview
When owned stock cannot cover a shortage, sub-hire procures supply from suppliers. The loop connects shortage detection → purchase order → virtual stock intake → availability supply → cost apportionment → optional supplier return.
Event-sourced purchase orders and virtual stock intakes follow the same Verbs projection pattern as invoices and rentals.
| Area | Route |
|---|---|
| Purchase orders | /purchase-orders |
| Virtual stock intakes | /virtual-stock |
| Rental shortages | /rentals/{rental}/shortages |
Supplier costs
CatalogueItems can carry supplier cost reference data — preferred supplier, unit cost, and lead time. Manage on the catalogue item detail page and via POST /api/v1/catalogue_items/{catalogue_item}/supplier_costs/{id}/set_preferred.
Supplier costs inform PO line pricing and margin reporting; costs.view gates cost field visibility on POs and intakes (not whole resources).
Purchase orders
PO lifecycle:
| Status transition | Action |
|---|---|
| Draft | Create and edit lines |
| Send | Mark sent to supplier |
| Receive | Record goods received; triggers intake workflow |
| Cancel | Cancel open PO |
PO tax uses the purchase axis (distinct from sales tax on invoices). See Purchase Orders API.
Virtual stock intakes
Virtual stock intakes represent supplier stock entering your availability pool as signed supply — the availability engine treats confirmed intake quantity as available for allocation without adding owned stock.
Intake lifecycle:
| Action | Purpose |
|---|---|
| Confirm | Confirm intake terms |
| Receive | Record physical receipt |
| Apportion | Allocate cost across rentals |
| Return | Return stock to supplier |
| Cancel | Cancel intake |
See Virtual Stock Intakes API.
Sub-hire shortage resolver
The SubHireResolver is the seventh shortage resolution path (alongside the six non-PO resolvers documented in Shortages). It creates or consolidates purchase orders from shortage lines.
Batch consolidation groups shortages across rentals into a single PO where supplier and dates align.
Flow: PO receipt → intake confirm → shortage clear — receiving a PO creates or updates the intake, which feeds availability and clears the originating shortage.
Cost apportionment
When one intake serves multiple rentals, cost apportionment distributes supplier cost using one of five strategies through the cost event stream. Date changes on rentals trigger recalculation.
Override apportioned cost per rental: PUT /api/v1/virtual_stock_intakes/{id}/cost_overrides and DELETE .../cost_overrides/{rental}.
Returns and P&L
Return-to-supplier workflow records stock leaving your pool. Intake P&L surfaces per-intake and per-rental profit impact when the viewer has cost visibility.
Barcoded virtual stock
Tracked sub-hire inventory can use barcoded virtual-stock assets — individual serial numbers on intake lines for scan-based fulfilment.
Using it in the app
Sub-hire UI spans catalogue item supplier costs, shortage resolution, purchase orders, virtual stock, and read-only cost apportionment on rentals.
Navigation
| Screen | Route | Nav location | Gate |
|---|---|---|---|
| Supplier Costs | /catalogue-items/{catalogue_item}/supplier-costs |
CatalogueItem tab | Tab visible with costs.view |
| Purchase Orders | /purchase-orders |
Finance mega-menu + Purchasing sidebar | purchase_orders.view |
| Virtual Stock | /virtual-stock |
Catalogue mega-menu + sidebar | virtual_assets.view |
| Rental Shortages | /rentals/{id}/shortages |
Rental tab | shortages.view + allocation visibility |
| Rental Costs | /rentals/{id}/costs |
Rental tab | rentals.view; amounts @costs |
Virtual Stock is labelled Sub-hire supply windows in the header mega-menu. Finance-only users (invoices.access without catalogue access) do not see Virtual Stock links.
Supplier Costs tab
On a catalogue item detail page, open Supplier Costs (tab requires costs.view and catalogue-items.view).
Table columns: Supplier, Basis, Cost, SKU, Lead Time, MOQ, Warehouse, Preferred
| Action | Permission |
|---|---|
| Add Supplier Cost | catalogue-items.supplier_costs |
| Set preferred | catalogue-items.supplier_costs |
| Edit / Delete | catalogue-items.supplier_costs |
Without costs.view, the tab is hidden — not merely redacted.
Shortages tab — sub-hire resolution
Visible for Order state or Quote + Reserved. Tab badge shows unresolved shortage count.
Sub-hire option cards appear in Detected Shortages with resolver options sized to the shortfall. Cards can show Preferred (preferred supplier first), Partial, and Auto badges. Resolve across jobs offers batch consolidation into a single PO when supplier and dates align — Apply batch.
Lifecycle buttons on applied resolutions: Confirm, Start, Fulfill, Cancel, Fail.
Flow in UI: pick sub-hire option → draft PO created → receive PO → intake confirm → shortage clears (see PO and intake screens below).
Purchase orders
Index (/purchase-orders) — Finance meta label; status chips; data table with live filters; columns Number, Type, Status, Supplier, Warehouse, Total, Expected; New Draft when purchase_orders.create.
Show page actions (all writes also require costs.view):
| Action | Permission | Notes |
|---|---|---|
| Edit | purchase_orders.edit |
Draft only |
| Send | purchase_orders.send |
Marks sent to supplier |
| Receive | purchase_orders.receive |
Modal with per-line quantities (partial receipt supported) |
| Cancel | purchase_orders.cancel |
Open PO |
| Delete | purchase_orders.delete |
Draft only |
For sub_hire PO lines, Create intake links pre-fill /virtual-stock/create?purchase_order_id=…&purchase_order_item_id=… with supplier unit costs from the PO line.
Documents card on the PO show page lists purchase-order document projections (header/body/footer snapshot) with document number, type, created time, and a Download PDF link. Empty state: "No documents."
Virtual stock intakes card lists linked intakes with Return to supplier when applicable.
Virtual stock intakes
Index — status chips; columns Number, CatalogueItem, Warehouse, Status, Window, Qty, Total Cost (when costs.view); New Intake.
Show page:
| Action | Permission |
|---|---|
| Confirm | virtual_stock.confirm |
| Receive | virtual_stock.receive (partial/over-receipt handled in receive modal) |
| Return to supplier | virtual_stock.return |
| Cancel / Delete | Draft only |
Panels cover catalogue item, warehouse, quantity, allocations, availability window, linked PO, per-source availability breakdown, and P&L/apportionment when costs.view.
Late return appears as an amber badge on the intake show page when the intake window has passed without return.
Rental Costs tab
Read-only table: Description, Cost Type, Transaction Type, Amount (@costs), Qty, Optional. Lines linked to an intake show a Intake {number} link to /virtual-stock/{id} when the viewer has cost visibility.
Manual cost overrides and apportionment editing are API-driven — the Costs tab does not expose override controls.
Permissions summary
| Permission | UI effect |
|---|---|
costs.view |
Supplier Costs tab, PO/virtual-stock writes, cost columns |
catalogue-items.supplier_costs |
CRUD supplier costs |
shortages.view / shortages.resolve |
Shortages tab view / resolution buttons |
purchase_orders.* |
PO list and lifecycle actions |
virtual_stock.* |
Intake list and lifecycle actions |
Related documentation
- Shortages — detection, gate, and resolver panel
- Equipment Availability — signed supply in availability calculations
- Tax & Compliance — purchase-axis tax
- Purchase Orders API
- Virtual Stock Intakes API