stepolan commited on
Commit
21b7b35
Β·
1 Parent(s): 928cf68

Add rich plan document format with deal types, targets, illustrative scenarios

Browse files

Supports co-founder's detailed comp plan view with:
- Deal type table (Enterprise/SMB with different rates)
- Annual quota built from deal targets
- Grouped ramp schedule with monthly targets and notes
- Accelerator tiers
- Illustrative earnings scenarios by deal mix
- Full policy terms
- Signature block

Files changed (1) hide show
  1. app.py +206 -82
app.py CHANGED
@@ -638,127 +638,251 @@ if active_tab == "compare":
638
 
639
  # Build agreement text
640
  lines = []
641
- lines.append("SALES COMPENSATION PLAN SUMMARY")
642
- lines.append("=" * 40)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
643
  lines.append("")
 
 
644
  lines.append("1. COMPENSATION OVERVIEW")
645
  lines.append("")
646
- lines.append(f" Base Salary: ${p.get('base_salary', 0):,.0f} per year, paid semi-monthly")
647
- lines.append(f" Annual Quota: ${p.get('annual_quota', 0):,.0f} in Net Revenue")
 
 
648
 
649
- # Draw
650
- draw_amt = p.get("draw_amount", 0)
651
  if draw_amt > 0:
652
  draw_mo = p.get("draw_months", 0)
653
  rec_label = "recoverable" if p.get("draw_recoverable", False) else "non-recoverable"
654
  monthly_draw = draw_amt / max(draw_mo, 1)
655
- lines.append(f" Signing Draw: ${draw_amt:,.0f} ({rec_label})")
656
- lines.append(f" Paid in equal installments over {draw_mo} months (~${monthly_draw:,.0f}/mo)")
657
- else:
658
- lines.append(" Signing Draw: None")
659
-
660
- lines.append("")
661
-
662
- # Ramp
663
- ramp_list = p.get("ramp_pcts", [])
664
- if ramp_list:
665
- lines.append("2. RAMP-UP PERIOD")
666
- lines.append("")
667
- lines.append(f" The initial ramp period shall be the first {len(ramp_list)} months of employment.")
668
- lines.append(f" During ramp, quota targets are reduced as follows:")
669
- lines.append("")
670
- lines.append(f" {'Month':<12} {'Quota %':<12}")
671
- lines.append(f" {'-' * 10:<12} {'-' * 10:<12}")
672
- for i, pct in enumerate(ramp_list):
673
- lines.append(f" Month {i+1:<5} {int(pct)}%")
674
- lines.append(f" Month {len(ramp_list)+1}+{'':<3} 100%")
675
  lines.append("")
676
- lines.append(f" Effective Annual Quota (ramp-adjusted): ${r.get('effective_quota', 0):,.0f}")
677
- else:
678
- lines.append("2. RAMP-UP PERIOD")
 
 
 
679
  lines.append("")
680
- lines.append(" No ramp period. Full quota applies from Month 1.")
681
 
 
 
 
682
  lines.append("")
683
 
684
- # Commission structure
685
- lines.append("3. COMMISSION STRUCTURE")
686
  lines.append("")
687
 
688
  comm_tiers = p.get("commission_tiers")
689
- deal_tiers = p.get("deal_rate_tiers")
690
  accel_t = p.get("accel_tiers")
691
 
692
- if comm_tiers:
693
- lines.append(" Commission rates are applied on a marginal basis. Each rate applies")
694
- lines.append(" only to revenue earned within that attainment band.")
 
 
 
 
 
 
 
 
695
  lines.append("")
696
- lines.append(f" {'Attainment Level':<24} {'Commission Rate':<18}")
697
- lines.append(f" {'-' * 22:<24} {'-' * 16:<18}")
 
 
 
 
 
 
 
698
  for i, tier in enumerate(comm_tiers):
699
  upper, rate = tier[0], tier[1]
700
  if upper is None:
