Subcontractor Dockets

A subcontractor docket (SUB- display-ID prefix) is a first-class docket type alongside labour-plant and materials dockets. It records work performed by a subcontractor against a project’s subcontractor contract — combining engine-priced workers, attendance-only workers, and scheduled-item lines on a single docket whose grand total feeds cost allocation.

How billing works on a single docket

A subcontractor docket supports three types of billing lines, all on one document:

Line typeWho it coversPricing
Engine-priced workerA worker with an Award classification assigned to the docket.Priced through the same 8-stage rule engine as labour-hire workers. Ordinary hours, OT, penalties all apply.
Attendance-only workerA worker with no classification (or marked attendance-only on add).Recorded on the docket but not billed. Useful for head-count, sign-on compliance, or future back-fill.
Scheduled-item lineA unit-rate item from the contract’s Schedule of Rates.Priced as entered_qty × base_rate; minimum engagement applies when entered_qty < minimum_engagement.

The docket’s grand total = engine-priced worker amounts + scheduled-item amounts. Attendance-only workers contribute $0 to the total.

Pricing is per worker

Each worker bills at the rate line they were assigned when they joined the docket, from that rate line’s own contract — never from the docket’s header contract, and never from another worker’s. One supplier is often engaged under several agreements at once, so a single master legitimately spans more than one contract.

A master docket carries one header supplier, and that supplier is who the docket is for. Everyone on it still prices from their own rate line’s own contract, so a master can span several contracts of that supplier’s engagement.

A crew is a saved list of the people who work together on a project. It is scoped to the project, not to a supplier, and it may hold anyone on that project — subcontractors, labour hire, plant operators. A person may be on several crews (a leading hand runs two gangs). A crew is not asked to name a supplier.

A crew is not a docket. Since 2026-08-13 the crew has nothing to do with which docket anybody’s hours land on. Master dockets are created per subcontractor user sign-on and are identified by supplier, project, date and shift — nothing else. The “crew master docket” was an earlier model from when crews were for subcontractors only, and it is gone.

The two consequences worth stating plainly:

  • Two of a supplier’s crews on one site on one day produce ONE docket for that company, not two. Under the old model the second crew collided with the first and was refused outright, so it got no docket at all.
  • Two suppliers’ people on one crew produce one docket EACH. They are two companies’ payroll records. The old model merged them onto the host crew’s docket, which billed one supplier’s labour to another.

A grouped allocation bills once. Grouping people onto one rate line bills that line’s hours × its rate a single time, whatever kind of rate it is — two surveyors grouped on a $150/hr line for 10 hours bill $1,500, not $3,000. The people are listed on the line; they don’t multiply it. A new group starts on the docket’s shift and break, or on the members’ own times when they signed on. The Live totals show grouped crews, individual labour and scheduled items exactly as the docket bills them.

Grouped (crew-rate) allocation is same-supplier only. A crew-rate line belongs to one supplier’s contract, so only workers who hold an engagement with that supplier can be grouped on it. Attempting to group a worker from another supplier is refused with a message naming the supplier that owns the crew rate.

Two supervisors opening two diaries for the same supplier, day and shift reach the same master rather than forking one each.

Creating a docket from a crew. The create-docket dialog no longer offers a crew (removed 2026-08-14 — see There is no “Crew” mode in this dialog). Crew-based creation is the diary’s Create crew docket action, which routes each member through their own engagement instead of heading one docket for the whole gang.

The API still accepts a crew as a seed, not an identity: a create posted with crew_id populates the worker list from the crew’s members and lends each member’s own classification to a worker posted without one; only the crew’s subcontractor members are a source of those billing facts, because a labour-hire member’s agency rate must never be stamped on a subcontractor’s docket. A crew that does not belong to this project is refused. Nothing in the web office UI posts it any more.

What does bill $0 is a worker with no usable rate reference at all — no rate line, or one on a contract belonging to a different organisation. That $0 is the signal to give them a rate line their own engagement covers.

The same rule applies to the docket’s other lines: a scheduled-item line and a crew-rate group each price from the contract that owns the rate they point at. A line never takes a same-named rate from a different contract.

