ModulesPlant RegisterEquipment Schedule Import

Equipment Schedule Import

The contract editor’s Equipment Schedule step (plant_hire) and Equipment Rates step (labour_hire) both support bulk authoring, so an admin doesn’t have to hand-key dozens of plant items one row at a time. There are two import sources, both reached from the same slide-over drawer:

  1. CSV / XLSX upload — drop a spreadsheet that matches the template.
  2. Import from rate source — one click copies the supplier’s existing rate lines for this contract’s project.

Both sources are append-only and use partial-import semantics: the request returns 200 even when some rows fail validation. Valid rows are committed; failures are reported individually (row number + reason) so the admin can fix and re-import.

Where it lives

agreement_typeEditor stepBacking table
plant_hireEquipment Schedulecontract_classifications
labour_hireEquipment Ratescontract_equipment_rates

Each endpoint is agreement-type guarded server-side: posting the plant_hire template to a labour_hire contract (or vice versa) returns a 400. The parsers and templates are shape-specific, and both classification target tables would otherwise collide.

CSV / XLSX upload

Click Bulk import (CSV / XLSX) on the step header (or the empty-state). Drop a .csv or .xlsx (≤ 10 MB). Validation runs server-side. Download the template first with Download template — it ships the exact header row.

plant_hire template columns

name,base_rate,billing_unit,monthly_minimum_smu,pro_rata_formula
ColumnRequiredNotes
nameYesEquipment name. Case-insensitive unique within the contract (≤ 255 chars).
base_rateYesPer-unit dollar rate, non-negative.
billing_unitNoshift_hours or meter. Blank defaults to shift_hours.
monthly_minimum_smuNoStored on the classification’s metadata (no native column) — annotation only.
pro_rata_formulaNoFree text, stored on metadata. No formula validation.

labour_hire template columns

name,description,meter_type,custom_unit_label,base_rate,minimum_engagement,monthly_minimum_smu,pro_rata_formula

meter_type is one of shift-duration, engine-hours, km, metres, loads, custom (blank defaults to engine-hours). custom_unit_label is required when meter_type is custom. monthly_minimum_smu and pro_rata_formula are native columns here.

Dedupe

Both within-file and against-existing duplicates are rejected (case-insensitive on name). Re-importing a name that already exists is a per-row “already exists” failure, not an update — there is no tombstoning. Dedupe is app-level (no DB unique constraint), so a concurrent double-submit could theoretically insert a duplicate; this is acceptable for the admin-only single-user flow.

Import from rate source

Click Import from rate source. This copies every active rate line from the supplier’s rate source (supplier_payment_method_lines) on this contract’s project into the Equipment Schedule. Each line maps to a row: the line description (falling back to code) becomes the equipment name, and the line rate becomes the base rate. Lines with no name or a negative rate are reported as per-row failures.

You pick a default billing unit (plant_hire) or meter type (labour_hire) applied to every imported row, then confirm. Same dedupe + append-only semantics as the file upload.

Security note (Zero-Trust)

The link-project is resolved server-side from the contract — the request never carries a link_project_id. This is deliberate: the underlying ListPaymentMethodLinesByLinkProject query is not tenant-gated, so a client-supplied link-project id would be a cross-tenant cost-book copy. An optional payment_method_id (when supplied) is asserted to belong to the contract’s resolved link-project before its lines are read.

After an import: cost allocations refresh

A successful import chains refreshContractMatches(orgId, contractId, projectId) so the newly imported rows’ cost allocations are populated (matchable) instead of being left unallocated. Unallocated equipment rows would block the Plant Register auto-docket chain, so this refresh is load-bearing — see the Cost Allocation step, where imported rows appear as allocation rows immediately after the import.