701
  prev_upper = int(comm_tiers[i-1][0])
702
- lines.append(f" {prev_upper}%+{'':<19} {rate:.1f}%")
703
  elif i == 0:
704
- lines.append(f" 0% – {int(upper)}%{'':<14} {rate:.1f}%")
705
  else:
706
  prev_upper = int(comm_tiers[i-1][0])
707
- lines.append(f" {prev_upper}% – {int(upper)}%{'':<12} {rate:.1f}%")
708
- elif deal_tiers:
709
- lines.append(" Commission is calculated as a percentage of deal ACV based on deal size.")
710
  lines.append("")
711
- lines.append(f" {'Deal ACV Range':<28} {'Commission Rate':<18}")
712
- lines.append(f" {'-' * 26:<28} {'-' * 16:<18}")
713
- for tier in deal_tiers:
714
  mn, mx, rt = tier[0], tier[1], tier[2]
715
  if mx is None:
716
- lines.append(f" ${mn:,.0f}+{'':<19} {rt:.1f}%")
717
  else:
718
- lines.append(f" ${mn:,.0f} – ${mx:,.0f}{'':<8} {rt:.1f}%")
719
-
720
- if accel_t:
721
- lines.append("")
722
- lines.append(" Accelerators apply based on annual quota attainment:")
723
- lines.append("")
724
- lines.append(f" {'Attainment Level':<24} {'Multiplier':<18}")
725
- lines.append(f" {'-' * 22:<24} {'-' * 16:<18}")
726
- for i, tier in enumerate(accel_t):
727
- upper, mult = tier[0], tier[1]
728
- if upper is None:
729
- prev_upper = int(accel_t[i-1][0])
730
- lines.append(f" {prev_upper}%+{'':<19} {mult:.1f}x")
731
- elif i == 0:
732
- lines.append(f" 0% – {int(upper)}%{'':<14} {mult:.1f}x")
733
- else:
734
- prev_upper = int(accel_t[i-1][0])
735
- lines.append(f" {prev_upper}% – {int(upper)}%{'':<12} {mult:.1f}x")
736
 
737
  lines.append("")
738
- lines.append(" Commissions are uncapped at this time.")
739
- lines.append("")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
740
 
741
  # Illustrative earnings
742
- lines.append("4. ILLUSTRATIVE EARNINGS")
743
- lines.append("")
744
- lines.append(f" At {r.get('attainment_pct', 0):.0f}% quota attainment (${inp.get('actual_revenue', 0):,.0f} revenue):")
745
  lines.append("")
746
- lines.append(f" Base Salary: ${p.get('base_salary', 0):,.0f}")
747
- if draw_amt > 0:
748
- lines.append(f" Draw: ${r.get('draw_paid', 0):,.0f}")
749
- lines.append(f" Commission: ${r.get('commission', 0):,.0f}")
750
- lines.append(f" Total Compensation: ${r.get('total_comp', 0):,.0f}")
 
 
 
 
 
 
 
 
751
  lines.append("")
 
752
 
753
  # Policies
754
- lines.append("5. KEY POLICIES")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
755
  lines.append("")
756
- lines.append(" - Commissions are earned upon fully-executed contract and recognized revenue.")
757
- lines.append(" - The Company reserves the right to adjust quotas based on market conditions,")
758
- lines.append(" territory changes, product availability, and strategic priorities.")
759
- lines.append(" - The Company reserves the right to modify or terminate this plan at any time.")
760
- lines.append(" - All determinations will be made in good faith and in compliance with")
761
- lines.append(" applicable law.")
762
 
763
  agreement_text = "\n".join(lines)
764
 
 
638
 
639
  # Build agreement text
640
  lines = []
