Inspector-level commission rate ignored during payroll calculation — inspector receives org-default rate instead

Objective

  • Inspectors configured with a custom commissionRate in the Base Pay section of their inspector profile should have their pay calculated against that individual rate, not the org-level default.
  • Resolving this ensures payroll output accurately reflects per-inspector compensation agreements and eliminates the need for manual adjustments or workarounds to correct the difference.

Background

  • Inspectors can be assigned a custom commission percentage via the Base Pay section of their profile (financials.commissionRate on inspectorSchema.ts). This is intended to override the default org-level commission rate for that inspector.
  • During payroll calculation, inspectorPay on each charge is computed in src/util/functions/payroll/dataFunc.ts (and its legacy predecessor src/util/dataFunc.js). The commission calculation path reads a commissionRate — but the inspector's individual rate is not being correctly picked up, causing the system to fall back to the default flat rate applied to other team members.
  • lineItemsFromContext.ts builds the final per-employee line items from the precomputed PayrollContext, reading charge.inspectorPay directly. If inspectorPay was computed incorrectly upstream in dataFunc.ts, the wrong value flows through unchanged into the payroll output.
  • The feeBreakdown.commissionRate field on PayrollFeeBreakdown is intended to capture which rate was actually applied. This can be used to verify whether the correct per-inspector rate is being read and applied, or whether the system is silently falling back.
  • There is no frontend recalculation path — PayrollOverview.tsx is display-only and renders whatever line items the backend produced. The fix must happen in the backend computation layer.
  • The affected inspector (David Montano, Top Choice instance) has worked around this by setting up service-level custom bonuses at 40% to approximate correct pay. This means his commission is currently appearing as "bonus" in payroll output rather than "commission," which may affect reporting and UKG export accuracy. This workaround will need to be cleaned up once the proper fix is shipped.

Product Decisions

Locked

  1. Rate source — The inspector's financials.commissionRate (set in the Base Pay section of the inspector profile) is the authoritative rate and must take precedence over the service-level commission rate when present.
  2. Fallback behavior — When an inspector's commissionRate is null or unset, the commission rate defined on the service should be used as the fallback.
  3. Priority chain — The correct priority order is: inspector commissionRate → service-level modifier → service base commission rate. The current behavior skips the inspector rate entirely when a service-level modifier is present.
  4. No manual workaround — The adjustments column in the payroll table is not an acceptable resolution path. The computation should produce the correct value automatically.
  5. Historical payroll — This is a forward-fix only. Prior pay periods will not be retroactively recalculated.

Scope

Backend

  • src/util/functions/payroll/dataFunc.ts — Primary location where inspectorPay is computed per charge and where commissionRate should be read from the inspector record. Most likely location of the defect.
  • src/util/dataFunc.js — Legacy version of the same logic; may need a parallel fix depending on which code path is active for this instance.
  • src/util/functions/payroll/payrollFeeBreakdown.tsfeeBreakdown.commissionRate should reflect the rate that was actually applied. Useful for verification and debugging.
  • src/models/inspectorSchema.ts — Confirms financials.commissionRate as the per-inspector rate field.
  • src/util/functions/payroll/lineItemsFromContext.ts — Reads charge.inspectorPay to build line items; no change expected here, but the right place to verify the corrected value flows through correctly end-to-end.

Frontend

  • No changes anticipated. src/app/tools/hr/payroll/PayrollOverview.tsx and src/app/tools/hr/payroll/CommissionPayrollList.tsx display computed values only.

References

  • Reported by Neil Chini (Top Choice instance) — inspector David Montano, commissionRate set to 40% in Base Pay
  • src/util/functions/payroll/payrollContext.ts — Builds inspectorById map passed to dataFunc; confirms the inspector record (including financials) is available during computation

Update — June 24, 2026

Neil Chini provided additional specificity during Office Hours on June 23:

  • The override is caused by a service-level pay modifier, not just the service-level commission rate. These are distinct: the base commission rate on a service and a modifier applied on top of it are separate fields. The modifier is what was superseding David Montano's 40% per-inspector commission rate.
  • Specifically: every service on the Top Choice instance has a flat-rate modifier (e.g. $50 for a home inspection, increasing with square footage). That modifier was taking precedence over the inspector's profile-level commission percentage entirely.
  • The workaround Neil landed on was creating custom bonuses per service at 40% in David's inspector profile, which superseded the service modifier. This confirms the priority order is at least partially broken: service modifier > inspector commission rate, when it should be inspector commission rate > service modifier > service base rate.
  • Neil intends to run an A/B test across the last two pay periods to compare pay models before potentially moving this inspector to a legacy pay plan.
  • Locked Decision #3 above reflects this clarification: the full priority chain must account for the service modifier as a distinct tier between the inspector rate and the service base rate.

Please authenticate to join the conversation.

Upvoters
Status

Planned

Board
🏠

Main App

Date

2 days ago

Author

Linear

Subscribe to post

Get notified by email when there are changes.