How the math works
An estimate that does not add up costs you the job and some of the trust you earned doing the work. This is exactly how BidPaper computes a total, and how that is verified.
Everything is whole cents
Computers store decimal fractions approximately. In most programming languages0.1 + 0.2 does not equal 0.3 — it produces0.30000000000000004. On one line that is invisible. Across a long estimate with a percentage discount and tax, it becomes a total that is a cent off and cannot be explained.
BidPaper never stores money as a decimal. $1,234.56 is stored as 123456 — a whole number of cents — and every calculation is integer arithmetic. Quantities are held the same way, in thousandths, so 2.5 hours is 2500. Tax and discount rates are held as parts per million, which is why a rate like 8.375% is exact rather than approximately 8.375%.
One rounding rule, in one place
Rounding happens at defined points and always the same way: half away from zero. A half-cent rounds up in your favor on a charge and down on a credit, which is standard commercial practice and, more importantly, is consistent.
The multiplication that produces a rounded figure is done at a precision wide enough that no intermediate value can overflow — so the rounding is applied to the true result rather than to something already slightly wrong.
The order of operations
Every document is computed in this sequence, and the sequence is the part that matters:
- Each line — quantity times unit price, rounded once.
- Line discounts — applied to that line and capped so a discount can never push a line below zero.
- Subtotal — the sum of the line nets.
- Document discount — taken off the subtotal, capped at the subtotal, thenallocated back across the lines.
- Tax — computed on each line's share after the discount, grouped by rate.
- Total — subtotal, less discount, plus tax.
- Deposit — a percentage of the total or a flat amount, capped at the total.
Why the discount is allocated, not just subtracted
A 10% discount on a document with both taxable materials and untaxed labor cannot simply be deducted at the end — the taxable base has to come down proportionally, or you collect tax on money the customer never paid.
So the discount is split across the lines in proportion to each line's value, using thelargest-remainder method. That guarantees the parts add back to the discount exactly, with no lost or invented cent. Credit lines with a negative amount are excluded from the split, because a credit should not absorb a discount it did not generate.
Why tax is grouped by rate
Tax is computed once for each distinct rate, over the summed taxable base of the lines carrying that rate — not once per line. Rounding each line separately would misstate the tax by up to half a cent per row, and would make the total depend on how many rows the same money happens to be split across. Assessing on a taxable subtotal is also how tax actually works.
Because many states tax materials but not installation labor, taxability is setper line rather than per document, and per-line rate overrides are supported for the cases where they differ.
Reordering lines never changes a total
Move a line from the bottom of the estimate to the top and every figure stays identical. That requires care: when the discount allocation has a leftover cent to place, it is assigned by a stable property of the line rather than by its position on the page. Without that, moving a row could shift a cent between a taxable and an untaxed line and change the tax.
How this is verified
The arithmetic is covered by automated tests that run on every change:
- Reference cases — fifteen documents worked out by hand, covering fractional quantities, mixed taxability, two tax rates on one document, indivisible discount splits, clamped deposits and credit lines.
- Property tests — thousands of randomly generated documents, checking that totals never depend on line order, that the discount allocation always sums exactly, that the accounting identities hold, and that no output is ever a fractional cent.
- Coverage — the money engine is held above a 95% branch-coverage floor, and the build fails below it.
What this means for you
The document your customer receives adds up, the tax basis is visible, and re-exporting an unchanged estimate produces a byte-for-byte identical PDF. None of that is glamorous. It is the part that stops a good job turning into an argument about a rounding error.
Questions
- Why does the order of discount and tax matter?
- Applying a discount after tax charges the customer tax on money nobody paid. BidPaper applies the document discount to the subtotal first, allocates it across the lines, and computes tax on what is left — so the taxable base reflects what is actually being charged.
- Why is tax calculated per rate instead of per line?
- Rounding once per line misstates the tax by up to half a cent per row, and makes the total depend on how many rows the same money is split across. BidPaper sums the taxable base for each distinct rate and rounds once per rate, which is how tax is actually assessed.
- Do the totals change if I reorder the line items?
- No. Reordering never changes a total. That is not an accident of the implementation — it is asserted by automated property tests that shuffle the lines of randomly generated documents and require every total to match.