Happy path: la venta de Tienda Frescura
Una historia con 6 personajes que muestra el módulo de comisiones en acción — desde el primer click en el landing hasta el pago del bono a los 3 meses.
🎭 Cast
| Personaje | Rol | Función |
|---|---|---|
| Tienda Frescura | Cliente (tenant) | Supermercado que paga la suscripción mensual |
| Pablo Ramírez | SDR — Sales Development Rep | Prospecta y trae el lead. Co-participante de la venta |
| María González | AE — Account Executive | Cierra el deal. Participante primaria |
| Carlos Manager | Gerente de ventas | Manager directo de María. Cobra override |
| Diana Director | Directora regional | Manager de Carlos. Cobra override de segundo nivel |
| Sopinf Tech LLC | El MSP | Dueño del producto SipSop. Retiene el resto del revenue |
🌳 Jerarquía
Diana Director (override 5%)
└── Carlos Manager (override 10%)
└── María González (AE, 70% del split)
Pablo Ramírez (SDR, 30% del split) — no tiene manager en este ejemploDiana y Carlos ganan override sobre la comisión que le corresponde a María (no sobre lo de Pablo, porque Pablo no está en su cadena de reportes).
💰 La venta — números reales
Tienda Frescura contrata el plan Business:
| Componente | Precio | Source Stripe |
|---|---|---|
| Base plan | $79.00 | stripe_base_price_id |
| 5 teléfonos × $4.80 | $24.00 | stripe_phone_price_id |
| 1 línea SIP | $12.00 | stripe_line_price_id |
| Overage international | $3.20 | stripe_overage_price_id |
| Total mensual | $118.20 | — |
Términos acordados con los reps al crear la asignación:
- Participantes: María 70% (role: AE) + Pablo 30% (role: SDR)
- % por componente: base 5% · phones 3% · lines 2% · overage 10%
- Bono one-time: $200, vesting 3 meses
- Basis: gross (sin descontar Stripe fees)
⚙️ Configuración en el sistema
commission_reps
María González — email: maria@sopinf.com — managerId: carlos.id
Carlos Manager — email: carlos@sopinf.com — managerId: diana.id — overridePercent: 10
Diana Director — email: diana@sopinf.com — managerId: null — overridePercent: 5
Pablo Ramírez — email: pablo@sopinf.com — managerId: null — overridePercent: 0commission_assignment para Tienda Frescura
tenantId: tienda-frescura.id
recurringPercent: 5 (fallback global — no se usa en modo breakdown)
basis: gross
startsAt: 2026-02-01
oneTimeAmountCents: 20000 ($200)
recurringPercentBase: 5
recurringPercentPhones: 3
recurringPercentLines: 2
recurringPercentOverage: 10
participants:
- María González: sharePercent=70, role='AE'
- Pablo Ramírez: sharePercent=30, role='SDR'🔄 Flujo mensual (meses 1, 2, y 3)
Día de facturación: Stripe cobra a Tienda Frescura
Stripe emite el invoice. El webhook invoice.paid llega al API. El handler descompone el invoice en 4 líneas y crea 4 revenue_events:
revenue_events para Tienda Frescura, período 2026-02:
source: stripe_invoice | component: base | amountUsdCents: 7900
source: stripe_invoice | component: phones | amountUsdCents: 2400
source: stripe_invoice | component: lines | amountUsdCents: 1200
source: stripe_invoice | component: overage | amountUsdCents: 320
Total bruto: $118.20Día 1 del mes siguiente: cron genera statements
El job commissions-monthly corre a las 3am UTC y llama materializeStatementsForPeriod('2026-02').
Cálculo del calculator por componente:
El motor bucketea los revenue events por componente y aplica el % correspondiente:
| Componente | Revenue | % María (70%) | % Pablo (30%) |
|---|---|---|---|
| base | $79.00 | 5% × $79 × 0.70 = $2.77 | 5% × $79 × 0.30 = $1.19 |
| phones | $24.00 | 3% × $24 × 0.70 = $0.50 | 3% × $24 × 0.30 = $0.22 |
| lines | $12.00 | 2% × $12 × 0.70 = $0.17 | 2% × $12 × 0.30 = $0.07 |
| overage | $3.20 | 10% × $3.20 × 0.70 = $0.22 | 10% × $3.20 × 0.30 = $0.10 |
| Total | $118.20 | $3.66 | $1.57 |
Nota: los centavos usan
Math.round(). Puede haber diferencia de ±1¢ por rounding.
Override walk para los line items de María:
Carlos Manager (10% override, manager de María):
$3.66 × 10%= $0.37 (override sobre los line items base de María)
Diana Director (5% override, manager de Carlos):
$3.66 × 5%= $0.18 (override sobre la comisión original de María — sin compounding)
Pablo no tiene manager, así que no genera overrides hacia arriba.
Statements generados (mes 1-3, solo recurring):
| Rep | Tipo | Amount |
|---|---|---|
| María González | recurring split | $3.66 |
| Pablo Ramírez | recurring split | $1.57 |
| Carlos Manager | override sobre María | $0.37 |
| Diana Director | override sobre María | $0.18 |
Sopinf retiene: $118.20 - $3.66 - $1.57 - $0.37 - $0.18 = $112.42
🎁 Mes 4 — vesting del bono one-time
startsAt = 2026-02-01. oneTimeQualificationMonths = 3. El cron del 1ro de mayo de 2026 genera el statement de abril 2026. qualifiedAt = 2026-05-01, que cae en [2026-04-01, 2026-04-30].
El calculator detecta que la asignación calificó para el bono one-time este período. Incluye un line item kind='one_time' por $200 total, prorateado por share:
| Rep | Recurring | One-time | Override one-time | Total mes 4 |
|---|---|---|---|---|
| María González | $3.66 | $140.00 (70%) | — | $143.66 |
| Pablo Ramírez | $1.57 | $60.00 (30%) | — | $61.57 |
| Carlos Manager | $0.37 | $14.00 (10% de $140) | — | $14.37 |
| Diana Director | $0.18 | $7.00 (5% de $140) | — | $7.18 |
Al finalizar el statement del mes 4, el servicio marca commission_assignments.oneTimePaidAt = '2026-04-01'. El bono no vuelve a aparecer en meses futuros.
📅 Timeline completo
Día 0: Pablo detecta oportunidad. Comparte landing.sipsop.net?ref=pablo-ramirez
con Tienda Frescura. (En este ejemplo, Pablo no tiene ref code — María cierra.)
Día 5: María contacta a Tienda Frescura. Convence al dueño de hacer el demo.
Día 12: Tienda Frescura llena el form del landing con ?ref=maria-gonzalez.
Cookie first-touch se establece (90 días).
POST /api/v1/leads → lead.commissionRepId = maría.id
Día 15: Admin aprueba el lead y crea el tenant.
Signup service detecta lead.commissionRepId → crea commission_assignment automáticamente.
Log: action='lead_converted'
Día 15: Admin edita la asignación: agrega Pablo como 30% SDR, ajusta María a 70% AE.
replaceParticipants → log: action='admin_reassigned'
Día 15: Tienda Frescura activa suscripción en Stripe (plan Business).
Día 31: Primer cobro. webhook invoice.paid → 4 revenue_events (base, phones, lines, overage)
Día 60: Segundo cobro. 4 más revenue_events.
Día 90: Tercer cobro. 4 más revenue_events.
Día 1/3: Cron de marzo materializa statements de febrero.
Día 1/4: Cron de abril materializa statements de marzo.
Día 1/5: Cron de mayo materializa statements de abril — incluye bono one-time.
Día 102: María y Pablo abren /seller/dashboard, ven statements de los 4 meses.
Descargan PDFs de sus statements.
Día 110: Admin revisa statements en /admin/commissions/statements.
Finaliza cada uno (oneTimePaidAt queda marcado al finalizar el de mes 4).
Marca como pagado: paymentMethod='ach', paymentReference='ACH-20260510'.Flujo de dinero
flowchart TD
A[Tienda Frescura\n$118.20/mes] -->|Stripe invoice.paid| B[revenue_events\n4 eventos por componente]
B --> C[commission-calculator\ncalculateForPeriod]
C --> D[Statement María\n$3.66 recurring\n+ $140 one-time mes 4]
C --> E[Statement Pablo\n$1.57 recurring\n+ $60 one-time mes 4]
C --> F[Statement Carlos\noverride 10% sobre María\n$0.37 recurring]
C --> G[Statement Diana\noverride 5% sobre María\n$0.18 recurring]
A -->|retiene| H[Sopinf Tech LLC\n$112.42]Statement PDF de María (mes 4)
El PDF generado por renderStatementPdf incluye algo así:
STATEMENT — María González
Período: 2026-04-01 / 2026-04-30
Estado: FINALIZED
TENANT KIND COMPONENTE REVENUE RATE AMOUNT
Tienda Frescura recurring_percent base $79.00 5% $2.77
Tienda Frescura recurring_percent phones $24.00 3% $0.50
Tienda Frescura recurring_percent lines $12.00 2% $0.17
Tienda Frescura recurring_percent overage $3.20 10% $0.22
Tienda Frescura one_time — — — $140.00
SUBTOTAL RECURRING: $3.66
SUBTOTAL ONE-TIME: $140.00
SUBTOTAL OVERRIDE: $0.00
TOTAL: $143.66Statement PDF de Carlos (mes 4)
Carlos ve sus override line items con el rep fuente:
STATEMENT — Carlos Manager
Período: 2026-04-01 / 2026-04-30
Estado: FINALIZED
TENANT KIND SOBRE COMPONENTE RATE AMOUNT
Tienda Frescura override ↗ María González base 10% $0.28
Tienda Frescura override ↗ María González phones 10% $0.05
Tienda Frescura override ↗ María González lines 10% $0.02
Tienda Frescura override ↗ María González overage 10% $0.02
Tienda Frescura override ↗ María González one_time 10% $14.00
SUBTOTAL RECURRING: $0.37
SUBTOTAL ONE-TIME: $0.00
SUBTOTAL OVERRIDE: $14.37
TOTAL: $14.37¿Por qué first-touch? Incentiva a los reps a traer leads tempranos en el ciclo de ventas. El SDR que inicia la conversación recibe crédito aunque el AE cierre el deal semanas después. El split 70/30 entre María y Pablo reconoce ambas contribuciones explícitamente, sin disputas.
Cuidado con dobles invitaciones. Si Pablo también tenía un ref code y Tienda Frescura visitó el landing primero con el link de Pablo, la cookie se habría establecido con
pablo-ramirez. El form enviaría ese ref code. María habría quedado fuera de la atribución automática. Por eso el admin puede hacer override manual víacommissions.leads.setRepantes de la conversión.
Override no duplica la comisión del IC. Carlos recibe $14.37 adicionales — Sopinf paga ese monto extra, no se descuenta de lo de María. El override es comisión adicional del MSP al manager, no un split del IC.