Activity Allocation (per-line distribution)
Activity Allocation lets a supervisor decide, for every priced line a docket carries, which activity that line attributes to. A line is a single charge produced by Lacroo’s pricing engine — ordinary hours, overtime hours, a weekend penalty, a site allowance, an equipment hire rate, and so on. Each line becomes a row in a matrix; each activity on the docket becomes a column; the supervisor fills the cells.
The result is cost-attribution that mirrors what actually happened on site — every dollar that flows to your ERP / GRN can be traced back to the activity that earned it.
What it is
Two ways to allocate a docket exist in Lacroo:
| Mode | What the docket stores | When to use it |
|---|---|---|
| Hours per activity | One total hours value per activity. The pricing engine spreads each priced line across the activities by the ratio of activity hours. | Day-rate plant, generic labour gangs, blended work where everyone is doing roughly the same thing. The default for every project. |
| Per-activity allocation | One quantity per (priced line × activity) cell. The supervisor decides, line by line, how much of each charge goes to which activity. | Civils sites that need accurate cost attribution per pour, pad, face, or zone. Project agreements that bill specific tasks at specific rates. Anywhere supervisors think in priced lines. |
The pricing engine itself is the same in both modes. What changes is the surface the supervisor sees and how the docket’s priced lines reach your GRN export.
Entering and reading hours
An activity’s hours box takes any duration, on the web and in the app:
| You type | It records |
|---|---|
1.25 | 1.25 h |
1:20 | 1.33 h |
80m | 1.33 h |
1h 20m | 1.33 h |
90 min | 1.5 h |
Hours are kept exactly and shown to two decimals: a 06:00–17:45 shift less a 30-minute break reads 11.25h, a whole number reads 8h. The box shows the exact stored figure, so typing back what you see changes nothing. Something that isn’t a duration is refused and the box goes back to what it held.
The COINS export sends quantities to two decimals, so 1 h 20 m goes out as 1.33.
When you’d use it
Per-activity allocation is for projects where the supervisor explicitly attributes priced lines — ordinary hours, overtime, penalties, allowances — to specific activities for cost-allocation accuracy.
Typical signals:
- The cost-controller asks “which activity carried the Saturday penalty?” when reviewing dockets.
- One worker on a 10-hour shift moves between two activities and you need to bill each correctly.
- The company catered lunch and the meal allowance shouldn’t bill.
- The engine missed a wet-weather allowance because rain wasn’t a structured input.
If none of those apply and a single hours-per-activity number is enough, leave the project on the default (Hours per activity).
Setup — the project default
A project’s default sets the mode for every new docket on that project. Existing dockets keep their current mode; supervisors can override per-docket from the docket sheet (see below).
Walkthrough:
- Open
/admin/configfor the project. - Scroll to the Activity Allocation card on the Settings tab.
- Click the inactive option (
Hours per activityorPer-activity allocation) to stage a change. The Save button becomes enabled. - Click Save. A toast confirms the change. Every docket created from this point on uses the new default.
Who can change it. Admin-tier users only — master_admin, organisational_manager, or project_admin. Non-admins see the current value but cannot change it.
Screenshot —
apps/docs/public/screenshots/activity-allocation/project-default.png(to be captured): the Settings tab’s Activity Allocation card with the segmented control.
Per-docket override
Each docket carries its own mode. You can flip it independently of the project default — useful when one specific docket needs the opposite treatment.
Walkthrough:
- Open any draft docket from the workbench or the diary’s Dockets tab.
- The header shows a segmented control above the tabs labelled Activity Allocation.
- Click the inactive option. A toast confirms the new mode. The Activities section below re-renders with the appropriate editor.
The toggle is only mutable when the docket is in draft. After submit, it locks. If you need to flip it, an admin can revert the docket back to draft level by level — see the revert flow in the Dockets module.
Anyone with edit rights on the docket — supervisor, senior supervisor, engineer, or any admin — can flip the mode.
The matrix
In Per-activity allocation mode, the Activities section becomes a matrix:
- Rows are priced lines emitted by the pricing engine — one row per (worker × tier) and one row per (equipment × rate type). A Saturday docket typically shows four rows per worker: ordinary hours, overtime 1.5×, Saturday penalty, site allowance.
- Columns are the activities on the docket. Each column header shows the activity name and a cost code badge underneath.
- Cells are quantities. You type how much of the row’s line goes to each activity. The unit (hour, day, occasion, week) is inherited from the line.
- Row total sits on the right edge of each row. A green checkmark appears when the cells sum to the line’s quantity. Amber appears under, red appears over.
- Activity totals sit along the bottom — useful for verifying every activity has been accounted for.
- Resource separators group consecutive lines for the same worker or plant under a dark pill showing their name, classification, line count, and per-resource subtotal in dollars.
Screenshot —
apps/docs/public/screenshots/activity-allocation/matrix-overview.png(to be captured): the line × activity matrix with two workers, four lines each, and three activities.
Mode per line
Each row carries its own mode toggle:
- Manual — you type each cell. Useful when you know exactly how much of a line went to each activity.
- Pro-rate (even split) — Lacroo divides the line’s quantity equally across every activity on the docket. Three activities and 9 hours of ordinary time? Each activity gets 3 hours. No anchor logic, no inheritance — pure even-split.
Flip a row by clicking the mode chip in the Mode column.
What happens when you flip:
- Manual → Pro-rate clears the row’s cells. The even-split is computed on read; nothing is stored.
- Pro-rate → Manual seeds the row’s cells with the even-split values, so you can adjust from there without retyping.
Switching modes never affects other rows. A docket can carry some lines in manual mode and others on even-split simultaneously.
Sum-per-line constraint
The cells in a row must add up to the line’s quantity:
- Even-split rows always validate by construction.
- Manual rows show a green checkmark when sum matches, amber when under, red when over.
Submit is blocked while any manual row is under or over. Even-split rows never block submit.
Removing a line
Sometimes the pricing engine emits a line that shouldn’t bill. The classic case: a 10-hour shift triggers a meal allowance but the company provided lunch.
To remove a line:
- Hover the row. The remove (
×) action appears on the right. - Click it. The row strikes through, dims to 50% opacity, and the row total replaces with
Removed — $X not billedin amber. - The top of the matrix shows an aggregated amber pill summarising all removed lines and total value not billed.
Removed lines do not flow to the GRN. The pricing engine’s total still includes them for divergence math — so the cost-controller can always see “engine priced $1,420, you billed $1,395, $25 not billed” on the submitted docket.
To restore a removed line, hover the row and click the restore action (↺) that’s visible while a line is removed.
Screenshot —
apps/docs/public/screenshots/activity-allocation/line-removed.png(to be captured): a meal allowance row struck through with the amber “1 line removed · $25 not billed” pill at the top.
Engine-emitted vs you-added: deletion semantics
- An engine-emitted line is soft-removed — the row stays in place, struck through, restorable. This preserves the audit trail.
- A line you added yourself (see next section) is hard-deleted when removed — the row disappears. You can re-add it from the catalog if needed.
Editing a quantity (pinning it)
A priced line’s quantity normally comes from the shift: 8 hours of ordinary time because the worker was on site for 8 hours. Sometimes it’s wrong and the shift can’t easily say so — a worker was stood down for an hour that still gets paid, a machine’s meter was misread, a penalty applies to a different number of hours than the roster implies.
You can set a line’s quantity by hand. Doing so PINS it.
Walkthrough (web): the matrix row shows qty × rate under the tier label. Quantity edits happen on the phone app today; on web you see the pin and can clear it (below).
Walkthrough (Expo app): open the docket, find the line under Pay Lines & Contract Items, tap the pencil, type the quantity, tap Save.
What “pinned” means
The pricing engine keeps re-pricing the docket around that line — the rate still tracks the payment schedule, the line still splits across activities, the docket total still recomputes — but the quantity stays where you put it. Every re-price path honours the pin: an edit to the shift, a classification change, a supplier negotiation, the background worker, the payment auto-fix, even a sign-on attaching to an existing docket.
Before this, editing an engine-emitted line’s quantity was refused outright, because the next re-price overwrote it and the edit vanished silently. Now it sticks.
The risk, and what protects you from it
A pin outlives whatever it was patching. If you pin 9.5 hours and someone later corrects the shift to 8 hours, the docket keeps billing 9.5 — that is the point of a pin, and it is also how a docket quietly bills the wrong number for weeks.
Three things make that visible instead of silent:
- Every pinned line is marked, on every surface that shows the quantity. An
Editedbadge on web (matrix row, stack card, and the read-only priced-lines pane); in the Expo app aSet by handchip under the line AND aBy handchip on the same row in the Pricing breakdown further down, which is the only place that quantity appears once the docket is submitted. The app’sEst. Totalmoves with the pin too. It shows on submitted, reviewed and confirmed dockets — the reviewer signing the money off sees it. - The engine keeps telling you what IT calculates. Each re-price records the quantity it would have used. The moment that differs from the pinned figure, the badge turns amber and names both:
Edited · calc 8on web,Set by hand · shift says 8on the app. - You can always un-pin. See below.
Clearing a pin
- Web: click Unpin on the row (matrix) or Unpin quantity (stack card).
- Expo app: tap the pencil, then Use N from the shift.
Clearing a pin re-prices the docket in the same action, so the engine’s own quantity is back immediately — you never end up with the hand-entered number sitting there with the badge gone.
That guarantee is enforced, not merely intended: if the re-price can’t run, the un-pin fails and the pin stays, rather than reporting success. You will see one of:
- “This docket has no contract linked, so there’s no calculated quantity to go back to.” — there is nothing for the engine to re-derive. Link the contract, then un-pin.
- “Couldn’t recalculate this docket just now, so the quantity is still set by hand. Try again in a moment.” — a transient failure; the pin was put back untouched, badge and all.
A failed un-pin leaves a visible pin you can retry. A “successful” one that didn’t actually re-derive would leave an invisible one nobody could — which is the failure this endpoint exists to prevent.
What can’t be pinned
| Line | Why not | What to do instead |
|---|---|---|
A manual-mode line (line-mode-manual) | It bills from its per-activity cells, not its quantity column. Writing the quantity would change every number on screen and nothing on the invoice. | Edit the cells in the matrix. |
A removed line (line-removed) | It contributes $0. A quantity on it would be a number that bills nothing. | Restore the line first (↺), then edit. |
A materials / scheduled-item / worker-group line (line-not-pinnable) | Their quantity is ENTERED further up the docket and the priced line is derived from it. Pinning the derived copy would leave two competing quantities with no rule for which wins. | Change the quantity on the materials or scheduled-item row itself — the priced line follows. |
Pins and duplicated dockets
Duplicating a docket (diary duplicate) copies the lines but not the pins. A pin is a statement about one shift; carrying it forward would silently override tomorrow’s engine quantity with yesterday’s hand-edit, and mark a row nobody had edited. Re-pin on the new docket if you still mean it.
Adding a line from the catalog
If the pricing engine missed a charge — the supplier’s wet-weather allowance the supplier’s contract permits, a fixed penalty for after-hours work — you can add it directly to the docket.
Walkthrough:
- Scroll to the resource (worker or plant) the line belongs to.
- Click
+ Add line for [name]beneath the resource’s existing lines. - The catalog picker opens. It lists every allowance and fixed penalty on the supplier’s contract that isn’t already on the docket.
- Pick a catalog item. Enter a quantity (hours, days, occasions — depending on the catalog unit). Any decimal is accepted, such as 0.33.
- Click Add. A new row appears with an Added badge. It defaults to pro-rate (even-split) mode.
No free text. The picker only shows items from the supplier’s contract — you cannot type your own line name or rate. This keeps every billable charge traceable back to an agreement.
Already-on-docket items are filtered out of the picker so you can’t double-add the same allowance.
Once added, the line behaves identically to engine-emitted lines: same mode toggle, same cell editing, same sum check.
Screenshot —
apps/docs/public/screenshots/activity-allocation/add-from-catalog.png(to be captured): the catalog picker open showing “Wet weather allowance — $5.00/hr” and “After-hours penalty — $50.00/occasion”.
Submitting a docket
When the docket is ready, click Submit.
If you haven’t removed or added any lines and every row balances, submit is one click — nothing extra.
You don’t need to add an activity or a cost code before submitting. A docket with none submits as-is and shows as Unallocated in reports. Cost codes are checked when the docket is pushed to your ERP.
If you have made any overrides, a soft-warn banner appears so you can confirm the divergence is intentional:
| What happened | Banner tone | What it says |
|---|---|---|
| One or more engine lines removed, removed value ≤ 10% of engine priced | Blue (info) | “X line(s) removed — $Y.YY not billed” |
| Removed value > 10% of engine priced | Amber | ”Significant divergence: X line(s) removed (Z% of engine priced)“ |
| One or more lines added by you | Blue (info) | “X line(s) added — +$Y.YY additional value” |
Click Confirm to proceed, or Cancel to keep editing.
After submit, the docket is locked from edits. To make changes, an admin can revert the docket back to draft step by step.
Submitting several dockets at once
Submit on the dockets list, Select all matching → Submit, and the diary’s Submit all run the same checks as a single Submit. A docket that fails them is skipped and stays in draft. The rest still submit.
The message says why a docket was skipped:
| Message | What to fix |
|---|---|
no supervisor | Pick a supervisor on the docket. |
custom fields incomplete | Fill in the required custom fields. |
no workers or lines | Add a worker or a line to the subcontractor docket. |
unassigned workers | Give each worker a classification or add them to a crew group. |
missing required: … | Fill in the named fields on the materials docket. |
When a docket cannot be priced
Submit, Review and Confirm work out a docket’s price before it moves. If no price can be worked out, the docket stays where it is. This applies to one docket, to a selection, to Select all matching, and to the diary’s Submit all, Review all and Confirm all.
One docket shows:
Docket not confirmed. No price could be worked out for it. Check its contract and rates.
Several dockets show what moved, then which ones did not:
Confirmed 3 of 5 dockets
2 not confirmed: GG-0012, GG-0019. No price could be worked out for them. Check their contract and rates.
On the web dockets list, a long list ends “and 4 more”, and Show dockets opens every docket that did not move. The mobile app shows the same words in an alert.
To fix it, open the docket and check that its contract has a rate for each worker, machine or material on it.
What the GRN export receives
Each non-zero cell becomes one line in the GRN payload that flows to your ERP. The cell’s quantity, the line’s rate, the activity’s cost code, and the resource’s identity all flow through verbatim. There’s no further decomposition — the supervisor’s distribution is the source of truth.
Worked example. John Smith on a 10-hour Saturday, two activities (Excavate west pad, Backfill east). Engine emits four lines; supervisor distributes:
| Line | Activity 1 cell | Activity 2 cell |
|---|---|---|
| Ordinary 8h × $45/hour | 5h | 3h |
| Overtime 1.5× 2h × $67.50/hour | — | 2h |
| Saturday penalty 8h × $15 (even-split) | 4h | 4h |
| Site allowance 1 day × $30 | 1 day | — |
The GRN receives 7 lines (one per non-zero cell). Activity 1 carries $225 ordinary + $60 penalty + $30 allowance; Activity 2 carries $135 ordinary + $135 overtime + $60 penalty. Each line is independently traceable.
Removed lines never emit. Lines you added emit identically to engine-emitted ones.
Re-pricing while you edit
If you change something that affects pricing (shift start time, breaks, a worker’s classification), the engine re-prices the docket automatically. The matrix tries to preserve your distribution wherever the line identity is unchanged:
- Lines whose identity is the same after re-pricing keep their cells, mode, removed state, and Added badge.
- Lines whose identity changed (a worker reclassified mid-edit, a new overtime rule kicks in) are replaced. Their old cells are cleared.
- A banner at the top of the matrix tells you when something was replaced: “Re-priced: 3 lines retained, 1 line replaced — re-distribute 1 line.”
Lines you added yourself are immune to re-pricing — they only change when you change them.
A line with a pinned quantity keeps that quantity through every re-price. Its rate still refreshes and its amount recomputes at the new rate — the pin freezes the quantity, never the price. If the engine stops emitting a line altogether and later re-emits it, the pin is dropped: the tier the pin was made against went away.
Mobile
The matrix collapses to a stack of cards on phones and the Expo app — one card per priced line, grouped under resource section headers. The same mode toggle, the same cells, the same remove and restore actions. The catalog picker is a full-screen sheet rather than an inline row.
In the Expo app the docket screen shows one list, Pay Lines & Contract Items — the worker’s own allocated pay lines and any extra contract items together, grouped by worker or machine. (These were two separate sections until the 2026-08-13 ruling merged them.) Rows stay distinguishable: an allocated pay line leads with a coloured tier dot and reads from your shift; an extra leads with a price-tag icon and reads added or allocated. Each row keeps its own actions — quantity edit, remove, Undo for a removed line, delete for one you added — plus the Add Additional Contract Item button per resource.
A crew-mate’s core wage and overtime lines stay off the phone deliberately. The full matrix on web is the surface for those.
Cell edits sync live: edit on the phone, watch the desktop update via WebSocket invalidation without refreshing the page.
Error messages you might see
The submit and editing flows can produce these error codes. Most are informational — they tell you what to fix:
| Code | When you see it | What to do |
|---|---|---|
docket-not-draft | You tried to edit a cell, flip a mode, or add/remove a line after submit. | Have an admin revert the docket back to draft. |
matrix-no-lines | The docket has no priced lines (engine hasn’t priced yet, or every line removed). | Re-price the docket; un-remove at least one line. |
line-sum-mismatch | A manual row’s cells don’t add up to the line’s quantity. | Adjust the row’s cells to total the row’s quantity exactly. |
line-removed | You tried to edit cells, flip the mode, or set the quantity of a removed line. | Restore the line first (↺), then edit. |
line-mode-manual | You tried to set the quantity of a manual-mode line. | Edit the row’s per-activity cells instead — that’s where a manual line’s quantity lives. |
line-not-pinnable | You tried to pin the quantity of a materials / scheduled-item / worker-group line. | Change the quantity on the docket row it’s derived from. |
line-not-pinned | You tried to clear a pin on a line that doesn’t have one. | Nothing to do — the quantity already follows the shift. |
invalid-line-type | You tried to soft-remove a line you added (use delete), or hard-delete an engine-emitted line. | Use the correct action — × for both, the system picks the right behaviour. |
catalog-item-not-found | The catalog item you picked is no longer on the supplier’s contract. | Pick a different catalog item, or have the contract updated. |
docket-catalog-empty | This docket’s supplier has no contract items on this project, so there is nothing to add. | Link the supplier to the project and add items to their contract. |
catalog-item-already-on-docket | You tried to add a catalog item that’s already on the docket. | Find the existing line and edit it instead. |
cells-paradigm-superseded | Returned by deprecated endpoints during the transition window. | Refresh the page — the editor will load the new matrix. |
Audit trail
Every per-line decision is captured in audit_logs with a dedicated action:
| Action | When it’s written |
|---|---|
prorate-mode-change | Whole-docket toggle flipped (Hours per activity ↔ Per-activity allocation). |
prorate-line-mode-change | A single row flipped between Manual and Pro-rate. |
prorate-line-removed | An engine-emitted line was soft-removed. |
prorate-line-restored | A removed line was restored. |
prorate-line-added | A line was added from the catalog. |
prorate-line-deleted | An added line was deleted (hard delete). |
matrix-cell-update | A cell’s quantity changed. |
prorate-line-qty-changed | A line’s quantity was set by hand (and pinned, on an engine-emitted line). |
prorate-line-qty-unpinned | A pinned quantity was cleared and handed back to the pricing engine. |
Every row carries who did it, when, and the before/after values. Filter audit_logs by action LIKE 'prorate-%' for a chronological history of allocation decisions.
Related docs
- Pricing engine overview — how the engine emits priced lines from awards / contracts / classifications.
- Contracts and the catalog — what
contract_allowancesandcontract_penalty_ratesare and how to manage them per supplier contract. - ERP integrations / GRN push — how priced lines reach COINS once a docket is confirmed.
- Level-by-level revert — how to step a submitted or confirmed docket back to draft so the matrix unlocks.
Spec & plan
- Spec:
docs/superpowers/specs/2026-05-13-prorate-line-allocation-design.md - Plan:
docs/superpowers/plans/2026-05-13-prorate-line-allocation.md
Splitting a subcontractor worker or crew across activities
On a subcontractor docket, each worker and each crew row has a Split activities button, on the web and on the phone. It divides that row’s priced line between two or more activities, for example 2 hours on one activity and 6 on another. The amounts must add up to the line. A crew is still billed once; only the cost-code attribution changes.
- Each activity can be used once, and every other row must already have an activity (or be split too), otherwise Lacroo says which row needs one.
- Rows that already had a single activity keep exporting exactly as before.
- Scheduled items and materials stay on a single activity.
- Who can split: office, the docket’s crew lead (on any row), and a worker on their own row. A worker cannot split a crew line or another worker’s line.
- Splitting, changing or removing a split follows the same rule as editing the row’s hours: it is possible at any stage you could still edit the row, and never once the docket has been sent to COINS.
- While a row is split its single activity is locked. Remove split puts it back on the docket’s activities.
- The activities must be on the docket’s diary.
- Each activity is sent to COINS as its own line with that activity’s cost code.