Rate Schedule
Every dollar a docket bills resolves from a contract’s rate lines. This page covers how a rate line is authored, what the server decides for you, the warnings the builder surfaces, and how a rule — or a rate line — can take its rate from the cost book.
Two questions
Adding a rate line asks two things:
- What are you pricing? — Labour, Plant & equipment, Crew, Materials & items, or Allowance & other.
- Per what? — a unit from that category’s list.
From that pair the server derives the line’s billing shape. You do not choose a basis, a quantity mode, or a meter type; those are consequences, not decisions.
| What you’re pricing | Units offered | Stored basis · quantity mode · meter type |
|---|---|---|
| Labour | hour | labour-hour · shift · — |
| Plant & equipment | hour | meter · shift · shift-duration |
| Plant & equipment | day, week | meter · period · shift-duration |
| Plant & equipment | engine hour (SMU), km, linear metre, load | meter · meter · (that unit) |
| Crew | crew hour | crew-hour · shift · — |
| Materials & items | each, m³, m², tonne, kg, or a custom unit | meter · meter · custom |
| Allowance & other | day, week, shift, lump sum | flat-shift · shift · — |
A day-priced machine is a machine, not a lump sum: Plant & equipment on a
day or week unit stores meter with a period quantity mode, so it bills one
period per shift and keeps its plant identity on the docket. A $2,400/day
Posi-Track on a Saturday with a 1.5× penalty bills $3,600, not $24,000 and
not $36,000.
An Allowance on a shift unit bills once per shift. A $180 site allowance bills $180 on a ten-hour shift, not $1,800 — and the category never offers you “per hour”, so that mistake is not reachable from the form.
Free text, and where it survives
Materials & items has a Custom… unit: type the word your supplier uses (bags, panels, drums). That is the only free-text unit left, because it is the only place the vocabulary is genuinely open. A custom material unit always stores the same shape, so a typed word can never change how a line prices.
Two kinds of word are refused there, and each refusal names the category that is the right answer:
- A per-period word —
day,week, and their abbreviations. A per-day price is a day-rate machine (Plant & equipment) or a flat per-day charge (Allowance & other). Allowed through, a “material” priced per day would be re-shaped into a per-period machine: it would leave the materials list, start taking shift loading, and a Saturday 1.5× rule would bill $450 on a $300/day consumable. - A reserved machine word —
engine hour,km,linear metre,load, and the words that fold onto them. Those four tokens are how the mobile app tells a machine from a material, so a material stored on one of them would be a machine on a phone and a material on the web.
What the server derives, and why you can’t set it
The two answers are sent to the API; the server derives basis,
quantity_mode and meter_type and echoes the result back with the line. The
long-standing coercion ladder still runs after that derivation and remains
the final authority — it is the same code path a legacy caller enters, so an
older integration that sends a raw basis behaves exactly as it always has.
The CSV import is no longer one of those legacy callers. It asks the same two questions the form asks and derives through the same body — see Importing a rate schedule.
Sending both vocabularies in one request is rejected with a message naming the conflict, rather than one silently winning — and that includes the meter type: a category owns all three derived fields, so a request may not pin one of them by hand. Sending neither vocabulary is rejected the same way, naming the five categories.
Canonical units
The stored unit tokens are engine-hours, km, metres and loads — never
lm or load. The mobile app’s compatibility layer matches those literal
strings to decide whether a line is a machine or a material. A line stored with
a non-canonical token reads as a material and disappears from the mobile
equipment picker, with nothing on screen to indicate why.
Editing pricing identity
Open a line and use Edit pricing…. Pick the category and unit again, and confirm. The server re-derives the billing shape and re-runs the same ladder.
Rules and break bindings are kept. The edit writes only the line’s own identity columns; nothing attached to the line is dropped.
Every category can be reached from every other, including moving a Plant or Materials line onto Labour, Crew or Allowance — the meter type is cleared as part of the re-derivation rather than left behind.
One consequence worth knowing: a change to a line’s name, unit or basis marks
it as manually edited, which sets its origin to manual. An imported line that
you re-classify therefore stops being updated by rate-source re-imports —
your judgement is not silently reverted by the next refresh. If you want the
line to keep tracking the source, change it at the source instead.
Legacy lines
Rate lines created before this vocabulary — including lines a CSV import created before the import spoke it — may have no stored quantity mode. Those lines show their raw basis and unit with no plain-language reading, and that is deliberate: opening a line never rewrites it. Reading is read-only, and a line only changes when you change it.
New imports do not add to that population: every row a CSV import creates now carries a full billing shape, because it derives through the same body the form uses.
On such a line, Edit pricing… opens with no category selected and Save stays disabled until you pick one. The builder will not guess a category for a line it cannot name, because a guess on this surface looks exactly like a fact.
Editing pricing identity on such a line is a real change. It can alter how future dockets price that line, so make it when you mean it, not to tidy the display.
Importing a rate schedule
Open Import on the Rate Schedule step and upload a CSV or XLSX. The Download CSV template link always gives you the current columns:
name, code, level, pricing_kind, unit, rate, min_engagement
name, pricing_kind, unit and rate are required; code, level and
min_engagement are optional. pricing_kind and unit are the same two
questions the form asks — the server derives the billing shape from that pair,
through the same code the form uses, so a line you import and a line you type by
hand cannot end up shaped differently.
One file may carry up to 5,000 rows. A larger file is refused whole, with the count it carried — split it and import the parts one after the other.
A file in the old format is rejected, not guessed at. If your header still
carries basis, meter_type or records_operator, the whole file is refused
with a message naming each retired column and what replaced it, and listing the
accepted pricing_kind values. Nothing is imported. Columns the import has
never claimed — your own notes or supplier columns — are ignored as before.
A bad value names itself. A pricing_kind or unit outside the accepted
list fails that row alone, and the message carries the value you wrote, the list
that row’s category accepts, and the closest accepted spelling when there is one
— unit "dayz" … did you mean "day"?. When nothing is close, no suggestion is
offered: a hint that points at the wrong unit would change what the line bills.
Wet hire is no longer an import column at all; set it on the line after import.
The two import modes
- Add new lines only (the default) — every valid row creates a new line. This is what the import has always done.
- Update matching lines, add new — a row that matches an existing line updates it; a row that matches nothing is added.
Matching is on code, falling back to name, ignoring case and padding. A row only matches when it matches exactly one line — and that rule applies to your file as well as to the schedule:
- In the schedule. A row whose code or name matches two or more live lines is reported as an error and neither updated nor added — there is no correct guess when a rate is at stake, and the message tells you how many lines it matched and how to fix the schedule. A row whose name matches a line already carrying a different code is reported too, so a name match can never silently re-code a coded line.
- In your file. If two rows of the same file carry the same code, every one of them is reported and none of them is written — neither updated nor added. Two rows naming one line is the same coin flip on money as two lines answering to one row, and it is also how a schedule accumulates the duplicate codes that block Update in the first place. The message names the code and how many rows carried it.
- Two rows may share a name when their codes tell them apart. Because matching
falls back to name only when a row’s code finds nothing, a file listing
Excavator 20ttwice — once asEX-20, once asEX-30— updates both lines. That is what thecodecolumn is for. The same name on two rows is reported only when the name is what the match would actually have to use: when a row carries no code, or when its code matches nothing in the schedule. - Two rows that reach the same line by different keys — one by code, one by name — are caught the same way: the first row updates the line, the second is reported. Updated counts lines that moved, never rows processed.
Four things worth knowing before you use Update:
- Re-uploading an unchanged file changes nothing at all. Rows that already match are counted as Unchanged and no line is written.
- Updating a line’s name, unit or category marks it manually edited, exactly as editing it by hand does — which stops rate-source re-imports updating it (see Editing pricing identity). Changing its rate marks the rate as overridden, for the same reason. Both fire only on a real change.
- A deleted line is never brought back. A re-import of a line you deleted adds a new line; it does not restore the old one.
min_engagementis only changed when the column is in your file. A file whose header carriesmin_engagementspeaks for that column in both directions: a number sets the minimum and a blank cell clears it. A file with nomin_engagementcolumn says nothing about it, so a stored minimum is left exactly as it was. That distinction is money — a minimum engagement is what pads a short shift up to the hours you contracted for.
Lines an import will not change
Three kinds of line are reported rather than updated. Each names itself and where to make the change instead:
- A line that bills from the payment schedule. Its rate is a live figure from the project’s cost book, not a stored number, so a spreadsheet cannot set it. Change it on the payment schedule.
- A line kept in step with the supplier’s rate source. Writing it from a spreadsheet would mark it manually edited and permanently stop the rate source updating it — the schedule would quietly stop tracking the cost book. Change it at the source.
- A line whose rate was set by hand. The override is deliberate, so an import does not silently undo it. Change it on the line.
A row that changes nothing on one of those lines is still counted as Unchanged, not reported — re-uploading a file that already matches is always safe.
The result table reports Added, Updated, Unchanged and Failed separately, with a row and a reason for every failure. A failed row never stops the rest of the file from importing, and every outcome — including “some rows already matched and some failed” — is summarised in one sentence above the table.
The warnings
All of them are advisory. None blocks a save, and none can be dismissed — they clear when the underlying condition clears.
An hourly line carrying per-shift intent. A Labour or Crew line whose unit is a shift, day, week or lump sum bills hours × rate, so a “per shift” figure bills that amount for every hour worked. Fix it with Edit pricing… → Allowance & other. The new form cannot create this state; it appears on legacy and imported lines.
A folded allowance that adds nothing. An all-purpose allowance always folds into the base rate, and the fold only handles per hour, per day and percentage. Set to per week or per occasion it contributes exactly $0. Change the unit to per hour, per day or percentage, or take the allowance off all-purpose so it bills as its own line. The rule still saves either way.
The warning reads the allowance’s type, not the Fold into base checkbox. That checkbox controls which classifications an allowance applies to, not whether it folds, so unticking it leaves the $0 in place.
Ordinary hours not set. With no ordinary quota, every weekday hour
classifies as overtime or penalty on any line that does not carry its own
ordinary-hours override. Set the daily hours on the contract’s Ordinary hours &
shifts step. Contracts that store their days abbreviated (mon, tue) count as
set — pricing reads both spellings.
A weekly overtime window that never applies. An overtime rule can be scoped weekly, and the scope is stored, but nothing prices it: hours are classified a day at a time, so a weekly rule bills exactly as a daily one would. Author it as daily, or record the weekly cap as an unmapped clause so it is not mistaken for something the engine enforces.
Rules the app refuses
Unlike the warnings above, these stop the save. Each is a setup that pricing would ignore or misread, so it is refused where it is made — on the rule card, through the API and through the Copilot tool — with a sentence that says what to change.
| You wrote | Why it can’t be saved | Write instead |
|---|---|---|
| Overtime on Hours worked (e.g. “Net hrs ≥ 9.6”) | Hours worked is a yes/no test on the whole day, so the rule takes every overtime hour once the day is long enough and leaves the steps below it empty | Hours range from 9.6 (no max) |
| A weekday step that ends inside ordinary time (e.g. 0–7.6 on a 7.6-hour day) | Weekday steps are counted from the end of ordinary hours, so it has no room and never pays | Start the step at the line’s ordinary hours or later |
| A day type of “rdo” | Days are weekday, Saturday, Sunday or public holiday; a rostered day off is Is RDO | Is RDO |
| A custom condition with no docket field picked | A condition on no field never matches | Pick the field |
| A rule for a shift that has no start and finish times | A docket’s start time picks its shift, so a shift with no times is never picked | Give the shift its times on Ordinary hours & shifts |
| An all-purpose allowance with conditions | All-purpose is paid on every hour; its conditions are never checked | A conditional allowance |
A shift with only one of its times. The Ordinary hours & shifts step
does not save a shift that has a start and no finish, or a finish and no start.
The missing time is marked and nothing saves until it is filled. Give the shift
two different times, or clear both. A stored time the field can’t show, such as
00:00-06:00 in the start field, appears beside that field as written: pick a
time to replace it.
A new hours step starts where it can pay. Adding an Hours range to a weekday overtime rule starts it at the line’s ordinary hours (7.6 → 9.6 on a 7.6-hour day), so the new step saves as it is. Saturday, Sunday and public holiday steps, and penalty and allowance rules, still start at 0.
How these price (2026-10-25)
- A step that wins a clash keeps to its own hours. When two overtime steps overlap, the higher rate wins the overlap and is applied first, but it bills only from its own start: a “9.6 and over” double-time step never takes the first two overtime hours.
- A shift with no start and finish times matches no docket. It used to match every docket, so a night shift without times put night penalties on day shifts.
- Loadings on a percentage or per-hour rate bill. A penalty at 15 % bills +15 % of the hours it applies to; a penalty at $x per hour bills $x for each of those hours. Both used to bill $0.
- Penalties and allowances on a plant line bill on that machine. A “travel fee per shift” written on a pump line bills once per shift on that line. Only rules written on the plant line itself bill there. A meter or day-rate line bills a per-hour amount on the docket’s worked hours.
- The clash check on a rate line includes rules billed from the cost book (Bills to). It used to leave them out, so a clash between two of them showed as none.
Sourcing a rule’s rate from the cost book
An overtime, penalty or allowance rule normally carries its own figure — a multiplier, a dollar rate, a percentage. It does not have to. A rule can take its figure from the payment-schedule line it already bills to, so the number is whatever you actually pay for that item on that project.
The rule itself does not change. It still decides when it applies and how it applies — the conditions, the scope, the hours it catches. Only the dollar figure moves.
Bills to comes first
Bills to is the first thing on the rule card, and picking a line there is how you set the rate. Choose the payment-schedule line this rule books to and the rule bills that line’s own figure: Rate reads Line’s rate and the dollar box beside it fills in and goes read-only.
The figure is live. It shows as a dollar amount, but it follows the payment schedule: change the schedule and the next docket bills the new figure. A docket that has been submitted keeps the figure it was submitted with, forever.
Beneath the row, one sentence names the line the money comes from.
Each line in the Bills to list shows its PO or schedule, line number and cost code under its name, and the list is searchable — see Cost Allocation.
Your own rate, still booked to the line
To bill something other than the line while still booking to it, set Rate to Own rate. The rule keeps its Bills to line and bills its own figure: a multiple of the base rate, a dollar amount, or a percentage, and for an allowance its own unit. For example, overtime at × 1.5 booked to the worker’s time-and-a-half PO line, or a meal allowance of $24.28 per day booked to the meal line.
A rule that had no figure of its own starts at the line’s figure, so switching alone changes nothing until you edit it. Switch back to Line’s rate at any time.
To stop booking to the line, un-pick it (set Bills to back to Inherit base line or Unallocated). The rule gets back its own typed rate, exactly as you left it.
A worked example
A “Saturday penalty” rule pointed at General Labourer - Saturday at $98.25/hr. A 2-hour Saturday docket bills:
| Two Saturday hours bill | |
|---|---|
| The line at $98.25/hr | $196.50 |
The client then raises that line to $105.00. The next docket bills $210.00. Every docket already submitted keeps the $98.25 figure it was priced on — not one cent moves.
Rules you already wrote
Picking a line only ever changes the rule you picked it on, at the moment you pick it. Nothing is converted in bulk, nothing changes on load, and an unrelated edit — renaming a rule, adding a condition — never moves its money.
If a rule carries its own dollar figure and already books to a line priced differently, the card says so in one grey line naming both figures, with a Use the line’s rate link beside it. It is a statement of fact, not a warning, and nothing happens until you click it.
Sourcing a rate LINE’s rate from the cost book
A rate line works the same way, and it is the bigger decision of the two.
Open a rate line and pick a payment-schedule line in its own Bills to. The line’s Base rate immediately shows that schedule line’s figure and stops being an editable box; one sentence beneath it names the line the money comes from. There is nothing else to choose, no confirmation step, and no option to bill something different — picking the line is setting the rate.
Name the blast radius before you pick. A rate line’s base rate is what every rule, tier, break and percentage allowance on that line multiplies. Pointing it at a schedule line moves ordinary hours, overtime, penalties, minimum-engagement top-ups and percentage allowances together.
The figure is live, on exactly the same terms as a rule’s: change the payment schedule and the next docket bills the new figure, while a docket that has left draft keeps the figure it was submitted with, forever.
Setting Bills to back to Unallocated hands the line back its own typed rate, exactly as you left it — picking a line never overwrote it.
A worked example
A labour rate line typed at $58.00/hr, pointed at General Labourer - Saturday at $63.20/hr. An 8-hour docket bills:
| Eight hours bill | |
|---|---|
| The line at $63.20/hr | $505.60 |
The client then raises that schedule line to $66.00. The next 8-hour docket bills $528.00. Every docket already submitted keeps the $505.60 it was priced on — the docket card and the priced total both.
When a rate LINE’s pointer breaks
The line bills $0, and so does everything that multiplies it: an overtime tier on that line bills $0, a percentage allowance folds to $0, a minimum-engagement top-up is $0. The docket card agrees with the priced total, the $0 lines are flagged, and the docket carries a note naming the line and the reason.
That is the honest answer, and it is deliberate. A line that cannot find its rate never quietly bills the last number it happened to be storing — a stale rate that looks plausible is how a wrong invoice leaves the building.
Materials dockets follow exactly the same rules
A materials rate line — anything priced per tonne, per m³, per each — behaves identically on a materials docket. A delivery line whose rate line is sourced bills the supplier’s own schedule figure; a submitted materials docket keeps the figure it was submitted with; a broken pointer bills $0 with the delivery line flagged and the docket carrying the named reason. It never falls back to the figure that was captured when the delivery line was first added.
Where the schedule list shows a rate, and where it does not
In the left-hand rate-lines list, a sourced line shows Bills to and its unit in place of a dollar figure. That is deliberate: the list has no project context, so the only number it could print is the line’s own abandoned one. Open the line to see the rate it actually bills.
Imported lines arrive already linked
When you run Import from rate source, each newly-created rate line arrives with its Bills to already set to the schedule line its rate came from, and already confirmed. Price a docket on it straight away — there is no re-pick step, and it never prices $0 asking you to point it somewhere.
That link is recorded because the schedule line is literally where the figure came from, so the purchase basis is provenance rather than an assertion.
Two things it deliberately does not do:
- A line that already exists is never converted. Re-importing updates an existing line’s name and rate under the usual gates and leaves its Bills to exactly as it is. Lines that predate this behaviour stay as they are until someone decides, deliberately, to change them.
- A link you confirmed by hand is never overwritten. If you picked and confirmed a line yourself, a later import leaves it alone.
What freezes, and when
A draft docket tracks the payment schedule live. Reprice it and it reads today’s rate — so the figure you submit is the figure you can see.
The moment a docket leaves draft, its rates are fixed. No later edit to the payment schedule can move a submitted docket’s money, however many times it is repriced afterwards.
When the pointer breaks
A rule can end up pointing at nothing. When that happens the rule bills $0, the $0 line is flagged on the docket, and the docket carries a note naming the rule and the reason. Nothing is blocked — the docket saves, the rule saves, and the total visibly drops so the gap cannot pass unnoticed. A rule that cannot find its rate never falls back to an old one.
| What is wrong | What you do |
|---|---|
| The project has no payment schedule at all | Add one on the project’s supplier link, then point the rule at a line |
| The rule has no Bills to line | Pick a payment-schedule line in Bills to |
| Bills to is set to inherit the base line | The base line has no rate of its own to source — point the rule at a schedule line |
| The Bills to line has not been confirmed | Re-pick the line in Bills to, which confirms it |
| The line it points at has been removed | Point it at a current line |
The same reason appears in both places — as an amber badge on the rule card while you are authoring, and on the docket that priced $0 — so whoever finds it first reads the same sentence.
Two more states read differently, because in neither is anything actually wrong with the rule.
On the Test Pricing panel, a rule that bills from a payment schedule resolves against the project you are working in — the same project the contract is being set up for. If that project has no payment schedule, the rule prices $0 in the preview and says so, with the same sentence you would see on a docket. If the contract is not linked to the project you are in — an ordinary state while a contract is still being set up — the rule prices $0 and says that, because adding a payment schedule to a project this contract does not reach would not change the preview: add the project to the contract’s supplier link, or switch to a project the contract already covers. If you are previewing without a project, or you cannot see pricing on that project, the rule prices $0 and says so.
When the page cannot read the schedule — a dropped connection, an API error — the rule card says so and offers a retry, and says nothing about what the rule bills. It will not report a line as removed on the strength of a read that never came back: that reason is actionable, and acting on it would re-point a healthy rule and change what every future docket bills.
Units are reported, never converted
If an hourly rule points at a line priced per day, the rule still bills that figure per hour — nothing is converted behind your back. The card says so, in both units, and leaves the judgement to you. Point it at an hourly line, or leave it if the per-day figure really is the hourly number you meant.
Allowances billed from a line: per hour or per day
An allowance normally carries a unit of its own — per hour, per day, per week, per occasion, or a percentage — and that unit decides how many times its figure is counted. An allowance that bills a Bills-to line’s figure is paid either per worked hour or once per day it applies, and the Unit control offers those two.
Picking a line sets the unit to match it: a line priced per hour gives Per hour, and a line priced per item or per day (a meal or travel line, “ea”) gives Per day. You can change it afterwards.
| Travel allowance at $27.56 “ea”, 10-hour weekday | Bills |
|---|---|
| Per day | $27.56 |
| Per hour | $275.60 |
An allowance that was already billing per hour keeps billing per hour until you change its unit.
What Test Pricing can preview
Test Pricing picks from the same rate lines the engine prices, so a crew rate and a per-shift line are selectable alongside labour and plant. Crew sits in its own section, under the Subcontractor docket type, because a crew rate bills flat — its hours are never split into overtime tiers the way a worker’s are.
Each line asks for what it actually bills on. A per-shift or day-rate line asks for Periods and leaves it blank to bill one; a line that bills by the shift hour asks for Hours and leaves it blank to bill the whole shift; a metered line asks for Meter start and Meter end and falls back to the shift hours until you enter a reading. Holidays include your organisation’s own calendar and, when this contract is linked to the project you are working in, that project’s calendar as well. The State control’s default now applies the national set rather than none.
AI extraction
The Awards Interpreter emits the same two-question vocabulary, from the same closed unit list. Whatever it emits for the underlying billing fields, the server derives the shape from the category and unit, so an extracted line and a hand-authored line with the same two answers always store the same thing.
The Interpreter also reads the supplier’s existing payment schedule for this contract’s project, in line order, so it reuses the codes and units you already pay against. Other projects’ schedules are never read. When a schedule lists each worker separately (“Bryan McGinty - CW4 - Operator”), the Interpreter is not shown the names. It makes one line per classification (“CW4 - Operator”), never one per worker. If a name still comes through, it is removed before you review the lines.