CTM Calculators: Add PS billing milestones, Adjust Genesys RAMP

This commit is contained in:
2026-07-08 11:24:57 -04:00
parent b5af65891c
commit a991879061
13 changed files with 4264 additions and 2712 deletions

View File

@@ -41,6 +41,10 @@ def test_ramp_zeroes_year_one_licences():
2028: 4_300_000.0}
assert a4.licence_costs_by_year(0)[2026] == 4_300_000.0
assert a4.licence_costs_by_year(18)[2027] == pytest.approx(4_300_000 * 6 / 12)
# The order form's ramp is 6 months — licences bill from July 2026.
assert a4.DEFAULT_RAMP_MONTHS == 6
assert a4.licence_costs_by_year() == {2026: 2_150_000.0, 2027: 4_300_000.0,
2028: 4_300_000.0}
def test_current_state_run_off():
@@ -113,3 +117,21 @@ def test_contracted_overlays_verbatim():
assert a4.TCO_VERBATIM["ccaas_annual"] == 4_300_000 # deck record intact
assert a4.tco("current_annual") == a4.TCO_VERBATIM["current_annual"]
assert a4.licence_costs_by_year(12, a4.tco("ccaas_annual"))[2027] == 3_200_000
def test_sow_milestones_and_managed_services():
assert a4.PS_CONTRACTED_TOTAL == pytest.approx(2_025_446.48)
for m in a4.PS_MILESTONES: # amounts match the shares
assert m["amount"] == pytest.approx(m["share"] * a4.PS_CONTRACTED_TOTAL,
abs=0.01)
ps = a4.ps_costs_by_year(contracted=True) # 50/50 across 2026-27
assert ps[2026] == pytest.approx(607_633.94 + 405_089.30 + 167_000)
assert ps[2027] == pytest.approx(607_633.94 + 405_089.30)
assert ps[2028] == 0.0
# The deck's verbatim year-1 lump stays intact for the as-pitched frame.
assert a4.ps_costs_by_year() == {2026: 2_567_000, 2027: 0.0, 2028: 0.0}
# Managed services bill from the month after MCX go-live (Sep 30 → Oct).
ms = a4.managed_services_by_year()
assert ms[2026] == pytest.approx(410_918.40 * 3 / 12)
assert ms[2027] == pytest.approx(410_918.40)
assert ms[2028] == pytest.approx(410_918.40)

View File

@@ -47,11 +47,9 @@ def test_email_benefit_split():
[site], FeatureScope("Email AI (Auto-Respond)", ["Small"]),
"realistic", year=1,
)
# Auto-Suggest is not a separate line — it lives inside Agent Copilot.
lines = set(df["benefit_line"])
assert lines == {
"Email Auto-Respond (displaced handling)",
"Email Auto-Suggest (drafting time)",
}
assert lines == {"Email Auto-Respond (displaced handling)"}
# auto-respond: 1,000×12 × 20% × 600s × 50% × $0.01 = $7,200
respond = df[df["benefit_line"].str.contains("Respond")]["annual_value"].sum()
assert respond == pytest.approx(7_200)

View File

@@ -28,8 +28,8 @@ def test_all_spec_meters_present():
"AI Translate",
# Genesys Cloud Copilot
"Genesys Cloud Copilot",
# Email AI (rates TBD)
"Email AI (Auto-Suggest)", "Email AI (Auto-Respond)",
# Email AI (rate TBD; Auto-Suggest is inside Agent Copilot)
"Email AI (Auto-Respond)",
}
assert expected == set(DEFAULT_METERS)
@@ -59,9 +59,7 @@ def test_confirmed_rates():
def test_unknown_meters_flagged():
unknown = {f for f, m in DEFAULT_METERS.items() if m.confidence is Confidence.UNKNOWN}
assert unknown == {
"Email AI (Auto-Suggest)", "Email AI (Auto-Respond)",
}
assert unknown == {"Email AI (Auto-Respond)"}
assert Confidence.UNKNOWN.icon == "🔴"
assert Confidence.CONFIRMED.icon == "🟢"

View File

@@ -41,20 +41,46 @@ def test_runrate_saving_annual():
assert mw.runrate_saving_annual(regions=["NA"]) == pytest.approx(3_000_000)
assert mw.runrate_saving_annual(licence_annual=4_800_000,
regions=[]) == pytest.approx(2_500_000)
# Contracted frame: managed services stay in the run-rate forever.
assert mw.runrate_saving_annual(
a4.tco("ccaas_annual"), managed_annual=a4.MANAGED_SERVICES_ANNUAL
) == pytest.approx(6_589_081.60)
def _default_wfm_benefits_by_year():
ben = mw.wfm_benefits_by_year(_default_benefit_rollout())
return {y: float(ben.loc[ben.year == y, "benefit"].sum()) for y in a4.YEARS}
def test_breakeven_extrapolates_past_window():
# Deck frame: deck licence rate (6-month ramp), verbatim PS lump,
# no managed services.
cs = a4.current_state_inputs(SITES)
cur = a4.current_costs_by_year(cs)
lic = a4.licence_costs_by_year()
ps = a4.ps_costs_by_year()
total = {y: cur[y] + lic[y] + ps[y] for y in a4.YEARS}
ben = mw.wfm_benefits_by_year(_default_benefit_rollout())
ben_y = {y: float(ben.loc[ben.year == y, "benefit"].sum()) for y in a4.YEARS}
inc, net = a4.case_flows(total, ben_y)
assert sum(net.values()) == pytest.approx(-1_553_000, abs=1_000)
inc, net = a4.case_flows(total, _default_wfm_benefits_by_year())
assert sum(net.values()) == pytest.approx(-3_703_000, abs=1_000)
label = mw.runrate_breakeven_label(net, mw.runrate_saving_annual())
assert label == "40 months (~Apr 2029, extrapolated)"
assert label == "44 months (~Aug 2029, extrapolated)"
def test_contracted_frame_with_sow_and_managed_services():
# Contracted frame: signed licence rate, SOW PS milestones, managed
# services from MCX go-live — the case the notebook leads with.
cs = a4.current_state_inputs(SITES)
cur = a4.current_costs_by_year(cs)
lic = a4.licence_costs_by_year(annual=a4.tco("ccaas_annual"))
ps = a4.ps_costs_by_year(contracted=True)
man = a4.managed_services_by_year()
total = {y: cur[y] + lic[y] + ps[y] + man[y] for y in a4.YEARS}
inc, net = a4.case_flows(total, _default_wfm_benefits_by_year())
assert sum(net.values()) == pytest.approx(-1_503_013, abs=1_000)
runrate = mw.runrate_saving_annual(
a4.tco("ccaas_annual"), managed_annual=a4.MANAGED_SERVICES_ANNUAL)
label = mw.runrate_breakeven_label(net, runrate)
assert label == "39 months (~Mar 2029, extrapolated)"
def test_breakeven_defers_in_window_and_guards_zero_runrate():