The docket’s own contract is what the header shows. It is a fallback, not an override — it is used only for a worker or line that has no rate reference of its own.

Break rules follow each worker’s own contract too: a worker is charged any break penalty their own agreement defines, at their own rate. The docket lists every break finding raised across the contracts on it, so nothing a worker is charged for is hidden.

A stamped rate is permanent for that docket. The rate a worker bills at is captured when they join, and it stays that way regardless of where their assignment later goes: moving a worker to a different supplier never re-rates, re-values or zeroes work already recorded. A docket from last month keeps last month’s rate, and last month’s total.

Changing the rate is a separate, deliberate act — and the choices offered there, and the ones the server accepts, are the supplier engagements the worker holds today. So the anchor is validated once, when it is set, and trusted from then on. Editing anything else on the row (hours, notes, break selections) is never blocked by a later change of supplier.

Split hours

Some days are paid at two rates. A Doherty operator who drives in for 2 hours and then runs the 36T excavator for 8 is paid 2 hours at the Operator rate and 8 hours at the 36T Exc (wet) rate. Split hours records that on the one person, on the one docket.

On a worker (web). Open the docket, set the worker’s times to the hours on their own rate (8 hours on the excavator), then choose Split hours on the worker’s card. Pick the other rate and enter its hours (2). The card lists the split under the worker, with its amount.

On a crew (web). The same button sits on the crew’s card. A crew’s split bills once for the crew, like the crew’s own line: 2 hours of Operator on a crew of three is 2 × $105, not 6 ×.

On the phone. Open the worker’s line (or the crew) and tap Split hours, choose the rate, enter the hours and tap Add. Tap a split to change its hours or remove it.

Who can split hours:

  • A worker on their own line only.
  • The crew lead and the office on any worker, and on a crew.
  • Nobody in the field once the docket is submitted from the field.

What the rates are:

  • The person’s own contract. The rates offered come from the contract the person (or crew) is priced on. When a supplier holds more than one contract on the project, a person only sees their own.
  • Hourly rates only, and not the rate the person is already on.
  • Once per rate. A person can have one split per rate; two people can each have Operator hours.

What it bills:

  • A worker’s split adds hours × rate to individual labour; a crew’s adds it to grouped crews. The Live totals and the docket total include both. Example: 8 h × $195 + 2 h × $105 = $1,770.
  • Splits stay through a re-price: they are hours someone entered, not hours worked out from times.
  • Hours are more than 0 and up to 24, to two decimal places.
  • Removing a worker or a crew from the docket removes their splits too.

Split hours is not Split activities. Split activities shares one line’s hours across cost codes; Split hours puts hours on a second rate. A worker can have both: the worker’s own line split across activities, plus a split on another rate.

Creating a subcontractor docket

A subcontractor docket can be created two ways: manually by an office user opening a diary, or automatically when a subcontractor’s worker signs on at a prestart. Both paths produce the same docket structure and both are idempotent — one master per supplier per shift. If a docket already exists for that combination, both flows redirect to the existing one rather than creating a duplicate.

Master identity — supplier, project, date and shift

A master docket is identified by supplier, project, date and shift. That is the whole key, and it is what makes a second sign-on for the same shift land on the same docket instead of creating a duplicate.

Two things are deliberately not in it:

  • the supervisor — so two supervisors opening two diaries cannot fork two masters for one supplier’s shift;

  • the crew — so two of a supplier’s crews on the same shift share the one docket. There was a second, crew-keyed identity until 2026-08-13; it is deleted, along with the “adopt-and-stamp” reconciliation between the two and the refusal that fired when they could not be reconciled.

  • Created inside a diary, the shift comes from the diary.

  • Created standalone, the shift must be chosen.

  • With neither a shift nor a diary, creation is refused and the reason is shown.

The date a docket is created for is the project’s date, not the viewer’s. On a project east of UTC that matters before mid-morning: without it, an early-shift docket would be filed against yesterday and would not join the day’s existing master.

Office flow — from the Diary

Open the Diary for the relevant shift, locate the Subcontractor bucket at the bottom, and click + Add Subcontractor Docket.