641
+ deal_types = p.get("deal_types")
642
+ illustrative = p.get("illustrative_scenarios")
643
+ policies = p.get("policies")
644
+ equity = p.get("equity")
645
+
646
+ lines.append("SALES COMPENSATION PLAN")
647
+ lines.append("=" * 60)
648
+ lines.append("")
649
+
650
+ # Header summary
651
+ lines.append(f" ${p.get('base_salary', 0):,.0f}".ljust(22) + f"~${r.get('total_comp', 0):,.0f}".ljust(22))
652
+ lines.append(" Base Salary".ljust(22) + "OTE".ljust(22))
653
+ draw_amt = p.get("draw_amount", 0)
654
+ if draw_amt > 0 or equity:
655
+ line_l = f" ${draw_amt:,.0f}" if draw_amt > 0 else ""
656
+ line_r = f" {equity.get('pct', '')}%" if equity else ""
657
+ lines.append(line_l.ljust(22) + line_r.ljust(22))
658
+ line_l2 = f" Draw ({p.get('draw_months', 0)} mo.)" if draw_amt > 0 else ""
659
+ line_r2 = f" Equity (Options)" if equity else ""
660
+ lines.append(line_l2.ljust(22) + line_r2.ljust(22))
661
  lines.append("")
662
+
663
+ # 1. Compensation Overview
664
  lines.append("1. COMPENSATION OVERVIEW")
665
  lines.append("")
666
+ lines.append(" Base Salary")
667
+ lines.append(f" β€’ ${p.get('base_salary', 0):,.0f} / year paid semi-monthly")
668
+ lines.append(f" β€’ Full benefits eligibility from day 1")
669
+ lines.append("")
670
 
 
 
671
  if draw_amt > 0:
672
  draw_mo = p.get("draw_months", 0)
673
  rec_label = "recoverable" if p.get("draw_recoverable", False) else "non-recoverable"
674
  monthly_draw = draw_amt / max(draw_mo, 1)
675
+ lines.append(" Signing Draw")
676
+ lines.append(f" β€’ ${draw_amt:,.0f} {rec_label} draw")
677
+ lines.append(f" β€’ Paid over {draw_mo} months in equal installments ~${monthly_draw:,.0f}/mo.")
678
+ if not p.get("draw_recoverable", False):
679
+ lines.append(f" β€’ Not subject to clawback β€” protects ramp period")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
680
  lines.append("")
681
+
682
+ if equity:
683
+ lines.append(" Equity Grant")
684
+ lines.append(f" β€’ {equity.get('pct', '')}% of company in stock options")
685
+ lines.append(f" β€’ {equity.get('vesting', '4-year vest with 1-year cliff')}")
686
+ lines.append(f" β€’ Strike price = FMV at grant date")
687
  lines.append("")
 
688
 
689
+ lines.append(" On-Target Earnings (OTE)")
690
+ lines.append(f" β€’ ${p.get('base_salary', 0):,.0f} base + ~${r.get('commission', 0):,.0f} variable = ~${r.get('total_comp', 0):,.0f} OTE")
691
+ lines.append(f" β€’ Uncapped β€” no ceiling on commissions")
692
  lines.append("")
693
 
694
+ # 2. Commission Structure
695
+ lines.append("2. COMMISSION STRUCTURE")
696
  lines.append("")
697
 
698
  comm_tiers = p.get("commission_tiers")
699
+ deal_tiers_data = p.get("deal_rate_tiers")
700
  accel_t = p.get("accel_tiers")
701
 
