Commission Ledger Setup
Configure salesperson commission plans, project splits, cash-basis posting, adjustments, and historical backfills.
The commission ledger records salesperson commission when customer payments are completed. It replaces report-only commission calculations with permanent, auditable entries.
The system supports:
- A commission configuration for each salesperson.
- Percentage-based commission splits between multiple salespeople on a project.
- Cash-basis commission posting as each payment is received.
- Positive and negative adjustments with a required reason.
- An idempotent backfill for completed historical payments.
How Commission Is Calculated
Commission posts when a project payment reaches Completed.
For each payment, Tempr:
- Calculates the commissionable portion of the payment.
- Divides that amount using the project's salesperson splits.
- Resolves each salesperson's commission configuration.
- Creates one protected earned ledger entry per salesperson.
The commissionable payment amount is proportional to the project's discounted subtotal:
payment share = payment amount / project grand total
commissionable payment = discounted subtotal * payment share
salesperson base = commissionable payment * project split percentage
commission earned = salesperson base * salesperson commission rate
This keeps commission on a cash basis while excluding the non-commissionable portion of the project total, such as sales tax.
The ledger entry's effective date uses the first available value in this order:
- Payment settlement date.
- Payment completion timestamp.
- Payment creation timestamp.
The split percentage and commission rate are stored on the ledger entry when it posts. Changing a user configuration later affects future payments only; it does not recalculate existing entries.
Available User Configurations
Each user has a commission_configuration value.
| Configuration | Stored value | Calculation |
|---|---|---|
| Non-commissioned | non_commissioned | Pays no commission. |
| Flat 3% | flat_three_percent | Always pays 3% of the user's commissionable share. |
| Flat 5.5% | flat_five_and_half_percent | Always pays 5.5% of the user's commissionable share. |
New users default to non_commissioned. Assign a flat commission configuration only when the user should earn commission.
Initial Deployment
1. Run The Migrations
php artisan migrate
The migrations add:
users.commission_configurationproject_commission_splitscommission_ledger_entries
No additional environment variables are required.
2. Confirm Queue Processing
Automatic commission posting is handled by the queued PostCommissionForCompletedPayment listener. Confirm that the environment's queue worker or Horizon process is running and processing the configured default queue.
php artisan horizon:status
If the application does not use Horizon in that environment, confirm its normal Laravel queue worker is running instead.
A completed payment can dispatch the listener successfully while commission remains unposted if no queue worker processes the job. Monitor failed jobs and queue health after deployment.
3. Assign User Configurations
In the Config panel:
- Open
/config. - Select Users.
- Create or edit the salesperson.
- Select a Commission Configuration.
- Save the user.
The selected configuration also appears in the Users table under Commission.
The same assignment can be made in application code:
use App\Enums\Commissions\CommissionConfiguration;
$user->assignCommissionConfiguration(
CommissionConfiguration::FlatFiveAndHalfPercent,
);
4. Configure Project Splits
In the main project profile:
- Open the project and select Products.
- Open the Actions menu.
- Select Commission splits.
- Add each salesperson and their project share.
- Confirm the percentages total exactly 100%.
- Save the splits.
If a project has no explicit splits, commission defaults to 100% for the project's consultant. If the project does not have a consultant, Tempr falls back to the customer's consultant.
Project splits lock after the first payment-backed commission entry posts. Configure historical splits before running the backfill. Corrections after posting must be entered as adjustments.
Backfilling Existing Payments
The backfill posts ledger entries for completed payments that do not already have commission entries.
Before running it:
- Assign the correct commission configuration to every salesperson.
- Add historical project splits where commission should not go 100% to the project consultant.
- Confirm every included project has either valid splits or an assigned project/customer consultant.
- Confirm the queue is paused or operational procedures prevent concurrent manual changes to the same commission setup.
Backfill all eligible completed payments:
php artisan commissions:backfill
Limit the backfill by settlement date:
php artisan commissions:backfill --from=2026-01-01 --to=2026-12-31
Both date options are inclusive. Either option can be used by itself.
The command is idempotent: payments with existing commission ledger entries are skipped, and posting the same payment again returns its existing entries. If a backfill stops because one project is not configured, correct that project and rerun the same command.
Do not use a configuration change to correct earned commission that has already posted. Earned entries cannot be edited, so post an adjustment instead.
Reviewing The Ledger
In the Reports panel:
- Open
/reportal. - Under Sales, select Commission Ledger.
- Filter by salesperson or entry type as needed.
Earned entries show the payment, project, commissionable amount, split percentage, snapshotted rate, and commission amount. Adjustment entries show their signed amount and reason.
The commission report also reads from these ledger entries and groups them by salesperson and effective date.
Posting Adjustments
Use an adjustment for salesperson errors, chargebacks, bonuses, or any correction to commission that has already posted.
In the Manage panel:
- Open
/manage. - Under Sales, select Commission Ledger.
- Select Add adjustment.
Enter:
- The salesperson.
- A signed dollar amount.
- The effective date.
- An optional related project.
- A required reason.
Use a negative amount to reduce commission and a positive amount to increase it.
Examples:
| Scenario | Adjustment |
|---|---|
| $125.50 chargeback for a measurement error | -125.50 |
| $200 discretionary bonus | 200.00 |
Manual adjustments can be edited from the same Manage-panel ledger if their salesperson, amount, effective date, project, or reason needs correction. Payment-earned entries cannot be edited, and no ledger entries can be deleted.
Operational Rules
- Only
Completedpayments generate earned commission. - Non-project payments do not generate commission entries.
- Each payment can create at most one earned entry for each salesperson.
- Project splits must contain unique users and total exactly 100%.
- Deleted users remain visible on historical ledger entries.
- Deleting a project or payment does not delete its historical commission amount; the related reference becomes null where allowed.
- Commission adjustments affect commission totals but do not change a salesperson's configured percentage.
Adding A Future Configuration
Commission formulas are separated behind App\Contracts\Commissions\CommissionRateConfiguration.
To add another variation:
- Create a rate configuration class that implements
rateForCommissionableAmount(int $commissionableAmount): float. - Add a backed case to
App\Enums\Commissions\CommissionConfiguration. - Add its user-facing label in
getLabel(). - Resolve the new strategy from
rateConfiguration(). - Add tests for the rate boundaries and mixed project splits.
The enum automatically supplies options to the user administration selector, so a new case does not require a database schema change.
For example, a future configuration could support a flat rate with a threshold, a project-type-specific rate, or another formula while retaining the same ledger and user assignment workflow.
Troubleshooting
A Completed Payment Has No Ledger Entry
Check:
- The queue worker or Horizon is running.
- The listener job is not in the failed jobs table.
- The payment belongs to a project.
- The project has valid splits or an assigned consultant.
- The payment state is actually
Completed.
After correcting the setup, retry the failed job or run the date-scoped backfill.
The Commission Splits Action Is Disabled
The action requires project financial access and project update access. If it is visible but disabled, either the user cannot update the project or at least one payment-backed commission entry has already posted. Posted splits are intentionally locked; use a signed adjustment to correct the salesperson's balance.
A User's New Rate Did Not Change An Old Entry
This is expected. The ledger snapshots the rate at posting time, and earned entries cannot be edited. The new configuration applies to future completed payments only.
The Backfill Stops On A Project
The project likely has no usable salesperson or its explicit splits do not total 100%. Correct the project setup, then rerun the command. Already-posted payments will be skipped.