# LEDGER — ISS-2026-9122: sales reps + commission (split/targets/reports/role)

**Date:** 2026-07-17 · hazemdev2 both repos · scope frozen (client took all 4 recs).

## Frozen decisions
- Commission = TRACKING only (no GL) — like today.
- Collection portion accrued PROPORTIONALLY as each payment posts; REVERSED (clawed back) on returns/credit notes.
- Target: MONTHLY period; basis = COLLECTED cash; if target not met → zero the COLLECTION portion only (creation portion stays).
- Mobile = responsive web + a scoped "Sales Rep" role (rep sees only own data). (Client noted may not use mobile.)

## Current state (recon, ISS-9122 research)
- salesperson_id EXISTS on invoices/orders/quotations (→ users). Commission engine EXISTS: sales_commission_rules + sales_commissions, computed once at posting (CommissionService::calculateForInvoice, PostSalesInvoice:108), tracking-only, status pending→approved→paid, summary report. Collection data EXISTS (SalesPayment, amount_paid/balance_due) but commission ignores it. Reports: by-salesperson + overdue-aging (aging filters by partner not rep) + funnel (accepts salesperson). Quotation→invoice: FE stub POSTs /sales/quotations/{id}/convert-to-invoice → 404 (BE missing). No sales-rep role, no row-level scoping.

## WPs
| WP | Scope | Repo | Depends | Status |
|----|-------|------|---------|--------|
| WP1 | ✅ e6144574c (36+21 tests, no migration) · Quotation→DRAFT-invoice: BE endpoint POST sales/quotations/{id}/convert-to-invoice (creates draft invoice w/ quotation salesperson+lines, marks quotation converted) — fixes existing FE 404 stub. | BE | — | ⬜ |
| WP2 | ✅ 8ae390e1c + fix 507dda9cb (29 tests); [FIN] review WARNING→resolved: HIGH=intended return-claw-back (tested), MEDIUM lock+unique index, LOW exact-settle · [FIN-track] Commission split: extend sales_commission_rules/commissions for create% vs collect%; create-accrual at post (exists); collect-accrual proportional on SalesPayment post; reversal on returns. | BE | — | ⬜ |
| WP3 | ✅ 50479a998 (7+29 tests, non-destructive gate, no-target identity) · [FIN-track] Targets: sales_rep_targets (rep+period(month)+basis=collected+threshold); gate the COLLECT portion on attainment (miss → collect=0). | BE | WP2 | ⬜ |
| WP4 | ✅ BE 53242d04e / FE 565373563 (24 tests, ng build green) · Rep reports: add salesperson_id to aging; combined per-rep report (their invoices + receivables aging + achievement vs target). BE endpoints + FE screen. | BE+FE | WP2,WP3 | ⬜ |
| WP5 | ✅ 427d31ff1 + security-fix f9af33603; review found C1 CRITICAL+H1/H2/M1/M2/M3/L2 → ALL FIXED (own-guard on closeShort/createRemainingIssue/createFrom*/update-force-salesperson + deny own-scoped on 9 company-wide reports + commission-approve hardening); 25 scoping tests + full Sales 146/1-pre-existing · "Sales Rep" role + row-level scoping (rep sees only own quotations/invoices/commissions/reports) via policy/query scoping; responsive-web verify. | BE+FE | — | ⬜ |

## Serialization
WP1 (BE, independent) + WP5 (BE role) can go early. WP2→WP3 BE-serial (commission tables). WP4 after WP2/WP3. One writer per repo.

## Notes
Big feature. [FIN]-adjacent (commission money, tracking-only). code-reviewer per commission WP. Update ISS-9122 ticket when done. hazemdev2, no main/deploy-to-client (owner /fullpush), deploy /app for testing.
