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>
124 lines
4.9 KiB
Python
124 lines
4.9 KiB
Python
"""Engine pins — every number hand-checked before pinning.
|
||
|
||
RA_benefit = v×(1−rf), RA_cost = v×(1+rf), PV = Σ RA_n/(1.1)^n, initial
|
||
undiscounted. The composite reproduction lands within Forrester's own table
|
||
rounding of the published Financial Summary (benefits PV $223 low, costs PV
|
||
$0.22 low) — pinned both engine-exact (±$1) and against PUBLISHED (±$1,000,
|
||
the convention the retired workflow notebooks used).
|
||
"""
|
||
|
||
import pytest
|
||
|
||
from teicalc import (
|
||
BENEFITS_VERBATIM,
|
||
COSTS_VERBATIM,
|
||
PUBLISHED,
|
||
X_LABELS,
|
||
YEAR_INDEX,
|
||
YEARS,
|
||
by_calendar,
|
||
compute_summary,
|
||
discount_factor,
|
||
money,
|
||
npv,
|
||
payback_label,
|
||
payback_months,
|
||
payback_years,
|
||
roi_pct,
|
||
)
|
||
|
||
# Hand-checked risk-adjusted PVs per row (see module docstring).
|
||
ROW_PVS = {
|
||
"ai_contact_resolution": 51_699_826.78,
|
||
"ai_content_sentiment": 11_326_357.54,
|
||
"ai_forecasting_supervision": 19_469_777.37,
|
||
"data_driven_profit_lift": 3_123_065.36,
|
||
"legacy_solution_savings": 16_077_540.50,
|
||
"amazon_connect_usage": 20_819_775.10,
|
||
"implementation_migration": 1_555_794.82,
|
||
"ongoing_management": 607_505.86,
|
||
}
|
||
|
||
|
||
@pytest.fixture(scope="module")
|
||
def composite():
|
||
return compute_summary(BENEFITS_VERBATIM, COSTS_VERBATIM, 0.10)
|
||
|
||
|
||
def test_calendar_mapping():
|
||
assert YEARS == [2026, 2027, 2028]
|
||
assert YEAR_INDEX == {2026: 1, 2027: 2, 2028: 3}
|
||
assert X_LABELS == ["Initial", "2026", "2027", "2028"]
|
||
assert by_calendar({"1": 10, "2": 20, "3": 30}) == {2026: 10, 2027: 20, 2028: 30}
|
||
|
||
|
||
def test_primitives():
|
||
assert discount_factor(0, 0.10) == 1.0
|
||
assert discount_factor(1, 0.10) == pytest.approx(1 / 1.1)
|
||
assert npv([110], 0.10) == pytest.approx(100)
|
||
assert npv([110], 0.10, initial=-50) == pytest.approx(50)
|
||
assert roi_pct(101_696_791, 22_983_076) == pytest.approx(342.48, abs=0.1)
|
||
assert roi_pct(100, 0) == 0.0
|
||
assert money(78_713_492) == "$78.7M"
|
||
assert money(-250_000) == "-$250K"
|
||
|
||
|
||
def test_payback_edges():
|
||
assert payback_years(0, [100]) == 0.0
|
||
assert payback_years(500, []) is None
|
||
assert payback_years(500, [-100, 200]) is None # gap widens, never covered
|
||
assert payback_years(300, [-100, 400]) == pytest.approx(2.0)
|
||
assert payback_months(100, [1_200]) == pytest.approx(1.0)
|
||
assert payback_label(None) == "beyond 2028"
|
||
assert payback_label(0.0) == "immediate"
|
||
assert payback_label(0.7178) == "0.7 months (~Jan 2026)"
|
||
assert payback_label(14.2) == "14.2 months (~Mar 2027)"
|
||
|
||
|
||
def test_per_row_pvs(composite):
|
||
rows = composite["rows"]["benefits"] + composite["rows"]["costs"]
|
||
assert len(rows) == 8
|
||
for row in rows:
|
||
assert row["pv"] == pytest.approx(ROW_PVS[row["field_key"]], abs=1)
|
||
|
||
|
||
def test_composite_totals_engine_exact(composite):
|
||
assert composite["benefits_pv"] == pytest.approx(101_696_567.55, abs=1)
|
||
assert composite["costs_pv"] == pytest.approx(22_983_075.78, abs=1)
|
||
assert composite["npv"] == pytest.approx(78_713_491.78, abs=1)
|
||
assert composite["roi_pct"] == pytest.approx(342.4846, abs=0.01)
|
||
assert composite["payback_months"] == pytest.approx(0.7178, abs=0.001)
|
||
assert composite["initial_costs"] == pytest.approx(1_196_250, abs=0.01)
|
||
|
||
|
||
def test_composite_reproduces_published(composite):
|
||
assert composite["benefits_pv"] == pytest.approx(PUBLISHED["benefits_pv"], abs=1_000)
|
||
assert composite["costs_pv"] == pytest.approx(PUBLISHED["costs_pv"], abs=1_000)
|
||
assert composite["npv"] == pytest.approx(PUBLISHED["npv"], abs=1_000)
|
||
assert round(composite["roi_pct"]) == PUBLISHED["roi_pct"]
|
||
assert composite["payback_months"] < PUBLISHED["payback_months_max"]
|
||
assert composite["payback_label"] == "0.7 months (~Jan 2026)"
|
||
|
||
|
||
def test_yearly_schedules(composite):
|
||
assert composite["benefits_by_year"][2026] == pytest.approx(27_279_019.00, abs=0.01)
|
||
assert composite["benefits_by_year"][2027] == pytest.approx(40_333_658.20, abs=0.01)
|
||
assert composite["benefits_by_year"][2028] == pytest.approx(57_983_494.40, abs=0.01)
|
||
assert composite["costs_by_year"][2026] == pytest.approx(7_281_066.70, abs=0.01)
|
||
assert composite["costs_by_year"][2027] == pytest.approx(8_771_168.50, abs=0.01)
|
||
assert composite["costs_by_year"][2028] == pytest.approx(10_539_889.05, abs=0.01)
|
||
assert composite["cumulative_net_by_year"][2028] == pytest.approx(97_807_797.35, abs=0.01)
|
||
|
||
|
||
def test_cross_foots(composite):
|
||
assert composite["npv"] == pytest.approx(
|
||
composite["benefits_pv"] - composite["costs_pv"], abs=0.01)
|
||
for y in YEARS:
|
||
assert composite["net_by_year"][y] == pytest.approx(
|
||
composite["benefits_by_year"][y] - composite["costs_by_year"][y], abs=0.01)
|
||
assert composite["cumulative_net_by_year"][2028] == pytest.approx(
|
||
sum(composite["net_by_year"].values()) - composite["initial_costs"], abs=0.01)
|
||
for table, total in (("benefits", "benefits_pv"), ("costs", "costs_pv")):
|
||
assert sum(r["pv"] for r in composite["rows"][table]) == pytest.approx(
|
||
composite[total], abs=0.01)
|