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:
- CSV / XLSX upload — drop a spreadsheet that matches the template.
- 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_type | Editor step | Backing table |
|---|---|---|
plant_hire | Equipment Schedule | contract_classifications |
labour_hire | Equipment Rates | contract_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| Column | Required | Notes |
|---|---|---|
name | Yes | Equipment name. Case-insensitive unique within the contract (≤ 255 chars). |
base_rate | Yes | Per-unit dollar rate, non-negative. |
billing_unit | No | shift_hours or meter. Blank defaults to shift_hours. |
monthly_minimum_smu | No | Stored on the classification’s metadata (no native column) — annotation only. |
pro_rata_formula | No | Free 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_formulameter_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.