Reseller Payouts — Production Guide
Licensing
- Catalog slug:
reseller-payouts - Category:
sales(Sales),category_sort_order = 20, modulesort_order = 80 - Free Marketplace opt-in (
is_default_included = false,is_billable = false, price0) - Hard dependency: Resellers (which itself requires Payments)
- Marketplace blocks install until Resellers is entitled
Bootstrap
- Ensure Resellers (and Payments) are entitled first
- Enable Reseller Payouts from Marketplace
- Permissions via
2026_08_06_100007_add_reseller_payouts_permissions
Without Reseller Payouts entitlement, invoice Paid transitions do not create commission rows (listeners no-op after entitlement check).
Schema migrations
| Migration | Purpose |
|---|---|
2026_08_06_100002_create_reseller_commission_entries_table | ledger + unique (customer_invoice_id, party) |
2026_08_06_100006_register_reseller_payouts_module | catalog via DefaultModuleRegistrar |
2026_08_06_100008_add_reseller_payouts_resellers_dependency | hard dep → resellers |
Event wiring
AppServiceProvider:
CustomerInvoiceBecamePaid→AccrueResellerCommissionCustomerInvoiceLeftPaid→VoidResellerCommission
Transitions are dispatched from CustomerInvoicePaidTransitions after invoice status recalculation (Payments post/void paths included).
Monitoring
- Spatie activity log name:
reseller_commission_entries - Track accrue/approve/pay/void volume via application logs / Nightwatch as needed
Deploy checklist
- Migrate commission table + catalog + permissions + Resellers dependency
- Confirm
module:reseller-payouts+reseller-payouts.*on admin/manager/staff/reseller maps - Confirm listeners registered in deployed app container
- Smoke: Paid invoice with
reseller_id→ two accrued rows → approve → pay; void invoice payment → entries voided; re-post payment → entries revived to accrued - Confirm Partial invoices do not accrue
Finance runbook — invoice leaves Paid
Leaving Paid (CustomerInvoiceLeftPaid, typically from voiding a posted payment) calls voidForInvoice, which bulk-voids every non-void commission row for that invoice — including entries already marked paid.
This is intentional Phase 1 behavior so the ledger cannot stay “settled” against an unpaid invoice:
| Situation | Result |
|---|---|
| Accrued / approved entry, invoice unpays | Status → void |
Manually marked paid entry, invoice unpays | Status → void (finance must treat as cancelled settlement) |
| Invoice becomes Paid again | Void rows for that invoice are revived to accrued with refreshed amount snapshots |
Ops guidance: If commissions were already disbursed outside the app before a payment void, reverse or reclaim that disbursement in your payroll/AP process — EloSync marks the ledger void but does not move money. Manual Void on a single entry still rejects rows that are already paid while the invoice remains Paid.
Deferred
No automated disbursement gateway in Phase 1 — pay is ledger status only. Cross-workspace identity for parties remains deferred.