702
+ if deal_types:
703
+ lines.append(" Commission is paid on all closed-won deals. Rates vary by deal type:")
704
+ lines.append("")
705
+ lines.append(f" {'Deal Type':<14} {'ACV Range':<22} {'Rate':<10} {'Per-Deal Range':<20} {'Notes'}")
706
+ lines.append(f" {'-'*13:<14} {'-'*20:<22} {'-'*8:<10} {'-'*18:<20} {'-'*20}")
707
+ for dt in deal_types:
708
+ acv_range = f"${dt['acv_min']:,.0f} – ${dt['acv_max']:,.0f}" if dt.get('acv_max') else f"${dt['acv_min']:,.0f}+"
709
+ payout_lo = dt['acv_min'] * dt['rate'] / 100
710
+ payout_hi = (dt.get('acv_max', dt['acv_min']) * dt['rate'] / 100) if dt.get('acv_max') else 0
711
+ payout_range = f"${payout_lo:,.0f} – ${payout_hi:,.0f}" if payout_hi else f"${payout_lo:,.0f}+"
712
+ lines.append(f" {dt['name']:<14} {acv_range:<22} {dt['rate']:.0f}%{'':<7} {payout_range:<20} {dt.get('notes', '')}")
713
  lines.append("")
714
+ lines.append(" Timing: Commissions earned upon fully-executed contract and first invoice")
715
+ lines.append(" issued. Paid on the first payroll cycle of the following month.")
716
+ lines.append(" Churn within 90 days triggers a proportional clawback.")
717
+ elif comm_tiers:
718
+ lines.append(" Commission rates are applied on a marginal basis. Each rate applies")
719
+ lines.append(" only to revenue earned within that attainment band.")
720
+ lines.append("")
721
+ lines.append(f" {'Attainment Level':<24} {'Commission Rate':<18}")
722
+ lines.append(f" {'-' * 22:<24} {'-' * 16:<18}")
723
  for i, tier in enumerate(comm_tiers):
724
  upper, rate = tier[0], tier[1]
725
  if upper is None:
726
  prev_upper = int(comm_tiers[i-1][0])
727
+ lines.append(f" {prev_upper}%+{'':<19} {rate:.1f}%")
728
  elif i == 0:
729
+ lines.append(f" 0% – {int(upper)}%{'':<14} {rate:.1f}%")
730
  else:
731
  prev_upper = int(comm_tiers[i-1][0])
732
+ lines.append(f" {prev_upper}% – {int(upper)}%{'':<12} {rate:.1f}%")
733
+ elif deal_tiers_data:
734
+ lines.append(" Commission is calculated as a percentage of deal ACV based on deal size.")
735
  lines.append("")
736
+ lines.append(f" {'Deal ACV Range':<28} {'Commission Rate':<18}")
737
+ lines.append(f" {'-' * 26:<28} {'-' * 16:<18}")
738
+ for tier in deal_tiers_data:
739
  mn, mx, rt = tier[0], tier[1], tier[2]
740
  if mx is None:
741
+ lines.append(f" ${mn:,.0f}+{'':<19} {rt:.1f}%")
742
  else:
743
+ lines.append(f" ${mn:,.0f} – ${mx:,.0f}{'':<8} {rt:.1f}%")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
744
 
745
  lines.append("")
