Materials Contracts

A docket cannot be created against a supplier until that supplier holds at least one Materials contract bound to the project, with all materials cost-allocated. This page walks the HC Project Admin through the authoring flow.

Project scoping. Contract pickers on a docket (supplier, materials, field-user, and the docket workbench cascade) list only the contracts the selected supplier holds on the current project. A supplier-link attached to several projects never bleeds another project’s contracts into the picker — the Go API enforces this server-side via project_id (it is never trusted from, or left to, the client). The supplier-detail “rates on other jobs” view is the one intentional exception: it shows the supplier’s contracts across projects for reference.

Creating a Materials contract

The Materials contract sits inside the existing supplier-link-project relationship — same path as Labour Hire and Plant Hire, just a new value of agreement_type.

  1. Navigate to Admin → Contracts for the project.
  2. Click + New Contract and pick the supplier from the typeahead. The supplier must already be linked to the project; if not, link it via Suppliers first.
  3. Set Agreement type to Materials. The editor switches to a 2-step flow:
    1. Materials — author the rate sheet.
    2. Cost Allocation — map each material to a supplier_payment_method_line.

There is no Classifications, Equipment Rates, or Allowances step — surcharges are modelled as material rows (see below).

Authoring materials

The Materials step is a flat editable table:

ColumnRequired?Notes
NameYesFree text, case-insensitive unique within contract. Australian spelling.
Supplier SKUNoSupplier’s catalogue code (e.g. BOR-N32-20-S100). Drives docket typeahead and reconciliation.
DescriptionNoMulti-line spec — slump, aggregate size, MPa class.
UnitYesOne of m3, tonne, ea, L, kg, lm, ls, load. Engine doesn’t unit-convert; pick the supplier’s billing unit.
RateYesPer-unit dollar rate. NUMERIC(14,4), non-negative, immutable on docket lines once created (see Dockets).
Min QtyNoBilling floor. When entered_qty < minimum_qty, the pricing engine emits a top-up line so the supplier is paid the contract min.

Realistic example for a Brisbane Metro Tunnel concrete supplier (Boral Wacol):

NameSKUUnitRateMin Qty
N20 ConcreteBOR-N20-20-S80m3245.0010
N32 ConcreteBOR-N32-20-S100m3285.0010
N40 ConcreteBOR-N40-20-S100m3312.0010
Saturday surchargeSUR-SAT-CONCea350.00
Pump truck hirePMP-32M-DAYea1850.00

Bulk import

Supplier rate sheets are typically 30–80 rows. The Materials step ships a bulk-import drawer.

  • Formats: .csv, .xlsx. Parsed server-side; the drawer renders a per-row preview before commit.
  • Template: click Download template for a CSV with canonical headers (name, supplier_sku, description, unit, rate, minimum_qty). Extra columns are ignored; missing required columns abort the parse.
  • Validation: required-field misses, invalid units, negative rates, and case-insensitive duplicates surface as red pills; valid rows show green.
  • Errors download: failed rows can be downloaded as CSV for fix-in-place in the admin’s spreadsheet of record.

The endpoint accepts multipart form-data and returns a per-row result map — see the spec for the shape.

Modelling surcharges as material rows

Per Decision #6 in the spec, the Materials step has no Allowances tab. Surcharges — after-hours, weekend, hazmat, pump-hire, hose-extension — are modelled as material rows with unit='ea'. This keeps the editor flat and avoids a parallel allowance schema.

Saturday concrete pour example: add Saturday surcharge / SUR-SAT-CONC / ea / 350.00 / no min qty. On the docket, the supervisor adds N32 Concrete × 12 m³ and Saturday surcharge × 1 ea. The pricing engine prices both; the cost-allocation resolver routes both to the same PM line if allocated together. Same shape covers hazmat fees, pump-truck day rates, hose-extension levies.

If a surcharge needs different cost-allocation than the headline material, it gets its own PM-line mapping in the Cost Allocation step.

Cost Allocation step

After the Materials step saves, the editor advances to Cost Allocation — the same step used for labour-hire and plant-hire contracts, pivoted on contract_materials rows instead of classifications or equipment rates.

  • Rows are materials; columns are supplier_payment_method_line entries on the supplier-link-project.
  • Click a cell to bind; confirm the binding to flip the status pill from Unconfirmed (amber) to Confirmed (green).
  • Status pills: Unallocated (red, no row), Unconfirmed (amber, confirmed_at IS NULL), Confirmed (green), Orphaned (red, PM line deleted).
  • Top-up lines inherit their parent material’s allocation via source_entity_id — no separate row.
  • Non-confirmed allocations don’t block docket creation, but the docket surfaces an Unconfirmed allocation pill until closed.

A note on the AI Awards Interpreter

Materials contracts use CSV/XLSX bulk import in v1; the AI Awards Interpreter is deferred (see spec §11).

  • Dockets — what happens once you’ve authored a contract.
  • ERP Push — pushing confirmed materials dockets to COINS.
  • Spec: docs/superpowers/specs/2026-04-30-materials-module-design.md §4 (data model), §7.1 (UI architecture).