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

@@ -107,6 +107,26 @@ TCO_CONTRACTED: dict[str, float] = {
"ccaas_annual": 3_200_000, # signed licence run-rate (deck pitched $4.3M/yr)
}
#: NTT professional services — SOW billing milestones (🟢 contractual).
#: The deck's verbatim anchor books $2.4M PS in year 1; the signed SOW
#: bills $2,025,446.48 in four milestones split 50/50 across 2026-27.
PS_MILESTONES: list[dict] = [
{"name": "SOW Effective Date", "date": dt.date(2026, 3, 15),
"share": 0.30, "amount": 607_633.94},
{"name": "Start of client UAT (first region)", "date": dt.date(2026, 9, 30),
"share": 0.20, "amount": 405_089.30},
{"name": "Start of client UAT (last region)", "date": dt.date(2027, 6, 30),
"share": 0.30, "amount": 607_633.94},
{"name": "Completion of last go-live migration", "date": dt.date(2027, 9, 30),
"share": 0.20, "amount": 405_089.30},
]
PS_CONTRACTED_TOTAL = sum(m["amount"] for m in PS_MILESTONES)
#: NTT managed services — commences billing at MCX go-live (🟢 contractual).
#: Not in the deck's TCO at all; an ongoing run-rate cost thereafter.
MANAGED_SERVICES_ANNUAL = 410_918.40
MCX_GO_LIVE = dt.date(2026, 9, 30)
def tco(key: str) -> float:
"""Contracted value where one exists, else the deck's verbatim anchor."""
@@ -120,7 +140,7 @@ REALIZE_MONTH = {r: m + BENEFIT_LAG_MONTHS for r, m in IMPL_MONTH.items()}
#: NA Gantt exception: Email implemented Jan 2027, realizes Apr 2027.
NA_EMAIL_IMPL_MONTH = 13
DEFAULT_RAMP_MONTHS = 12 # Genesys ramp programme
DEFAULT_RAMP_MONTHS = 6 # Genesys ramp programme (🟢 order form)
DEFAULT_TERMINATION = dt.date(2027, 12, 31) # current-platform term contracts
# ── Region ⇄ site mapping ────────────────────────────────────────────
@@ -275,12 +295,42 @@ def licence_costs_by_year(
for y in YEARS}
def ps_costs_by_year() -> dict[int, float]:
"""Base professional services + training — verbatim, year 1 only."""
return {2026: TCO_VERBATIM["prof_services_y1"] + TCO_VERBATIM["training_y1"],
def ps_costs_by_year(contracted: bool = False) -> dict[int, float]:
"""Base professional services + training.
Verbatim: the deck's $2.4M PS lump plus training, all in year 1.
Contracted: PS phased on the SOW billing milestones (50/50 across
2026-27, $2.03M total); training stays the verbatim year-1 line —
the SOW milestones don't itemize it separately.
"""
training = TCO_VERBATIM["training_y1"]
if contracted:
ps = {y: 0.0 for y in YEARS}
for m in PS_MILESTONES:
ps[m["date"].year] += m["amount"]
return {y: ps[y] + (training if y == 2026 else 0.0) for y in YEARS}
return {2026: TCO_VERBATIM["prof_services_y1"] + training,
2027: 0.0, 2028: 0.0}
def ps_milestones_dataframe() -> pd.DataFrame:
"""The SOW billing milestones as a display table."""
return pd.DataFrame(PS_MILESTONES)
def managed_services_by_year(
annual: float = MANAGED_SERVICES_ANNUAL, start: dt.date = MCX_GO_LIVE
) -> dict[int, float]:
"""Managed services bill from the month after go-live (Sep 30 → Oct),
then run at the full annual rate — an ongoing cost with no end date
inside the model window."""
def _months(y: int) -> int:
if y < start.year:
return 0
return 12 if y > start.year else 12 - start.month
return {y: annual * _months(y) / 12 for y in YEARS}
# ── Token consumption (missing cost #1) ──────────────────────────────
@@ -293,7 +343,6 @@ def claim_scenario(email_auto_respond_rate: float = 0.255) -> Scenario:
voice_summarization_eligibility=0.0,
voice_knowledge_eligibility=0.0, # unused by the Appendix-4 scope set
email_auto_respond_rate=email_auto_respond_rate,
email_auto_suggest_acceptance=0.0, # Auto-Suggest is inside Copilot (V2 #1)
consumption_cost_realization={1: 1.0, 2: 1.0, 3: 1.0},
)