746
+
747
+ # 3. Annual Quota & Deal Targets
748
+ deal_targets = p.get("deal_targets")
749
+ if deal_targets:
750
+ lines.append("3. ANNUAL QUOTA & DEAL TARGETS")
751
+ lines.append("")
752
+ lines.append(f" On-target quota is ${p.get('annual_quota', 0):,.0f} ACV.")
753
+ lines.append("")
754
+ lines.append(f" {'Segment':<14} {'# Deals':<10} {'Avg ACV':<14} {'Total ACV':<14} {'Rate':<8} {'Commission'}")
755
+ lines.append(f" {'-'*13:<14} {'-'*8:<10} {'-'*12:<14} {'-'*12:<14} {'-'*6:<8} {'-'*12}")
756
+ total_deals = 0
757
+ total_acv = 0
758
+ total_comm = 0
759
+ for dt in deal_targets:
760
+ t_acv = dt['num_deals'] * dt['avg_acv']
761
+ t_comm = t_acv * dt['rate'] / 100
762
+ total_deals += dt['num_deals']
763
+ total_acv += t_acv
764
+ total_comm += t_comm
765
+ lines.append(f" {dt['segment']:<14} {dt['num_deals']:<10} ${dt['avg_acv']:,.0f}{'':<6} ${t_acv:,.0f}{'':<6} {dt['rate']:.0f}%{'':<5} ${t_comm:,.0f}")
766
+ lines.append(f" {'Total':<14} {total_deals:<10} {'β€”':<14} ${total_acv:,.0f}{'':<6} {'β€”':<8} ~${total_comm:,.0f}")
767
+ lines.append("")
768
+ section_num = 4
769
+ else:
770
+ section_num = 3
771
+
772
+ # Ramp
773
+ ramp_list = p.get("ramp_pcts", [])
774
+ if ramp_list:
775
+ lines.append(f"{section_num}. QUOTA RAMP SCHEDULE")
776
+ lines.append("")
777
+ lines.append(f" Commission rates remain constant throughout the ramp. Only the quota")
778
+ lines.append(f" hurdle changes, giving time to build pipeline without sacrificing")
779
+ lines.append(f" earnings on early deals closed.")
780
+ lines.append("")
781
+ monthly_quota = p.get('annual_quota', 0) / 12
782
+ lines.append(f" {'Month':<10} {'Quota %':<12} {'Monthly Target':<18} {'Ann. Run Rate':<16} {'Notes'}")
783
+ lines.append(f" {'-'*8:<10} {'-'*10:<12} {'-'*16:<18} {'-'*14:<16} {'-'*20}")
784
+ # Group consecutive months with same pct
785
+ ramp_groups = []
786
+ i = 0
787
+ while i < len(ramp_list):
788
+ pct = ramp_list[i]
789
+ start = i + 1
790
+ while i + 1 < len(ramp_list) and ramp_list[i + 1] == pct:
791
+ i += 1
792
+ end = i + 1
793
+ ramp_groups.append((start, end, pct))
794
+ i += 1
795
+ ramp_notes = {0: "Onboarding, no quota", 25: "First demos, early pipeline",
796
+ 50: "Close first deals", 75: "Full funnel operating", 100: "Full quota in effect"}
797
+ for start, end, pct in ramp_groups:
798
+ month_label = f"{start}–{end}" if start != end else str(start)
799
+ target = monthly_quota * pct / 100
800
+ run_rate = target * 12
801
+ note = ramp_notes.get(int(pct), "")
802
+ if draw_amt > 0:
803
+ monthly_draw_val = draw_amt / max(p.get('draw_months', 1), 1)
804
+ lines.append(f" {month_label:<10} {int(pct)}%{'':<9} ${target:,.0f}{'':<8} ${run_rate:,.0f}{'':<6} {note}")
805
+ else:
806
+ lines.append(f" {month_label:<10} {int(pct)}%{'':<9} ${target:,.0f}{'':<8} ${run_rate:,.0f}{'':<6} {note}")
807
+ # Full quota months
808
+ if len(ramp_list) < 12:
809
+ month_label = f"{len(ramp_list)+1}–12"
810
+ lines.append(f" {month_label:<10} 100%{'':<8} ${monthly_quota:,.0f}{'':<8} ${monthly_quota*12:,.0f}{'':<6} Full quota in effect")
811
+ lines.append("")
812
+ section_num += 1
813
+
814
+ # Accelerators
815
+ if accel_t:
816
+ lines.append(f"{section_num}. ACCELERATORS & OVERACHIEVEMENT")
817
+ lines.append("")
818
+ lines.append(f" {'Attainment':<18} {'Rate / Multiplier':<22} {'Notes'}")
819
+ lines.append(f" {'-'*16:<18} {'-'*20:<22} {'-'*20}")
820
+ for i, tier in enumerate(accel_t):
821
+ upper, mult = tier[0], tier[1]
822
+ if upper is None:
823
+ prev_upper = int(accel_t[i-1][0])
824
+ lines.append(f" {prev_upper}%+{'':<14} {mult:.1f}x multiplier")
825
+ elif i == 0:
826
+ lines.append(f" 0% – {int(upper)}%{'':<9} {mult:.1f}x (base rate)")
827
+ else:
828
+ prev_upper = int(accel_t[i-1][0])
829
+ lines.append(f" {prev_upper}% – {int(upper)}%{'':<7} {mult:.1f}x multiplier")
830
+ lines.append("")
831
+ section_num += 1
832
+ elif comm_tiers:
833
+ # Attainment-based tiers already shown above, no separate accelerator section needed
834
+ pass
835
 