The dialog asks for exactly one thing — who the docket is for — and offers two answers:

Individual — pick one worker. Their supplier’s master for the day is created (or joined, if it already exists) with that worker on it. The supplier and contract are sourced from the worker’s own engagement; neither is asked for. Workers marked Not assigned or Unverified are deliberately offered and still get a docket, landing unanchored with an empty pricing grid for the supervisor to fill in.

Supplier — pick a subcontractor. An empty master docket is created for them: correct supplier, project, date and shift, and nobody on it. Use this when the paperwork needs to exist before the people are known. They join it afterwards, three ways:

  • their own sign-on at the prestart routes them onto it;
  • the diary’s Create crew docket fan-out attaches them;
  • the office adds them in the docket editor.

The Supplier list holds only suppliers this project engages under a subcontractor agreement. Labour hire and plant hire raise their own dockets and have no master to head, and an attendance-only engagement raises no billing docket at all — none of them appear.

An empty master is a draft until somebody joins it. It prices cleanly at $0 and the docket screen shows No workers recorded. rather than an error, but it cannot be submitted while it has neither a worker nor a scheduled-item line.

Once the docket exists, the rest of the flow is the same either way:

  1. Review / adjust workers — add, remove, or change billing mode for any worker.
  2. Add scheduled-item lines — pick items from the contract’s Schedule of Rates via a typeahead and enter the quantity (entered_qty). The line total and minimum-engagement floor are calculated immediately.
  3. Submit — the docket moves through the standard docket lifecycle.

Creating for a supplier who already has a docket that day re-opens the existing one. The master’s identity key is enforced in the database, so a second create for the same supplier, project, date and shift returns the docket that is already there rather than a duplicate-key error — the same idempotency the sign-on path relies on.

There is no “Crew” mode in this dialog

It was removed on 2026-08-14. Crew-based creation still exists — it has its own Create crew docket action beside + Add Docket on the diary, which fans out over the whole gang and routes each member through their own engagement: labourers onto their own dockets, the plant operator with his machine on his, and each subcontractor’s people onto their own supplier’s master.

The mode that was removed did something different and worse: it derived one supplier for the entire crew and seeded every subcontractor member onto that one master, so a crew spanning two suppliers had one supplier’s labour attributed to the other. It was also a second door onto a concept that no longer exists — a crew stopped heading a docket when the crew-keyed master was deleted on 2026-08-13.

Field flow — auto-spawn on a subcontractor’s sign-on

When a subcontractor’s worker signs on at a prestart, that supplier’s master docket for the shift is found or created automatically — no office action required:

  • The sign-on attaches that one worker. It does not seed anybody else: each person’s own sign-on is what puts them on the docket, which is what “masters are created per subcontractor user sign-on” means.
  • It fires even if the worker is unclassified — the docket is created and they are recorded as attendance-only until a classification is added.
  • A master raised by a sign-on records no crew-lead stamp, and does not need one. Since 2026-08-12 the lead is a live per-person flag on the project, so a flagged worker who appears on this docket can manage it whether or not anything was stamped — and an unflagged one cannot, however the docket was raised. crew_lead_user_id is written only when the office raises a docket from a crew with a designated lead, and is history only: nothing gates on it. See Crew Lead. It no longer records a crew_id either — nothing writes that column since 2026-08-13.

If the same worker signs on a second time during the same shift (e.g. after a prestart amendment), the system returns the already-created docket — no duplicate is spawned.

Field flow — a worker starts or joins the day’s docket

A subcontractor worker who taps Create Docket on the field app goes straight to Start Shift and picks three things: date, shift and supervisor. The supplier is never asked for — it is inferred from the worker’s project assignment and shown on the form, above the date, along with a line saying the docket is shared with everyone from that supplier on site today.

Started vs joined. The first worker for a given supplier, project, date and shift creates the docket; everyone who starts a shift after that joins the same one. When the docket opens, the app says which happened. On the wire the signal is docket_attached on the create response: true means this call joined an existing docket, false means it minted one. docket_created stays true either way — a billing docket exists after the call regardless.

