Gross Margin per Job + Job Costing

Why this matters

Most service-business owners track revenue per job + maybe materials cost. Very few track TRUE gross margin per job - labor + materials + truck cost + warranty reserve + everything that touches that specific job. The owners who track this catch the loss-leader jobs before they bleed the business + spot the high-margin work to chase. The owners who don't track this leave 8 - 20% margin on the table month after month.

The full job cost equation

For any job, the true cost to deliver is:

Direct labor cost
+ Materials cost (at YOUR cost, not retail)
+ Subcontractor payments
+ Truck cost allocated (fuel + maintenance + depreciation)
+ Disposal / dump fees
+ Permit fees
+ Job-specific equipment rental
+ Warranty / callback reserve
+ Sales commission (if applicable)
= TOTAL JOB COST

Subtract this from the customer invoice = TRUE Gross Profit. Divide by invoice = TRUE Gross Margin %.

This is different from the simplified "invoice minus materials" most operators use.

Direct labor cost (most-mis-calculated)

Labor cost is NOT just wage x hours. Add: payroll taxes (~13 - 15%), workers comp (3 - 15% trade-specific), health insurance, PTO/benefits (~10%), plus non-billable time (40 - 60% utilization typical).

Worked in wage-units. Index the tech's base hourly wage at 1.00 and stack the loads on top of it:

Component Share of base wage
Base hourly wage 1.00
Payroll taxes (FICA, FUTA, SUTA) 0.14
Workers comp, mechanical-trades band 0.06
PTO + holidays + benefits 0.10
Health insurance (illustrative, see note) 0.20
Loaded cost per PAID hour 1.50

Health insurance is the one line that is not a percentage of anything. It is a flat per-head premium, so it loads a junior tech's hour far more heavily than a senior tech's. The 0.20 above is illustrative; yours is that tech's annual premium divided by his annual wage, and it is the line most worth computing for real.

So an hour of that tech PAID costs 1.50 times his wage. You only bill some of those hours. At 60% utilization, 1.50 / 0.60 = 2.50 times the base wage per BILLABLE hour. That is the number that belongs in a job cost, and it is two and a half times the figure most operators use.

Now price against it. Bill that hour at 5.3 times the base wage and the labor line carries about 53% margin, healthy for service work. Bill it at 3.6 times the wage and you are at just over 30%, which is underpriced - and it sounds generous only because anyone anchored on the wage hears "over three and a half times what I pay him" and stops there. Most operators calculate against the unloaded wage + lose money. The wage is not the cost. The billable hour is.

Materials cost at YOUR cost

Materials cost = what YOU paid the supplier for the parts (NOT what you charged the customer).

Common operator mistake: thinking "I make 30% on materials" without tracking actual supplier cost.

Markup options:

  • Standard markup: 30 - 100% over your cost (typical residential service)
  • Flat-rate book: customer-facing rate that incorporates parts + labor + margin
  • Cost-plus: time + materials at YOUR cost + agreed markup percentage (commercial work)

Track materials at supplier-invoice cost. The receipt is the source of truth.

Truck cost allocation

The truck delivering the tech to the job costs money whether it moves or not:

  • Payment or depreciation (it loses value sitting in the yard)
  • Loan interest (if financed)
  • Commercial auto insurance
  • Fuel
  • Maintenance, tires + the annual inspection
  • The tools + stock riding in it

Allocate to jobs:

  • Hours method: total truck cost / annual billable hours = truck cost per billable hour
  • Mile method: cost per mile x miles driven per job
  • Simpler: add 5 - 10% of revenue per job as "truck cost"

Pick a method + stay consistent for tracking. Both worked examples below use the percent-of-revenue shortcut, because it is the only one of the three you can apply without keeping a second set of records.

Warranty / callback reserve

Reality: some jobs come back. Customer reports the heater isn't working 3 weeks after install; tech returns at no charge.

The cost of callbacks is real. Track + reserve:

  • Industry average callback rate: 3 - 8% of jobs
  • Reserve: 1 - 3% of revenue per job as "warranty reserve"
  • Funds the inevitable warranty + callback work

Operators who don't reserve get hit by callbacks that erode margin invisibly.

Job costing example: HVAC blower motor replacement

A medium repair - blower motor failed, one tech diagnoses and replaces it in a single visit. Index the customer's invoice at 1.00 and read every cost line as a share of that invoice. The two percent-of-revenue allocations are shares of the same 1.00, so they are literally the 8% and the 2% they say.

Cost line Share of invoice
Direct labor (loaded billable-hour cost x hours on site) 0.21
Materials at YOUR cost (motor, capacitor, wire) 0.17
Truck allocation at 8% of revenue 0.08
Warranty / callback reserve at 2% of revenue 0.02
Disposal of the old motor 0.01
Total job cost 0.49

Gross profit is 1.00 - 0.49 = 0.51, so gross margin is 51%. That sits inside the 40 - 60% band this article gives for a medium repair, and one point above the shop's own 50% "winner" line.

Now drop the two lines most operators never post. Without truck allocation (0.08) and without warranty reserve (0.02), cost reads 0.39 and margin reads 61%. Same job, same invoice, same work performed: you would believe the margin was 61% when it was 51%, overstating by exactly 10 points. Those two lines together are 0.10 of the invoice, which is where the 10 points come from. The overstatement is not an estimate, it is the size of what you left out.