836
  # Illustrative earnings
837
+ lines.append(f"{section_num}. ILLUSTRATIVE EARNINGS SCENARIOS")
 
 
838
  lines.append("")
839
+ if illustrative:
840
+ lines.append(f" {'Scenario':<22} {'Deal Mix':<20} {'Total ACV':<14} {'Commission':<14} {'Total Cash':<14} {'Notes'}")
841
+ lines.append(f" {'-'*20:<22} {'-'*18:<20} {'-'*12:<14} {'-'*12:<14} {'-'*12:<14} {'-'*20}")
842
+ for sc in illustrative:
843
+ lines.append(f" {sc['label']:<22} {sc.get('deal_mix', 'β€”'):<20} ${sc['acv']:,.0f}{'':<4} ${sc['commission']:,.0f}{'':<4} ${sc['total_cash']:,.0f}{'':<4} {sc.get('notes', '')}")
844
+ else:
845
+ lines.append(f" At {r.get('attainment_pct', 0):.0f}% quota attainment (${inp.get('actual_revenue', 0):,.0f} revenue):")
846
+ lines.append("")
847
+ lines.append(f" Base Salary: ${p.get('base_salary', 0):,.0f}")
848
+ if draw_amt > 0:
849
+ lines.append(f" Draw: ${r.get('draw_paid', 0):,.0f}")
850
+ lines.append(f" Commission: ${r.get('commission', 0):,.0f}")
851
+ lines.append(f" Total Compensation: ${r.get('total_comp', 0):,.0f}")
852
  lines.append("")
853
+ section_num += 1
854
 
855
  # Policies
856
+ lines.append(f"{section_num}. KEY POLICIES & TERMS")
857
+ lines.append("")
858
+ if policies:
859
+ for key, val in policies.items():
860
+ lines.append(f" {key:<24} {val}")
861
+ else:
862
+ lines.append(" Commission Earned Upon fully-executed contract and recognized revenue.")
863
+ lines.append(" Commission Paid First payroll cycle following the month of close.")
864
+ lines.append(" Clawback Proportional if customer churns within 90 days.")
865
+ lines.append(" Split Deals Agreed upfront in writing; default 50/50.")
866
+ lines.append(" Plan Changes Company may update at each FY with 30 days notice.")
867
+ if draw_amt > 0:
868
+ lines.append(f" Draw Period {p.get('draw_months', 0)} months from start. Ceases if employment ends.")
869
+ if equity:
870
+ lines.append(f" Equity Vesting {equity.get('vesting', '4-year vest, 1-year cliff')}.")
871
+ lines.append(" Territory As assigned by Company. May be refined as team grows.")
872
+ lines.append("")
873
+ section_num += 1
874
+
875
+ # Acknowledgment
876
+ lines.append(f"{section_num}. ACKNOWLEDGMENT & ACCEPTANCE")
877
+ lines.append("")
878
+ lines.append(" By signing below, both parties agree to the terms of this")
879
+ lines.append(" Sales Compensation Plan.")
880
+ lines.append("")
881
+ lines.append(" ________________________________________")
882
+ lines.append(" Account Executive Signature Date")
883
  lines.append("")
884
+ lines.append(" ________________________________________")
885
+ lines.append(" Authorized Signatory Date")
 
 
 
 
886
 
887
  agreement_text = "\n".join(lines)
888