studies/202602_AmazonConnect -> studies/202602_TEI_Amazon_Connect, rebuilt as pattern Variant 4 (TEI composite reproduction): - teicalc/ self-contained engine (stdlib-only): Forrester's tables as the never-edited verbatim anchor, NPV/ROI/payback + risk adjustment transplanted from core/calculations, ClientDrivers overlay (contacts/ agents/fixed driver map, growth re-base, identity at composite scale), scenario stress with core-identical semantics - one deliverable notebook (business_case.ipynb): widget-pair sidebar drivers, published-vs-overlay KPI columns, cash-flow/waterfall/scenario charts, verification gate, backstage JSON data appendix - gate + tests reproduce the published totals within PDF rounding: NPV $78.7M / ROI 342% / payback <6 months (engine $78,713,492 / 342.48% / 0.7 months); 27 study tests, headless nbconvert green, stage simulation leak-free, exports carry the appendix - old Athena workflow (00_provision..04_export, config.py, seed_data.py) deleted; git history preserves it; root test fixture repointed to teicalc.anchor - docs: study README rewritten; root README points new studies at template/MercuryNotebook; pattern doc stale ctm-token-calculator paths now cite studies/202607_CTM_GenesysCX; Variant 4 cites this study as its realized reference Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
97 lines
3.6 KiB
Python
97 lines
3.6 KiB
Python
"""Client-overlay pins — identity at the composite, linear per-driver
|
|
scaling, growth re-basing, and copy semantics."""
|
|
|
|
import dataclasses
|
|
|
|
import pytest
|
|
|
|
from teicalc import (
|
|
BENEFIT_DRIVERS,
|
|
BENEFITS_VERBATIM,
|
|
COMPOSITE,
|
|
COST_DRIVERS,
|
|
COSTS_VERBATIM,
|
|
ClientDrivers,
|
|
compute_summary,
|
|
growth_multiplier,
|
|
overlay_rows,
|
|
scale_factor,
|
|
)
|
|
|
|
|
|
def _row(rows, key):
|
|
return next(r for r in rows if r["field_key"] == key)
|
|
|
|
|
|
def test_identity_at_composite():
|
|
"""overlay_rows(COMPOSITE) reproduces the verbatim study to the cent."""
|
|
ob, oc = overlay_rows(COMPOSITE)
|
|
got = compute_summary(ob, oc, 0.10)
|
|
want = compute_summary(BENEFITS_VERBATIM, COSTS_VERBATIM, 0.10)
|
|
assert got["benefits_pv"] == pytest.approx(want["benefits_pv"], abs=0.01)
|
|
assert got["costs_pv"] == pytest.approx(want["costs_pv"], abs=0.01)
|
|
assert got["npv"] == pytest.approx(want["npv"], abs=0.01)
|
|
|
|
|
|
def test_driver_map_covers_every_row():
|
|
assert set(BENEFIT_DRIVERS) == {r["field_key"] for r in BENEFITS_VERBATIM}
|
|
assert set(COST_DRIVERS) == {r["field_key"] for r in COSTS_VERBATIM}
|
|
|
|
|
|
def test_scale_factor():
|
|
d = ClientDrivers(agents_fte=1_000, annual_contacts_y1=40_000_000)
|
|
assert scale_factor("agents", d) == pytest.approx(0.5)
|
|
assert scale_factor("contacts", d) == pytest.approx(2.0)
|
|
assert scale_factor("fixed", d) == 1.0
|
|
with pytest.raises(KeyError):
|
|
scale_factor("revenue", d)
|
|
|
|
|
|
def test_half_agents_halves_agent_rows_only():
|
|
ob, oc = overlay_rows(ClientDrivers(agents_fte=1_000))
|
|
assert _row(ob, "ai_forecasting_supervision")["year_values"]["1"] == \
|
|
pytest.approx(6_651_680 / 2)
|
|
assert _row(ob, "legacy_solution_savings")["year_values"]["1"] == \
|
|
pytest.approx(6_177_600 / 2)
|
|
# Contact-driven and fixed rows unmoved.
|
|
assert _row(ob, "ai_contact_resolution")["year_values"]["1"] == \
|
|
pytest.approx(13_911_040)
|
|
assert _row(oc, "amazon_connect_usage")["year_values"]["1"] == \
|
|
pytest.approx(6_456_448)
|
|
assert _row(oc, "implementation_migration")["initial"] == 1_087_500
|
|
|
|
|
|
def test_double_contacts_doubles_usage_only():
|
|
_, oc = overlay_rows(ClientDrivers(annual_contacts_y1=40_000_000))
|
|
assert _row(oc, "amazon_connect_usage")["year_values"]["1"] == \
|
|
pytest.approx(6_456_448 * 2)
|
|
assert _row(oc, "implementation_migration")["year_values"]["1"] == \
|
|
pytest.approx(188_333)
|
|
assert _row(oc, "ongoing_management")["year_values"]["1"] == \
|
|
pytest.approx(256_200)
|
|
|
|
|
|
def test_growth_rebase():
|
|
assert growth_multiplier(1, 0.0) == 1.0 # Y1 always 1.0
|
|
assert growth_multiplier(2, 0.0) == pytest.approx(1 / 1.3)
|
|
assert growth_multiplier(3, 0.0) == pytest.approx((1 / 1.3) ** 2)
|
|
assert growth_multiplier(3, 0.30) == 1.0 # composite growth = identity
|
|
|
|
ob, oc = overlay_rows(ClientDrivers(growth_rate=0.0))
|
|
row = _row(ob, "ai_contact_resolution")
|
|
assert row["year_values"]["1"] == pytest.approx(13_911_040)
|
|
assert row["year_values"]["2"] == pytest.approx(23_932_480 / 1.3)
|
|
assert row["year_values"]["3"] == pytest.approx(37_797_760 / 1.3**2)
|
|
# Fixed rows ignore the growth re-base too.
|
|
assert _row(oc, "ongoing_management")["year_values"]["2"] == pytest.approx(187_200)
|
|
|
|
|
|
def test_drivers_frozen_and_rows_are_copies():
|
|
with pytest.raises(dataclasses.FrozenInstanceError):
|
|
COMPOSITE.agents_fte = 1 # type: ignore[misc]
|
|
ob, oc = overlay_rows(COMPOSITE)
|
|
ob[0]["year_values"]["1"] = -1
|
|
oc[0]["year_values"]["1"] = -1
|
|
assert BENEFITS_VERBATIM[0]["year_values"]["1"] == 13_911_040
|
|
assert COSTS_VERBATIM[0]["year_values"]["1"] == 6_456_448
|