This page builds one small domain from start to finish, so you can see how the parts covered elsewhere in this section fit together. The subject is a general contractor pricing a job: a handful of activities connected only by the variables they share, answering the question every estimator starts from — what should I bid?
It is a focused starting point, not a full job-costing system. By the end it prices a job and charts the cash flow of the work; change orders, retainage, and progress billing are left as natural extensions. That is the point of a library domain: cover one workflow well and leave room to grow.
Throughout, one scenario keeps the numbers concrete: a worker at 35perhour∗∗,∗∗40hours∗∗oflabor,∗∗8,000 of materials, a $32,000 subcontractor, 12% overhead, and a target 20% margin.
From a wage to a loaded rate
The first activity turns a base wage into a fully burdened rate:
Writing this equation brings three variables into being. You then configure each one: base_wage and loaded_labor_rate are money — a number whose value carries a currency unit, so 35 is 35 USD — and labor_burden_rate is a percent, entered as a decimal (0.30 for 30%). At a 35wageand3045.50**.
The same equation answers more than one question. Mark all three variables calculable and the activity solves in whichever direction the inputs allow:
- “What’s my loaded rate at a $35 wage and 30% burden?” → solves
loaded_labor_rate.
- “What burden rate gives me a 52loadedrateona40 wage?” → solves
labor_burden_rate.
You did not write a second equation for the reverse question. One relationship, marked calculable, covers both.
Costing the work
A task’s labor cost is the loaded rate times the hours:
loaded_labor_rate is the same variable the previous activity produces — naming it here is the whole of the wiring. That shared name is how the two activities connect, and why consistent keys matter. At 45.50for40hours,laboris∗∗1,820**.
For a crew, the natural input is a list of workers rather than a single rate. A table variable holds it — one row per worker, a loaded-rate column and an hours column — and one function totals it:
sumofproducts multiplies each row’s two cells and sums the products: it is the right tool for any weighted sum. (Reaching for sum here would total only the first column — the rates — and ignore the hours.)
Materials and subcontractors follow the same markup shape:
At 8,000ofmaterialsmarkedup1532,000 subcontractor marked up 10%, those are 9,200∗∗and∗∗35,200.
Rolling up to a bid
The costed pieces sum into a direct cost, overhead is allocated on top, and the two give the job’s total cost:
For the scenario: direct cost 46,220∗∗,overheadat125,546.40, total job cost $51,766.40. None of these activities needed explicit wiring — each names variables the earlier activities produce, and the domain composes into a single solvable chain. See how activities compose.
A contractor states the markup two ways, so there are two routes to the bid:
These are two activities that produce the same variable from different inputs. Supply a margin and the first resolves; supply a markup and the second does. They are complementary pathways, not duplicates — the domain answers the question whichever way the estimator happens to phrase it. A 20% margin gives a bid of $64,708.
Margin is not markup
margin_pct and markup_pct are the kind of pair that quietly breaks a domain if you let them blur together. Margin is a percentage of the sell price; markup is a percentage added to the cost. A 20% margin is a 25% markup on the same job — the bid is identical, the number is not.
Two small activities convert between them:
The real safeguard is in the descriptions. Because a contractor might say “twenty percent” for either, each variable’s description claims its own phrasing and names the other to rule it out — margin is a percentage of the bid price, distinct from markup, which is a percentage added to cost. That mutual disambiguation is how you keep results landing on the variable you mean.
When two variables compete for the same words, resolve it in the descriptions, not the equations — and make the distinction point both ways, so each names the other. A pair separated in only one description still collides from the other side.
Seeing the result
Pricing the job is one workflow; tracking its cash is the next, and it shows how a domain turns a series of values into a picture. Say the work bills and pays out over several months. Two table inputs hold the draws and the costs, one row per period, and a subtraction gives the net per period:
Two tables of the same shape subtract element-wise, so this is a one-row-per-period table of net cash — positive in months you collect more than you spend, negative when you spend ahead of billing. Give that variable a bar_chart display type and it reads as bars above and below zero; the chart is a presentation of the table, not a separate value.
The running total — how far underwater is the job at its worst? — is a loop that carries a cumulative sum:
Each row adds the period’s net cash to the running total in last, so the table climbs and dips with the project and its final row is the job’s net cash position. The functions here — loop, length, item — are covered in the function library.
Where it could go next
The domain prices a job and shows its cash flow — a real tool an estimator would use, and an obvious invitation to extend:
- “Add a change order at a price and roll it into the contract value.”
- “Withhold retainage from each draw and track the balance.”
- “Track cost-to-date against the budget and project the cost at completion.”
Each is a few more activities over the variables already here. Before publishing any of it, round-trip the calculable variables in the Test view to catch drift: solve bid_price from a margin, then recover that margin from the bid, and confirm the value holds. Because the activities are reversible, it should.