Billing Methodology
How invoice amounts are calculated for each rate type.
All current rate types use actual workdays (Mon–Fri) in the specific month as the fundamental unit. This keeps proration (partial months) and deductions (absences, holidays, vacation) calculated against the same daily value — no inconsistency between how we charge for a partial month and how we credit for a missed day.
One historical exception: monthly invoices dated before PRORATION_CUTOFF (2026-04-01)
that didn't opt out of paid holidays/vacation use a legacy calendar-day proration with
hourly × hours absence deductions, matching the formula that QuickBooks invoices were
generated under historically. Reconciliation against pre-cutoff QB invoices needs to know
about this — see "Pre-cutoff legacy formula" below.
Monthly contracts (current)
The monthly rate is prorated by workdays, and the same per-workday rate drives every deduction:
effective_daily_rate = monthly_rate / workdays_in_month base_amount = workdays_in_intersection × effective_daily_rate
| Scenario | Formula |
|---|---|
| Full month | monthly_rate |
Partial month (mid-month start/end, including revised_end) | workdays_in_intersection × effective_daily_rate |
| Absence (always deducts) | −effective_daily_rate per absence row |
Holiday — only when paid_holidays = false | −effective_daily_rate per weekday holiday |
Vacation — only when paid_vacation = false | −effective_daily_rate per weekday vacation day |
- Effective daily rate = $10,000 / 22 = $454.55
- Full month = $10,000.00
- Start Apr 16 (11 workdays) = 11 × $454.55 = $5,000.00
- 1 absence = −$454.55
- 1 unpaid holiday (
paid_holidays=false) = −$454.55 - Start Apr 16 + 1 absence + 1 unpaid holiday = 9 × $454.55 = $4,090.91
Pre-cutoff legacy formula
Monthly invoices dated before 2026-04-01 with both paid_holidays = true and
paid_vacation = true (the historical default) use the legacy formula instead — calendar-day
proration plus hourly × hours absence deductions:
base_amount = monthly_rate × (calendar_days_in_intersection / calendar_days_in_period) hourly_rate = monthly_rate × 12 / 2080 absence_deduction = absence.hours × hourly_rate net_amount = base_amount − sum(absence_deductions)
Holidays and vacation are not deducted here — they were always considered baked into the
monthly rate under this model. Setting paid_holidays = false or paid_vacation = false
on a monthly contract bypasses the legacy branch entirely (it falls through to the workday-based
model regardless of invoice date), since the legacy formula has no concept of per-day deductions.
Daily contracts
Billing is hours-first: compute hours_worked, then convert to days × rate:
weekday_hours = workdays_in_intersection × daily_hours hours_worked = weekday_hours − holiday_hours − absence_hours − vacation_hours days_worked = hours_worked / daily_hours amount = days_worked × daily_rate
Where daily_hours = weekly_hours / 5 (40h/week → 8h/day). Holidays count as full
days (holiday_hours = holidays.length × daily_hours); absences and vacation read
hours directly from each row, so partial-day records are preserved.
| Deduction | Source | Applied when |
|---|---|---|
| Holiday hours | Resource contract's holiday schedule (country-specific) | paid_holidays = false |
| Vacation hours | Resource Vacation rows for the period | paid_vacation = false |
| Absence hours | Resource Absence rows for the period | Always |
false:
- Weekday hours = 22 × 8 = 176
- Hours worked = 176 − 8 − 8 − 4 = 156
- Days worked = 156 / 8 = 19.5
- Amount = 19.5 × $400 = $7,800.00
Hourly contracts
Same hours-first computation as daily, but the final step skips the days conversion:
amount = hours_worked × hourly_rate
Same flag semantics: holidays / vacations only deduct when their respective flag is false;
absences always deduct.
paid_holidays = true, paid_vacation = true:
- Hours worked = (22 × 8) − 8 = 168
- Amount = 168 × $50 = $8,400.00
paid_holidays / paid_vacation flags
Both flags live on ClientContract and apply to all three rate types:
| Flag | Default | Effect when true | Effect when false |
|---|---|---|---|
paid_holidays |
true (existing contracts migrated to true) |
Holidays are paid at the daily rate. Monthly: no deduction (covered by monthly rate). Daily/hourly: holiday hours are not subtracted from hours_worked. |
Holidays unpaid. Monthly: each weekday holiday deducts one effective_daily_rate as a deduction row. Daily/hourly: holiday hours subtracted from hours_worked. |
paid_vacation |
true |
Same as above: vacation paid at the daily rate; not deducted. | Vacation hours / days deducted using the same formula as holidays. |
Setting either flag to false on a monthly contract also forces the
workday-based model even for pre-cutoff invoice dates, since the legacy calendar-day formula
can't express per-day deductions.
Absence cost (display-time)
The amount shown for each absence on an invoice is recomputed at display time by
computeAbsenceCost(contract, invoiceDate, absence), which picks the formula the
invoice was originally billed under:
| Frequency | Invoice date | Absence cost formula |
|---|---|---|
| Monthly | Before PRORATION_CUTOFF | absence.hours × (cost × 12 / 2080) |
| Monthly | On/after PRORATION_CUTOFF | cost / workdays_in_month per absence row |
| Daily / hourly | Any | 0 — already absorbed into hours_worked (no separate deduction row) |
For monthly contracts under the post-cutoff model, holiday and vacation deductions use the
same formula as absences (with hours = 8 per weekday), so all three deduction
types are consistent within a given invoice month.
Invoice adjustments & passthroughs
Beyond the base computed amount, a ClientContractInvoice can carry adjustments
of two kinds, both stored in ClientContractInvoiceAdjustment with a signed
amount:
- Deductions — emitted by the billing routine for monthly holiday /
vacation deductions when the corresponding flag is
false.amountis negative. - Passthroughs — billable items from the resource side (e.g., a billable
resource.InvoiceAdjustment) mirrored onto the client invoice.amountis whatever the billable_amount was; usually positive.
Both are added into net_amount alongside baseAmount:
net_amount = base_amount + sum(deductions) − sum(absence_costs) + sum(passthroughs)
(Deduction amounts are already negative, so the formula reads correctly.)
Billed absences
Absences are stored on the resource side (resource.Absence). When a client
invoice is generated that includes the absence, two pointers are set:
billed_at— the timestamp of billing.billed_by_invoice_id— theClientContractInvoicethat absorbed it. Prevents the same absence from being billed twice.
Absences with billed_at = NULL are eligible for the next invoice in the period.
The display-time cost shown on a billed absence is recomputed each render via
computeAbsenceCost, not stored, so the cost is always self-consistent with the
invoice date.
Contract end dates
If revised_end is set on a contract, it overrides contract_end for
the intersection calculation. This is how mid-period contract shortenings flow into billing
without losing the original end date.
QB-vs-DB reconciliation note
When matching against QuickBooks invoices, expect occasional small per-month residuals from:
- Bonus / catchall lines — QB sometimes adds line items (e.g. weekend bonuses)
directly to a customer invoice that the DB billing model doesn't generate. The
QB-reconciliation report annotates these as
data_gap. - Same-numbered supplemental QB invoices — see #908. Some QB doc numbers represent supplemental adjustments that don't have a 1:1 DB counterpart; reconciliation pairs by doc_number first then date, and unmatched ones surface as Missing in DB.
- Pre-cutoff calendar-day vs workday proration — partial-month invoices generated under the legacy formula may differ from a regen under the current formula by a few cents to a few dollars per resource per month.
Key definitions
| Workdays | Monday through Friday (excludes weekends) |
| Workdays in month | Varies per month (e.g. Feb = 20, Apr = 22, Jul = 23) |
| Intersection | The overlap between the contract's active period (using revised_end if set) and the invoice month |
| Holiday schedule | Country-specific holiday calendar assigned to each resource contract |
| Absence | An unplanned period the resource did not work, recorded in hours (partial-day allowed) |
| Vacation | A planned period off, drawn from the resource's vacation allotment, also recorded in hours |
PRORATION_CUTOFF | 2026-04-01. Invoice dates < cutoff use the legacy calendar-day formula (monthly only, both flags true). On/after = workday-based for all rate types. |
paid_holidays / paid_vacation | Per-contract flags controlling whether holidays / vacation are baked into the rate (true) or deducted (false). |