Attendance-only suppliers. When the supplier link is attendance-only for the project, starting a shift records attendance and raises no docket. The app says so before the worker taps Continue and confirms it afterwards as a normal outcome, not an error.

When the docket is on hold. If the worker has no classification — or, for an operator, no operator or equipment rate — attendance is still recorded and the docket waits. The app stays on the form with every input preserved, names the exact cause and the supervisor the worker selected, and offers Try again so the worker can retry on the spot once the supervisor has fixed it.

After a QR sign-on. If a sign-on lands the worker on a docket — their own, or their line on their supplier’s master — the app opens it instead of leaving them to find it in the Dockets tab. The sign-on response carries linked_docket_id for exactly this. It does not say whether that sign-on started or joined the docket: the sign-on response carries no created-vs-joined signal, so the app opens the docket without a started/joined line. That line appears only on the Start Shift path, where docket_attached makes the answer knowable.

One docket per supplier per shift

Both flows share the same idempotency key: (supplier_org_id, project_id, shift_date, shift_type). The first creation wins; subsequent triggers redirect to the existing docket. This means:

  • The office clicking + Subcontractor Docket after a field auto-spawn opens the existing docket, not a new one.
  • A worker signing on after the office has already opened that supplier’s docket from the diary appends to the same record.
  • Two of the supplier’s crews working that shift append to the same record — which is the point of removing the crew from the key.

Changing a supplier’s agreement type

A supplier’s agreement type on the project decides where its people’s sign-ons land:

  • Subcontractor: one shared docket per shift.
  • Any other type: each person gets their own docket.

Changing the type, on the supplier’s Config tab, from the project’s Suppliers list, or on the phone, first says what will change. Then you confirm. The change applies from the next sign-on. Dockets already made stay as they are.

Each change is recorded on the supplier link, with who made it, the old and new type, and when.

Sign-on attachments

When a worker attaches to a master docket through a field sign-on, the attachment is recorded in the docket’s history — who was added, and from which sign-on. A repeat sign-on for a worker already on the docket refreshes their record without adding a second history entry, and a worker the office has removed is not brought back by a later sign-on.

Open screens update live: a docket open in another window shows the new worker without a refresh.

Where it surfaces

SurfaceWhat you see
/admin/dockets → Subcontractor tabAll subcontractor dockets across the project, filterable by status, date, and contract.
Diary → Subcontractor bucketSubcontractor dockets linked to that diary’s shift, with totals inline.
Cost Allocation step (contract editor)scheduled_item rows appear alongside classification and equipment-rate rows. Allocation works identically.

Display ID

Subcontractor dockets use the SUB- prefix, e.g. SUB-0001. The numeric suffix is global-unique within the project.

Mobile — Crew Lead captain

On mobile, a crew lead is the captain of the crew’s subcontractor docket. When they open it in the Expo app, a banner at the top of the screen identifies them as the captain.

The app does not work out who the captain is — the server tells it. Every docket read carries a can_manage_crew capability computed for the person asking, using the very same check the write endpoints enforce, so the app can never offer a control the server will refuse or hide one it would allow.

That capability is true for an office user with docket pricing rights, and for anyone the office has ticked as a crew lead on this project who appears on this docket. Because it is resolved live, ticking someone takes effect on dockets that already exist, and un-ticking them takes effect immediately.

Until 2026-08-12 the app decided this itself, by comparing the signed-in user against the docket’s crew_lead_user_id stamp. That stamp is frozen when the docket is raised, so swapping the lead mid-shift left the app showing captain controls to the person who had just been replaced, and hiding them from the person who had just been given the job.

Workers who are crew members but are not flagged open the same docket in read-only mode; the captain controls are hidden. Since 2026-10-23 they also see only their own line and the crew line they are on: other workers’ lines and sign-ons, other crew lines, and the master’s scheduled items and equipment are not sent to them, and the list says “Showing your lines only”. See Crew Lead.

More than one person on a docket can be a captain — tick two people and both get the controls.

What the captain can do

Add crew members — RETIRED 2026-08-13

The captain’s Add Member picker has been removed from the app. It listed crew members who had signed on for the docket’s crew and shift, and it found them by walking the docket’s crew_id — a column nothing writes any more, so the picker could only ever have shown an empty list.

