The month closed. The commissions did not. — A case study
A sale becomes a job, and the shelf knows about it.
Before
The storefront took the order, the warehouse discovered it, and the install went into a calendar somewhere else. Three records of one event, kept in step by attention. Stock counts drifted from reality between counts, so the cost side of every job was an estimate — which meant the margin was an estimate, which meant the commission was an estimate too, all the way down.
Now
A sale creates one job that owns everything about itself: the materials it consumes, at the cost those materials were actually bought at, and the service that has to be delivered. Stock moves because the job consumed it, not because somebody remembered to record it. The cost side becomes a fact rather than a reconstruction.
- One job record carrying the sale, its materials and its install
- Stock consumed by the job, at real purchase cost rather than list price
- Inventory and service tracked in the same system, not reconciled between two
- Margin available as soon as the job is specified, not at month end
A job becomes what two people are owed.
Before
The rates lived in a document, and the interpretation lived in somebody's head. Each month the closed jobs were pulled into a spreadsheet, rates applied by hand, and each one split between the setter and the closer according to what everyone remembered the arrangement to be. Two reps disagreeing about a split was resolved by seniority, because there was no artefact to point at.
Now
Commission is derived from the job, not typed next to it. The rules are data the business edits itself, the split between setter and closer is recorded on the job at the moment it closes, and the rate is resolved against the version of the rules that was live on that date. Changing next quarter's rates does not quietly rewrite last quarter's payouts.
- Rate rules held as data, editable without a deploy
- Rules effective-dated; a historic job is calculated under its own era's rules
- Setter and closer split captured on the job at close, not read from a role later
- Commission computed on real margin, so a discount given away is felt by the person who gave it
The month closes, and the numbers keep moving anyway.
Before
This is the part the spreadsheet had no answer for. A commission paid in March against a job cancelled in May was simply money that had gone. Recovering it meant somebody noticing, working out what should have been paid, and then having that conversation from a standing start with nothing to show. Most of the time nobody noticed, and when they did the argument cost more than the amount.
Now
What a rep is entitled to is recomputed from the job's current facts, and what they were actually paid is recorded separately. The difference between those two is the adjustment — owed, or owed back — and it exists as a number the moment the job changes rather than whenever somebody investigates. The rep can see it, and the reason for it, before payday rather than after.
- Entitlement derived continuously from the job as it stands now
- The ledger records only what was genuinely paid out
- The difference is the adjustment, surfaced when it arises
- Nothing pays out on revenue the company has not yet collected
A company that sells roofing materials and installs them was rebuilding its entire commission run by hand every month — and then defending it, line by line, to the reps who did not believe it. The arithmetic was not the hard part. The hard part is that a commission depends on a job's margin, a margin depends on what the job actually consumed, and all of it keeps moving after the money has gone out. We built the system that holds inventory, jobs and commission as one chain, so the number explains itself and nobody has to reconstruct it.
Selling a roof is two transactions wearing one coat. Materials leave a shelf, and a crew goes into a calendar — and for this company those lived in different places, reconciled by whoever noticed the discrepancy first. A job could be booked against stock that had already gone out on another job, and nothing in the process was going to say so.
Sitting on top of that was the commission structure, which is where the real cost was. A sale involves two people: whoever sets the appointment and whoever closes it. Both are owed a share, the share depends on the rate, and the rate depends on the margin of the job — which depends on what materials the job actually consumed and what those materials actually cost. Commission is therefore downstream of inventory, and the two systems had never been introduced.
So every month somebody rebuilt the whole thing in a spreadsheet: pull the closed jobs, look up the materials, apply the rates from memory or from a document, split each one between two people, and publish. It took real time from someone senior, and the output was then audited by every rep who felt short-changed — which was, predictably, most of them, because nobody could see the working.
But the genuine difficulty is the one that a monthly calculation cannot express at all. The inputs do not stop moving when the month closes. A job cancels in May that closed in March. A customer never pays. A discount gets approved after the fact. A material price is corrected. Each of those changes what somebody was owed for work already paid for, and there was no mechanism for that idea — only a new argument.
The decisions
What was chosen, why it was chosen, and what it cost.
Entitlement is derived. The ledger stores only what was actually paid.
The inputs to a commission keep moving after payday — cancellations, non-payment, late discounts, corrected costs. Storing the computed figure freezes something that is not frozen. Derive it instead and the current truth is always available, with the adjustment falling out of subtracting what was paid from what is owed.
The cost: A number that can change is unsettling to a person whose income it is, so 'why is this different from last week' has to be answerable on the spot. That constraint is what forced the explainability below, and it made the engine slower to build than one that simply wrote a total to a column.
Commission rules are data the business edits, not code an engineer deploys.
Sales compensation changes with the season, the product and whatever the competition just did. If every rate change is a ticket, finance waits on engineering — and a finance team that waits goes back to the spreadsheet, at which point the system has been abandoned without anyone saying so.
The cost: A rules engine is a small programming language with all the obligations of one: it needs validation, a preview of what a change would do before it applies, and protection against a rule that is expressible but nonsensical. Considerably more work than a hardcoded percentage.
Rules are effective-dated. A historic job is calculated under the rules of its own era.
Editing a rate must never retroactively rewrite what people were paid last year. Without this, a routine change to next quarter's structure silently restates every payout in the system's history — which is both an accounting problem and the fastest possible way to lose the reps' trust in the tool.
The cost: Every rule change forks the history, so the engine always has to resolve which version applies before it can compute anything. Testing becomes a matter of dates as well as inputs, and 'recalculate everything' stops being a safe operation.
Commission is computed on real margin, from stock at actual cost.
Paying on revenue pays people to discount, because the discount costs the company and costs the rep nothing. Paying on margin aligns them — but only if the cost side is genuine, which is exactly why inventory and commission had to be one system rather than two that agree occasionally.
The cost: It makes payroll depend on inventory accuracy. A miscounted pallet is now a wrong commission, which raises the stakes on warehouse data considerably and means stock discrepancies have to be resolvable rather than just correctable.
Payout gates on the job's state, not on the calendar.
Paying commission on the date a deal was signed pays out against money that has not arrived and may never. Gating on installed-and-collected means the company never funds a commission out of revenue it has not received.
The cost: Reps are paid later than they were, and that is a genuine morale cost that no amount of engineering makes pleasant. It had to be decided as a business policy and communicated as one — the system only enforces it, and building it did not make the conversation easier.
The setter/closer split is captured on the job at close, not read from a role afterwards.
People change roles, leave, and swap territories. If a split is resolved by looking up who currently holds a position, then reorganising the sales team silently rewrites who earned what on jobs closed months ago.
The cost: It puts an obligation on the process at exactly the busiest moment — the close — and a job whose split was never recorded is a job the engine cannot pay. The interface has to make it near-impossible to close without it.
A rep sees their running commission continuously, not once a month.
The disputes were not really about arithmetic. They were about a number appearing once a month with no working shown, to people whose income it was. A figure that is visible all the time and explains itself removes the argument before it has anywhere to start.
The cost: It also makes every clawback visible in real time, which turns one uncomfortable monthly conversation into several small ones. Better, but not painless, and it is a change-management problem rather than a feature.
Every figure expands to its working. No shortcut that cannot be shown.
The deliverable was never the number — it was the end of the argument about the number. A commission that can be opened up to the job, the materials it consumed, the margin those produced, the rule version applied and the split agreed is a commission nobody has to audit by hand.
The cost: It constrains the engine permanently. A faster route to a correct total is not allowed if the route cannot be displayed, so some optimisations are simply off the table for as long as explainability is the point of the product.
Built on
- Next.js
- The job workflow, the stock views and the rep-facing commission screens.
- Supabase
- Postgres, auth and the row-level rules that keep a rep's figures to that rep.
- PostgreSQL
- Stock, purchase costs, jobs, rule versions, entitlements and the payout ledger.
- TypeScript
- Shared across the app and the commission engine, so a rate means one thing everywhere.
Talks to
- Payroll
- Accounting export
What changed
- Commission is derived from the job rather than rebuilt by hand each month
- A sale moves stock and schedules the install as one action
- Rates change without a deploy, and previous payouts stay as they were
- Commission is computed on real margin, from what a job actually consumed
- A rep can see what they are owed today, and the working behind it
- A cancellation or a late discount produces an adjustment rather than an argument
- Nothing pays out on revenue the company has not collected
- Every figure expands to the job, the materials, the rule version and the split
If your team checks the number, the number is not doing its job.
A figure people re-derive by hand is a figure nobody trusts, and the expensive part is never the arithmetic — it is the argument afterwards, every month, with the people you need selling. Tell us which number in your business gets checked, and you leave the first conversation with a scope, a budget and a ship date.
