Files
palladium/studies/202512_GenesysCX/ctm-token-calculator/tokencalc/migration_wfm.py

84 lines
3.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Migration + WFM scenario — the no-AI business case.
Current state → Genesys Cloud CX migration, NA users migrated onto
Genesys WFM, WFM implemented for APAC (ANZ + ASIA). Migration and WFM
are included in the base implementation price (the verbatim PS +
training), so the only cost lines are the existing-platform run-off,
the licence ramp, and base PS — no token consumption, no AI
implementation labour. The only benefits kept are the deck's verbatim
WFM lines for the regions in WFM scope.
Single source of truth behind ``notebooks/ctm_migration_wfm.ipynb``
(served with Mercury) — the presentation layer holds no math. All
primitives come from :mod:`tokencalc.appendix4`; this module only
scopes and extrapolates.
"""
from __future__ import annotations
import math
import pandas as pd
from . import appendix4 as a4
#: WFM scope in this scenario. NA is a migration off its existing
#: WFM-like tool (verbatim benefit $0 — "has similar feature"); ANZ and
#: ASIA are new implementations ("APAC"); EMEA is out of scope, so the
#: deck's EMEA WFM benefit line is dropped.
DEFAULT_WFM_REGIONS = ["NA", "ANZ", "ASIA"]
def wfm_benefits_by_year(
benefit_rollout, regions: list[str] | None = None
) -> pd.DataFrame:
"""Verbatim WFM benefits for the scoped regions, phased on the
deck's deployment schedule (realize = impl + 3 months, inclusive).
Long DataFrame: region, capability, year, benefit — the WFM slice
of :func:`tokencalc.appendix4.benefits_by_year`.
"""
scope = DEFAULT_WFM_REGIONS if regions is None else regions
df = a4.benefits_by_year(benefit_rollout)
return df[(df["capability"] == "WFM")
& (df["region"].isin(scope))].reset_index(drop=True)
def wfm_annual_runrate(regions: list[str] | None = None) -> float:
"""Sum of the verbatim WFM *annual* values for the scoped regions."""
scope = DEFAULT_WFM_REGIONS if regions is None else regions
return float(sum(annual for (r, c), (annual, _t) in
a4.VERBATIM_BENEFITS.items()
if c == "WFM" and r in scope))
def runrate_saving_annual(
licence_annual: float | None = None,
regions: list[str] | None = None,
baseline_annual: float | None = None,
) -> float:
"""Steady-state annual saving once term contracts end and the ramp
is over: (baseline licence run-rate) + scoped WFM annual values.
"""
lic = a4.TCO_VERBATIM["ccaas_annual"] if licence_annual is None else licence_annual
base = a4.TCO_VERBATIM["current_annual"] if baseline_annual is None else baseline_annual
return (base - lic) + wfm_annual_runrate(regions)
def runrate_breakeven_label(
net_by_year: dict[int, float], runrate_annual: float
) -> str:
"""Payback label, extrapolated past the model window at a run-rate.
Inside 2026-28 this defers to :func:`tokencalc.appendix4.payback_label`;
a deficit at end-2028 fills at ``runrate_annual`` per year.
"""
deficit = -sum(net_by_year[y] for y in a4.YEARS)
if deficit <= 0:
return a4.payback_label(net_by_year)
if runrate_annual <= 0:
return "never at current run-rate"
m = 12 * len(a4.YEARS) + math.ceil(12 * deficit / runrate_annual)
return f"{m} months (~{a4.month_label(m)}, extrapolated)"