If you are still on an older app build, the button is still there. It now shows an empty list rather than an error — the server keeps answering it for one release so the tap does not fail. It disappears when your app updates.

A crew lead can no longer add another person to a subcontractor master from the app. Two things replace it, and between them they cover the case:

  • The worker signs on. That is what puts somebody on a master now — the docket is keyed to sign-ons.
  • The office adds them from the web docket editor, which can add anyone.

Remove members

The captain can swipe or tap to remove any worker from the docket. Removing a worker does not affect the crew definition or the worker’s sign-on record; it only removes the billing line from this docket.

Toggle billable / attendance-only

Each worker row shows a Billable toggle. This controls the is_attendance_only flag on the docket worker record:

Toggle stateis_attendance_onlyEffect
On (billable)false (default)Worker is priced through the 8-stage engine on their classification. Ordinary hours, OT, penalties apply.
Off (attendance-only)trueWorker is recorded on the docket but contributes $0 to the total, even if they have a classification.

Flipping the toggle reprices the docket immediately. The attendance-only state travels with the docket through its lifecycle — an office user reviewing the submitted docket will see the same toggle state the captain set in the field.

Billable requires a classification. A worker without an Award classification is always attendance-only regardless of the toggle; the system ignores is_attendance_only = false if no classification is present.

Enter scheduled-item quantities

The captain can add and edit scheduled-item lines — unit-rate work picked from the contract’s Schedule of Rates. For each line:

  1. Tap + Scheduled Item and search or scroll the contract’s rate list.
  2. Enter the quantity (entered_qty). The line total (entered_qty × base_rate) and any minimum-engagement floor update as you type.
  3. Multiple lines from the same or different scheduled items are supported.

Submit

The captain can submit the docket directly from mobile. Although a crew lead is typically a labour or operator role (not an office supervisor), the office’s tick explicitly authorises them to submit their crew’s docket. Submission moves the docket into the standard submitted state and makes it visible to office users for review.

Scope boundary — what ships later

CapabilityStatus
Web parity for the per-worker billable toggleFollow-on

ERP and CSV export shipped in Slice 6. See Export for the full pipeline documentation.

Note: Leaving a crew is not retroactive. A worker’s past attendance stays as it was recorded on the dockets for those shifts; changing the crew today does not rewrite yesterday’s payroll.

Historical dockets. A small number of dockets raised before 2026-08-13 still carry a crew_id, from when the crew was part of the master’s identity. Nothing routes, prices or authorises on it — the column is retained only so those rows stay readable.

Field view (non-commercial pricing)

A user without the dockets.view_pricing permission — field workers (labour, operator, wet/dry hire) and subcontractor crew-leads — sees every docket-detail surface in a non-commercial form: quantities, hours, crew composition, classifications, and activity allocation are shown, but no rates or dollar totals.

This is enforced server-side: the Go API redacts all commercial fields (base/effective rates, line amounts, worker/equipment/scheduled-item/worker-group totals, grand total, penalty rates, and the audit trail) before the response leaves the API, and flags the payload with commercial_redacted: true. The mobile app selects a non-commercial layout from that flag — it never relies on client-side hiding. This covers the Pricing Section, the equipment section, the scheduled-item lines, the worker-group / crew-rate lines, and the docket KPI strip (which shows net hours instead of an estimated dollar total).

A price written into a name or description is hidden too. Many rate lines carry their rate in their name (for example Traffic Controller @ $72.50 p/h); a field user sees Traffic Controller @ $— p/h. This applies to every docket line, the item picker, the pricing preview, day-works, the diary and day-report docket lists, and a docket export made without pricing. Notes and a crew’s own group label are shown as written.

Commercial users (supervisors, engineers, admins — anyone granted dockets.view_pricing) continue to see full pricing unchanged.

  • Schedule of Rates — the contract-level step that defines the scheduled items available to pick on a docket.
  • Crews — project-scoped groupings of workers; used for both creation flows documented above.
  • Export — CSV and ERP pipeline for subcontractor dockets.
  • Overview — module architecture and persona breakdown.