Job costing example: roof replacement

Tear-off + new shingle, 32 crew-hours. Index the quoted invoice at 1.00 again.

Cost line Share of invoice
Materials at YOUR cost (shingle, underlayment, flashing, fasteners) 0.31
Direct labor, 32 crew-hours loaded 0.24
Disposal (dumpster + tipping fee) 0.04
Truck allocation at 5% of revenue 0.05
Warranty / callback reserve at 2% of revenue 0.02
Total job cost 0.66

Gross profit is 0.34, so 34% margin - well inside the 25 - 40% install band, and normal for a roof.

Now quote the same roof 12% cheaper to win it. This is where operators guess wrong, because they assume cost falls along with price. It does not. The shingle, the 32 hours + the dumpster do not care what you quoted: those three lines are 0.31 + 0.24 + 0.04 = 0.59 of the ORIGINAL invoice, and they stay at 0.59 in absolute terms. Only the two percent-of-revenue allocations shrink, because those are defined as a share of whatever you billed.

At 0.88 of the original price: cost = 0.59 fixed + 7% of 0.88 for truck and warranty together, which is 0.59 + 0.0616 = 0.6516. Margin = 1 - (0.6516 / 0.88) = 26%.

Read what the discount cost. A 12% price cut bought an 8-POINT fall in margin: two thirds of a margin point for every one percent of price given away. It also moved the job from a normal roof to one point above the shop's own 25% loss-leader line, which is inside the error bar of anybody's cost tracking. The crew spends the same 32 hours on the roof either way.

Notice what the 25 - 40% install band would have told you: nothing. The underpriced roof is still inside it. A band that wide catches a catastrophe, not a discount, which is why the comparison that matters is this job against the same job quoted properly.

What to do with this analysis

Track each job in the same format

Most modern field-service software (Manuall, ServiceTitan, Housecall Pro) has job costing built in. Use it. Enter:

  • Time logged per tech per job (mandatory)
  • Material cost per job (entered at procurement, not estimated)
  • Truck allocation auto-applied
  • Reserve auto-applied
  • Compute margin per job automatically

Review monthly

  • Sort jobs by margin %
  • Identify the loss leaders (margin < 25%)
  • Identify the winners (margin > 50%)
  • Patterns: customer type, job type, tech, day-of-week, time-of-day

Every figure in this article is ONE job's margin: that job's gross profit over that job's own invoice. Rolling a period of them into a single shop-level margin is a separate question with two different correct answers, because averaging the per-job percentages and dividing total margin by total revenue disagree whenever job sizes vary - and on a mix like the two examples above, they disagree badly. Read the average-of-averages card in the references before you quote a shop margin to a lender or set it against a published benchmark.

Action on patterns

  • Loss-leader customer: drop them OR repricing conversation
  • Loss-leader job type: raise pricing OR stop offering
  • Winner job type: market for more of these
  • Winner tech: study what they do differently

The single most-impactful job-costing change for most operators is tracking TIME ACCURATELY. Techs underreport time on jobs: drive time, diagnostic time + the paperwork after. Be careful what you expect that to do to the cost, though, because finding those hours does not multiply it. Loaded cost per BILLABLE hour is loaded cost per PAID hour divided by utilization, and hours that were worked but never logged were missing from that utilization as well. Recover them and the hours on the job rise, but utilization rises with them + the cost per hour falls by about the same factor. At the shop level the two moves cancel: payroll was always payroll. The only way to get a bigger labor cost out of this is to inflate the hours + leave the old utilization figure sitting underneath them, which is arithmetic, not a discovery.

What unlogged time really wrecks is the PER-JOB number. Those hours were worked on some particular job and you cannot say which, so their cost sits on whatever job did get logged. Your shop average can be roughly right while job-by-job margin is unreliable across the board. Worse, the missing hours are not spread evenly: they are the return trip, the callback, the diagnosis nobody billed + the job that ran two hours long. Those belong to the jobs that lost money, so the loss leaders are exactly the ones your report shows as fine. Use GPS timestamps from job-to-job in your dispatch app + auto-allocate drive time. You are not hunting a bigger cost number. You are finding out which job it belonged to.

Diagnostic + repair vs install: different margin reality

Service businesses typically have a margin gradient:

  • Diagnostic (single visit, time-only): 60 - 80% margin (high)
  • Small repair (1-hour fix, simple parts): 50 - 70% margin
  • Medium repair (multi-hour, multiple parts): 40 - 60% margin
  • Install (whole-system replacement): 25 - 40% margin (lower)
  • Maintenance contract (recurring): 55 - 70% margin (high)

This is why "selling membership" + "selling maintenance" drives so much profitability - those service lines have the highest margins.

References

  • ServiceTitan, Housecall Pro, Manuall job-costing reports
  • See related: Gross Margin Percent Is an Average of Averages - which of the two shop-level margins a rolled-up figure is, and why they differ
  • Manuall internal: Reading a P&L Statement for a Service Business, Pricing Strategy Fundamentals