Build an AI business case a finance team will accept, with options, full costs, risk-adjusted benefits, switching values and a plan to track benefits after go-live.
A business case answers one question for the people who hold the budget: is this worth doing, compared with the other things we could do with the same money?
For AI, a good business case does five things:
Compares options. Doing nothing, switching on a standard SAP feature, and building something custom are all options. "Build or not" is too narrow.
Counts the full cost. Model tokens are usually a small part. People, platform, security reviews, training and support are most of it.
Discounts benefits by how sure you are. A saving measured in a pilot is worth more than one a vendor estimated.
Names the assumption that would flip the answer. For example: "if clerks save less than 4.7 minutes per order, this loses money."
Says how benefits will be tracked after go-live, by whom, and what happens if they fall short.
The business case is where the earlier work of this course comes together. Unit 8 gave you a metric and a pilot result. Token economics gave you a cost per transaction. This topic turns both into a decision.
Most AI pilots are easy to fund and hard to scale. The pilot is cheap and exciting; the production system needs a budget line, an owner and a promise. That promise is the business case.
Take the running example: an assistant that explains blocked sales orders to order-to-cash clerks. The pilot showed clerks spent less time per blocked order. Three things decide whether that becomes money:
Volume. A few minutes saved on 6,000 orders a month is hundreds of hours. The same saving on 200 orders is not worth a project.
Realization. Hours saved are only worth something if the freed time goes to work that matters, such as clearing the backlog or calling customers earlier. If nobody plans for that, the hours disappear.
Running cost. The assistant needs a platform, monitoring, someone to update prompts and models, and security reviews. These continue every month.
A business case that ignores any of these will look better on paper than in the profit and loss statement. Finance teams know this. A case that shows the uncertainty openly is more likely to be approved than one that hides it.
As of October 2026, SAP offers several tools that feed a business case. None of them replaces your own numbers.
SAP Business AI Feature Catalog in SAP Discovery Center lists SAP's AI features with use cases, value and metrics. SAP News reported almost 400 features and agents in the catalog in June 2026.
SAP Business AI Feature Estimator estimates how many AI Units a feature would need, so you can budget before you switch it on. SAP Learning describes it as support for early commercial discussions; the next step is a quote from SAP.
SAP Business AI value calculator on sap.com takes your line of business, industry, employee count and revenue, and returns an estimated annual EBIT increase. SAP states the result is an estimate, not an offer.
SAP for Me shows your AI Unit balance and consumption after you buy, by product and feature. This is where you track the cost side after go-live.
SAP Signavio's value case creation agent drafts value cases from process mining insights, with configurable cost and effort parameters. When checked on 7 October 2026, its product page offered registration for beta testing.
Two commercial facts change the arithmetic. SAP's pricing page says base AI features come with standard cloud subscriptions at no extra cost, while premium features use AI Units. It also says AI Units are bought annually and expire after 12 months if unused. Buying too many is a cost; buying too few means asking for more mid-year.
This is the shape of a case a steering committee can read in five minutes. The numbers are made up for the blocked-orders assistant; the deep layer computes them.
Line
Build: custom assistant
Buy: standard feature, if one fits
Coverage
All block reasons
Assumed: credit blocks only (60%)
Main benefit
6 minutes of clerk work saved per order (pilot)
Same per order, fewer orders
One-time cost
135,000 EUR
60,000 EUR
Running cost per month
About 7,300 EUR
About 2,200 EUR, half of it AI Units
Net present value, 3 years
82,000 EUR
93,000 EUR
Payback
Month 23
Month 16
Flips to a loss if
Clerks save under 4.7 minutes, or only 47% of freed time is used
Not tested
Biggest uncertainty
How much freed time is used
Whether a standard feature fits at all
Notice three things:
Both options pay back, and neither by much. That is normal for honest AI cases. A case with a 1,000% return usually hides a cost or counts hours as money.
The cheaper option wins on value here, but covers less. The committee's real question is whether the extra coverage is worth 70,000 EUR more up front.
The switching values are the conversation. "This loses money if clerks save under 4.7 minutes" tells the process owner exactly what to protect.
"Token costs are the main cost." In most enterprise cases, people and platform cost far more than tokens. In this topic's example, tokens are under 2% of running cost.
"Hours saved equals money saved." Only if the time is reused or the work would otherwise need more people. Count a realization share, not 100%.
"A vendor's value estimate is our benefit." It is a starting point. Your process, data and adoption decide your result.
"If the pilot worked, the case is proven." A pilot measures one team for a few weeks. Adoption across all teams is slower, and running costs only start at go-live.
"A higher ROI always wins." A small, cheap option can have a high ROI and a small total value. Compare net present value and coverage as well.
"The business case is done once approved." It is a promise. Track it monthly against actuals.
Business case: a document that compares options on cost, benefit, risk and timing to support a decision.
Net present value (NPV): future benefits minus future costs, each reduced for how far in the future it falls. Positive means the option beats doing nothing.
Payback period: how long until total benefits catch up with total costs.
Return on investment (ROI): net benefit divided by cost.
Realization: the share of a theoretical saving that turns into real value.
Optimism bias: the tendency of appraisals to be too optimistic about costs, timing and benefits.
Switching value: the value an assumption must reach for the decision to flip.
AI Units: SAP's prepaid currency for premium AI features, bought annually.
Benefits tracking: comparing actual results with the plan after go-live.
Pick one answer for each question. The explanation appears after you choose.
1Your team proposes an AI assistant and shows one option: "build it". What should you ask for first?
Answer: B. A business case compares options, not a single proposal against a blank page. Doing nothing and a standard SAP feature are real alternatives. A better cost estimate for one option doesn't tell you whether it is the right one.
2The pilot shows clerks save 6 minutes per blocked order. Why does the case count only part of that time as value?
Answer: C. Hours saved turn into money only when the time is reused, for example to clear a backlog, or when the work would otherwise need more people. The realization share makes that explicit. Finance teams do accept time-based benefits when the use of the time is planned.
3In a typical enterprise AI business case, which cost is usually the smallest?
Answer: D. In this topic's example, tokens are under 2% of the running cost. People, platform, security reviews and training dominate. A case that only prices tokens will look far cheaper than it is.
4How should you use SAP's value calculator and Feature Catalog figures in your case?
Answer: D. SAP explains its estimates rest on expertise, benchmarks, third-party research and early customer feedback, and calls the calculator's result an estimate. They are useful for picking where to look. Your own baseline and pilot are the evidence a finance team will trust.
5Your SAP account team proposes buying AI Units for three years of expected growth up front. What is the risk?
Answer: A. SAP's pricing page says AI Units are bought annually and expire after 12 months if not used. Unused units are money spent for nothing. SAP for Me shows balance and consumption, so you can buy closer to real use.
6The case says "this loses money if clerks save under 4.7 minutes per order". What is that number called, and why does it matter?
Answer: B. A switching value is the point where the decision flips. It turns a vague risk into something the process owner can watch and protect. Payback is about timing, not about which assumption carries the case.
7Six months after go-live, the assistant delivers 72% of the planned time savings. What is the right next step?
Answer: C. Benefits tracking exists to trigger action. Splitting the gap into usage and minutes saved shows whether the problem is adoption or the assistant itself. Rewriting the plan hides the gap, and a bigger model only helps if minutes saved is the problem.
Ask a question
Testing: only staff see this
Stuck on something in this layer? Ask it here. Questions are answered in the order they arrive, and the answer appears under My questions.
Sign in (free) to ask a question. You can ask anonymously.
A business case is a set of options, each a stream of monthly cash flows, compared with doing nothing, and tested for which assumption would change the answer.
for each option:
benefit(month) = sum over benefit lines of value at full adoption x confidence x adoption(month)
cost(month) = (one-time + monthly + per-item x volume x adoption(month)) x (1 + contingency)
NPV = sum over months of (benefit - cost) / (1 + r) ^ month
then:
which input, moved within its plausible range, swings NPV the most?
at what value of that input does NPV reach zero? <- the switching value
Each piece answers a question a finance reviewer will ask:
Piece
The reviewer's question
Options
"What else could we do with this money?"
Benefit lines with evidence
"Where does this number come from?"
Confidence and realization
"How sure are you, and is this real money?"
Adoption ramp
"When does the value start?"
Full cost with contingency
"What have you forgotten?"
Discounting
"Is a euro in year three worth a euro today?"
Sensitivity and switching values
"What would have to be wrong for this to fail?"
Benefits tracking
"How will we know if it's working?"
The UK Treasury's Green Book, a public guide to appraising spending, uses the same building blocks: options compared on discounted costs and benefits, an adjustment for optimism bias, sensitivity analysis with switching values, and a plan for realizing benefits. It is written for public money, but its method is the one finance teams expect.
Do nothing. The baseline. Every other option is measured against it, so its NPV is zero by definition.
Use what SAP ships. A base feature (no extra license cost) or a premium feature paid in AI Units. Check the Feature Catalog and your value map first.
Build. A custom solution side by side on SAP BTP, as in Units 5 to 10.
Add others when they are real: fix the master data that causes the blocks, change a process rule, or a smaller build that covers one block reason. Sometimes "not AI" wins, and saying so builds trust.
AI benefits fall into a few kinds. Each needs its own formula and its own evidence.
Kind
Formula per month
Example
Typical evidence
Time (efficiency)
items x share affected x minutes saved / 60 x hourly cost x realization
Clerk minutes per blocked order
Pilot task logs, time studies
Per-item effect (effectiveness)
items x share affected x value per item
Fewer orders cancelled while blocked
System data, pilot vs. control
Working capital
value released per month x 12 / 365 x days faster x cost of capital / 12
Orders released earlier, cash collected earlier
Cycle times from SAP or process mining
The working-capital formula is worth slowing down for. If 40.8 million EUR of blocked orders is released each month, the daily flow is about 1.34 million EUR. Releasing them 0.2 days faster frees about 268,000 EUR of cash, once. The yearly value is what that cash would cost to finance: at 8%, about 21,500 EUR a year, or 1,788 EUR a month. A faster decision measured in hours is worth much less than it sounds.
Three separate factors shrink the benefit. Keep them separate so each can be argued on its own.
Realization asks: if the saving happens, how much becomes value? For time savings, this is the share of freed time used for work that matters. The sample case uses 0.6.
Confidence asks: how likely is the saving to happen at the size claimed? A measured pilot result might get 0.8; a guess from sales, 0.3.
Contingency on costs answers the other half of optimism bias. The Green Book's advice is to raise cost estimates and lower benefit estimates to correct for it. The sample adds 20% to every cost.
These are judgement calls. The point is not the exact factor but that it is visible and can be challenged.
Value starts when people use the system, not when it goes live. The sample assumes 30%, 60%, 80% and then 90% of blocked orders handled with the assistant in the first months after go-live. Per-item costs follow the same ramp, but fixed monthly costs start in full. That is why the early months lose money in every AI case.
At steady state the build option costs about 7,330 EUR a month with contingency. Tokens are 108 EUR of that. The model cost from Token economics matters for design choices, but it rarely decides the case. For SAP premium features, the per-item cost is AI Units instead of tokens, and the arithmetic is the same: units per business event, times volume, times the contracted price.
Money spent now and value received in two years are not equal. Discounting each month's net cash flow at the company's rate gives the net present value. Your finance team sets the rate; the sample uses 10% a year.
monthly rate r = (1 + 0.10) ^ (1/12) - 1 = 0.797%
NPV = sum of net(month) / (1 + r) ^ month, months 1..36
Report three numbers, because each answers a different question:
NPV: how much value the option adds compared with doing nothing.
Payback month: how long the money is at risk.
ROI: value per euro spent. Useful for ranking small options, misleading on its own because a tiny option can have a huge ROI.
Pick a horizon that matches how long the solution will last without a rebuild. AI moves fast; three years is a common, defensible choice.
A single NPV hides how fragile it is. Sensitivity analysis moves one input at a time between a plausible low and high value and records the NPV at each end. Sorted by swing, it is a tornado chart as a table: the inputs at the top deserve the most attention.
A switching value goes one step further: the value of that input at which NPV reaches zero. The Green Book defines it as the value an assumption would need to change to for the option to no longer be value for money. "NPV turns negative if realization drops below 0.47" is a sentence a process owner can act on.
flowchart LR
C[Case file<br/>options, benefits, costs] --> R[run<br/>NPV, payback, ROI]
C --> S[sensitivity<br/>tornado, switching values]
R --> D{Decision<br/>and gate}
S --> D
D --> G[Go-live]
G --> T[track<br/>actuals vs plan]
T --> D
A business case is a promise with a date on it. After go-live, compare the plan with actuals every month: usage, minutes saved per order and running cost. Splitting the gap matters. If usage is short, the fix is adoption and change management. If minutes saved per order is short, the fix is the assistant itself. The Green Book puts this in the "management case": plans for monitoring costs and realizing benefits belong in the case, not after it.
You will build a small calculator that reads a case file with two options for the blocked-orders assistant, computes NPV, payback and ROI for each, finds the assumptions that matter most and their switching values, and compares six months of actuals with the plan. It writes a one-page summary you can hand to a sponsor.
Before you start: complete Set up your computer for this course and Set up for Unit 10. They create your orchestrate-course folder with .venv and the unit10 folder. The script uses only Python's built-in modules, so there is nothing new to install and no account to create. If you did the exercises in Defining success metrics with the business and Token economics, keep metric_agreement.md and cost-model.md open: the exercise at the end uses them.
flowchart LR
I[init] --> J[(blocked_orders_case.json<br/>actuals.csv)]
J --> R[run]
J --> S[sensitivity]
J --> T[track]
R --> M[business_case.md]
In VS Code, right-click the unit10 folder, choose New File, name it business_case.py, paste the code below and save.
"""Unit 10: the business case for the blocked-orders assistant.
A business case compares options over time: what each one costs, what it returns, how sure we are,
and which assumption would have to be wrong for the answer to flip. This script reads a case file
(JSON), computes each option month by month against "do nothing", and writes a one-page summary.
It uses only built-in Python. All numbers in the sample case are MADE UP for teaching.
python unit10/business_case.py init write the sample case and sample actuals to unit10/case/
python unit10/business_case.py run NPV, payback and ROI per option; writes business_case.md
python unit10/business_case.py sensitivity which assumption moves the answer most, and switching values
python unit10/business_case.py track after go-live: compare actual months with the plan
add --case PATH to use your own case file, --actuals PATH for your own actuals
"""
import argparse
import copy
import csv
import json
import sys
from pathlib import Path
HERE = Path(__file__).resolve().parent
CASE_DIR = HERE / "case"
DEFAULT_CASE = CASE_DIR / "blocked_orders_case.json"
DEFAULT_ACTUALS = CASE_DIR / "actuals.csv"
# ---------------------------------------------------------------------------------------------
# The sample case. Every number is made up; replace them with your own measured or quoted values.
# ---------------------------------------------------------------------------------------------
SAMPLE_CASE = {
"name": "Blocked-orders assistant",
"currency": "EUR",
"horizon_months": 36,
"discount_rate_annual": 0.10,
"cost_contingency": 0.20,
"items_per_month": 6000,
"item_name": "blocked order",
"options": [
{
"id": "build",
"title": "Build: custom assistant on SAP BTP",
"go_live_month": 4,
"adoption_ramp": [0.3, 0.6, 0.8, 0.9],
"benefits": [
{"name": "Clerk time saved", "kind": "time", "share_of_items": 1.0,
"minutes_saved": 6.0, "hourly_cost": 55, "realization": 0.6, "confidence": 0.8,
"evidence": "pilot task logs: 15 to 9 minutes of clerk work per order"},
{"name": "Faster release, less cash tied up", "kind": "working_capital",
"value_released_per_month": 40800000, "days_faster": 0.2,
"cost_of_capital_annual": 0.08, "confidence": 0.6,
"evidence": "pilot: median decision 4.7 hours faster than control"},
{"name": "Fewer orders cancelled while blocked", "kind": "per_item",
"share_of_items": 0.0005, "value_per_item": 2000, "confidence": 0.3,
"evidence": "sales estimate, not measured"},
],
"one_time_costs": [
{"name": "Build team (2 people, 10 weeks)", "amount": 100000, "month": 1},
{"name": "Security review and authorization design", "amount": 20000, "month": 3},
{"name": "Training and change management", "amount": 15000, "month": 4},
],
"monthly_costs": [
{"name": "Platform (runtime, AI Core, vector store)", "amount": 2500, "from_month": 1},
{"name": "Support and model updates (0.25 person)", "amount": 3000, "from_month": 4},
{"name": "Evaluation and monitoring", "amount": 500, "from_month": 4},
],
"per_item_costs": [
{"name": "Model tokens", "amount": 0.02},
],
},
{
"id": "standard",
"title": "Buy: a standard feature, if one fits (placeholder values)",
"go_live_month": 3,
"adoption_ramp": [0.4, 0.7, 0.9],
"benefits": [
{"name": "Clerk time saved", "kind": "time", "share_of_items": 0.6,
"minutes_saved": 6.0, "hourly_cost": 55, "realization": 0.6, "confidence": 0.7,
"evidence": "assumed: covers credit blocks only (60% of blocks)"},
{"name": "Faster release, less cash tied up", "kind": "working_capital",
"value_released_per_month": 40800000, "days_faster": 0.12,
"cost_of_capital_annual": 0.08, "confidence": 0.5,
"evidence": "assumed: 60% of the build option's effect"},
],
"one_time_costs": [
{"name": "Configuration and testing", "amount": 40000, "month": 1},
{"name": "Training and change management", "amount": 20000, "month": 3},
],
"monthly_costs": [
{"name": "Key user support (0.1 person)", "amount": 1000, "from_month": 3},
],
"per_item_costs": [
{"name": "AI Units (placeholder: use an estimate and a quote)", "amount": 0.15},
],
},
],
"sensitivity": [
{"option": "build", "path": "benefits.0.minutes_saved", "low": 3, "high": 8},
{"option": "build", "path": "benefits.0.realization", "low": 0.3, "high": 0.9},
{"option": "build", "path": "benefits.0.hourly_cost", "low": 45, "high": 65},
{"option": "build", "path": "benefits.1.days_faster", "low": 0.05, "high": 0.5},
{"option": "build", "path": "one_time_costs.0.amount", "low": 80000, "high": 200000},
{"option": "build", "path": "monthly_costs.1.amount", "low": 2000, "high": 8000},
{"option": "build", "path": "per_item_costs.0.amount", "low": 0.01, "high": 0.10},
{"option": "build", "path": "go_live_month", "low": 3, "high": 8},
],
}
# Made-up actuals for the first six months after the build option went live (months 4 to 9).
SAMPLE_ACTUALS = [
# month, items, used_share, minutes_saved, run_cost
(4, 5800, 0.22, 5.1, 6200),
(5, 6100, 0.41, 5.6, 6300),
(6, 6400, 0.55, 5.8, 6500),
(7, 5900, 0.63, 6.1, 6200),
(8, 6050, 0.66, 6.0, 6300),
(9, 6200, 0.70, 6.0, 6400),
]
# ---------------------------------------------------------------------------------------------
# The arithmetic
# ---------------------------------------------------------------------------------------------
def adoption(option: dict, month: int) -> float:
"""Share of items that use the solution in a given month (1 = first month of the case)."""
k = month - option["go_live_month"]
if k < 0:
return 0.0
ramp = option["adoption_ramp"]
return ramp[min(k, len(ramp) - 1)]
def benefit_per_month(line: dict, items: float) -> float:
"""Full monthly value of one benefit line at 100% adoption, before the confidence factor."""
kind = line["kind"]
if kind == "time":
hours = items * line["share_of_items"] * line["minutes_saved"] / 60
return hours * line["hourly_cost"] * line["realization"]
if kind == "per_item":
return items * line["share_of_items"] * line["value_per_item"]
if kind == "working_capital":
# Cash released once = daily flow x days faster. The yearly value is what that cash
# would otherwise cost to finance; spread evenly over the months.
cash_released = line["value_released_per_month"] * 12 / 365 * line["days_faster"]
return cash_released * line["cost_of_capital_annual"] / 12
if kind == "monthly":
return line["amount"]
raise ValueError(f"unknown benefit kind: {kind}")
def monthly_flows(case: dict, option: dict) -> list:
"""One row per month: benefits (risk-adjusted), costs (with contingency) and net."""
rows = []
items = case["items_per_month"]
uplift = 1 + case.get("cost_contingency", 0.0)
for m in range(1, case["horizon_months"] + 1):
a = adoption(option, m)
benefit = sum(benefit_per_month(b, items) * b["confidence"] for b in option["benefits"]) * a
cost = sum(c["amount"] for c in option["one_time_costs"] if c["month"] == m)
cost += sum(c["amount"] for c in option["monthly_costs"] if m >= c["from_month"])
cost += sum(c["amount"] for c in option["per_item_costs"]) * items * a
cost *= uplift
rows.append({"month": m, "adoption": a, "benefit": benefit, "cost": cost, "net": benefit - cost})
return rows
def summarize(case: dict, option: dict) -> dict:
"""NPV, payback month and ROI over the horizon for one option, compared with doing nothing."""
rows = monthly_flows(case, option)
r = (1 + case["discount_rate_annual"]) ** (1 / 12) - 1 # monthly discount rate
npv = sum(row["net"] / (1 + r) ** row["month"] for row in rows)
pv_benefit = sum(row["benefit"] / (1 + r) ** row["month"] for row in rows)
pv_cost = sum(row["cost"] / (1 + r) ** row["month"] for row in rows)
cumulative, payback = 0.0, None
for row in rows:
cumulative += row["net"]
if payback is None and cumulative >= 0 and row["month"] >= option["go_live_month"]:
payback = row["month"]
run_rate = rows[-1]
return {"npv": npv, "pv_benefit": pv_benefit, "pv_cost": pv_cost,
"roi": (pv_benefit - pv_cost) / pv_cost if pv_cost else 0.0,
"payback": payback, "steady_benefit": run_rate["benefit"], "steady_cost": run_rate["cost"],
"cost_per_item": run_rate["cost"] / (case["items_per_month"] * run_rate["adoption"] or 1)}
def get_path(obj, path: str):
for part in path.split("."):
obj = obj[int(part)] if isinstance(obj, list) else obj[part]
return obj
def set_path(obj, path: str, value) -> None:
parts = path.split(".")
for part in parts[:-1]:
obj = obj[int(part)] if isinstance(obj, list) else obj[part]
last = parts[-1]
if isinstance(obj, list):
obj[int(last)] = value
else:
obj[last] = value
def npv_with(case: dict, option_id: str, path: str, value) -> float:
trial = copy.deepcopy(case)
option = find_option(trial, option_id)
set_path(option, path, int(round(value)) if path == "go_live_month" else value)
return summarize(trial, option)["npv"]
def switching_value(case: dict, option_id: str, path: str, low, high):
"""The value of one input at which NPV crosses zero, if it does between low and high."""
if path == "go_live_month":
for v in range(int(low), int(high) + 1): # whole months only
if npv_with(case, option_id, path, v) < 0:
return v
return None
f_low, f_high = npv_with(case, option_id, path, low), npv_with(case, option_id, path, high)
if (f_low > 0) == (f_high > 0):
return None
for _ in range(60): # bisection: halve the range until it is tiny
mid = (low + high) / 2
if (npv_with(case, option_id, path, mid) > 0) == (f_low > 0):
low = mid
else:
high = mid
return (low + high) / 2
def find_option(case: dict, option_id: str) -> dict:
for option in case["options"]:
if option["id"] == option_id:
return option
sys.exit(f"No option with id '{option_id}' in the case file.")
# ---------------------------------------------------------------------------------------------
# Commands
# ---------------------------------------------------------------------------------------------
def money(x: float, cur: str) -> str:
return f"{x:,.0f} {cur}"
def load_case(path: Path) -> dict:
if not path.exists():
sys.exit(f"Case file not found: {path}\nRun: python unit10/business_case.py init")
try:
with path.open(encoding="utf-8") as f:
case = json.load(f)
except json.JSONDecodeError as e:
sys.exit(f"{path} is not valid JSON: line {e.lineno}, column {e.colno}: {e.msg}")
for key in ("horizon_months", "discount_rate_annual", "items_per_month", "options"):
if key not in case:
sys.exit(f"The case file is missing '{key}'. Compare it with the sample from 'init'.")
for option in case["options"]:
for b in option["benefits"]:
if not 0 <= b.get("confidence", -1) <= 1:
sys.exit(f"Benefit '{b.get('name')}' needs a confidence between 0 and 1.")
return case
def cmd_init(args) -> None:
CASE_DIR.mkdir(parents=True, exist_ok=True)
DEFAULT_CASE.write_text(json.dumps(SAMPLE_CASE, indent=2), encoding="utf-8")
with DEFAULT_ACTUALS.open("w", newline="", encoding="utf-8") as f:
w = csv.writer(f)
w.writerow(["month", "items", "used_share", "minutes_saved", "run_cost"])
w.writerows(SAMPLE_ACTUALS)
print(f"Wrote {DEFAULT_CASE.relative_to(HERE.parent)} and {DEFAULT_ACTUALS.relative_to(HERE.parent)}")
print("All numbers are made up. Next: python unit10/business_case.py run")
def cmd_run(args) -> None:
case = load_case(Path(args.case))
cur = case["currency"]
years = case["horizon_months"] / 12
print(f"{case['name']}: {case['horizon_months']} months, discount rate "
f"{case['discount_rate_annual']:.0%} a year, cost contingency {case.get('cost_contingency', 0):.0%}")
print(f"Compared with doing nothing. Benefits are multiplied by their confidence.\n")
head = f"{'option':<10}{'PV benefits':>16}{'PV costs':>14}{'NPV':>14}{'ROI':>7}{'payback':>10}{'cost/item':>12}"
print(head)
results = {}
for option in case["options"]:
s = summarize(case, option)
results[option["id"]] = s
pay = f"month {s['payback']}" if s["payback"] else "none"
print(f"{option['id']:<10}{s['pv_benefit']:>16,.0f}{s['pv_cost']:>14,.0f}{s['npv']:>14,.0f}"
f"{s['roi']:>7.0%}{pay:>10}{s['cost_per_item']:>12.2f}")
print(f"\nAmounts in {cur}. cost/item = steady-state cost per {case['item_name']} handled.")
print("\nWhere the steady-state monthly value comes from (before confidence, at full adoption):\n")
for option in case["options"]:
print(f" {option['title']}")
for b in option["benefits"]:
v = benefit_per_month(b, case["items_per_month"])
print(f" {b['name']:<40}{v:>10,.0f} x confidence {b['confidence']:.1f}")
lines = [f"# Business case: {case['name']}", "",
f"Horizon {years:g} years, discount rate {case['discount_rate_annual']:.0%} a year, "
f"cost contingency {case.get('cost_contingency', 0):.0%}. Compared with doing nothing. "
f"Benefits are risk-adjusted by a confidence factor per line.", "",
"| Option | PV benefits | PV costs | NPV | ROI | Payback | Cost per item |",
"| --- | --- | --- | --- | --- | --- | --- |"]
for option in case["options"]:
s = results[option["id"]]
pay = f"month {s['payback']}" if s["payback"] else "none in horizon"
lines.append(f"| {option['title']} | {money(s['pv_benefit'], cur)} | {money(s['pv_cost'], cur)} | "
f"{money(s['npv'], cur)} | {s['roi']:.0%} | {pay} | {s['cost_per_item']:.2f} {cur} |")
lines += ["", "## Benefit lines and evidence", "",
"| Option | Benefit | Monthly value at full adoption | Confidence | Evidence |",
"| --- | --- | --- | --- | --- |"]
for option in case["options"]:
for b in option["benefits"]:
lines.append(f"| {option['id']} | {b['name']} | {money(benefit_per_month(b, case['items_per_month']), cur)} | "
f"{b['confidence']:.1f} | {b.get('evidence', '')} |")
lines += ["", "## Decision asked for", "", "_Write one sentence: which option, what budget, "
"and the gate at which it will be reviewed._", ""]
out = CASE_DIR / "business_case.md"
CASE_DIR.mkdir(parents=True, exist_ok=True)
out.write_text("\n".join(lines), encoding="utf-8")
print(f"\nWrote {out.relative_to(HERE.parent)}")
def cmd_sensitivity(args) -> None:
case = load_case(Path(args.case))
tests = case.get("sensitivity", [])
if not tests:
sys.exit("The case file has no 'sensitivity' list. Compare it with the sample from 'init'.")
base = {o["id"]: summarize(case, o)["npv"] for o in case["options"]}
print(f"NPV when one input moves to its low or high value (others stay at base). {case['currency']}\n")
rows = []
for t in tests:
option = find_option(case, t["option"])
base_value = get_path(option, t["path"])
lo = npv_with(case, t["option"], t["path"], t["low"])
hi = npv_with(case, t["option"], t["path"], t["high"])
sw = switching_value(case, t["option"], t["path"], t["low"], t["high"])
rows.append((abs(hi - lo), t, base_value, lo, hi, sw))
rows.sort(key=lambda r: r[0], reverse=True) # widest swing first: a tornado chart as a table
print(f"{'option':<9}{'input':<28}{'base':>9}{'low':>9}{'high':>9}{'NPV at low':>13}{'NPV at high':>13}{'swing':>11} switching value")
for swing, t, base_value, lo, hi, sw in rows:
if sw is None:
sw_text = "-"
elif isinstance(sw, int) or abs(sw) >= 100:
sw_text = f"{sw:,.0f}"
else:
sw_text = f"{sw:.2f}"
print(f"{t['option']:<9}{t['path']:<28}{base_value:>9g}{t['low']:>9g}{t['high']:>9g}"
f"{lo:>13,.0f}{hi:>13,.0f}{swing:>11,.0f} {sw_text}")
print(f"\nBase NPV: " + ", ".join(f"{k} {v:,.0f}" for k, v in base.items()))
print("switching value = the value of that input at which NPV reaches zero; '-' means NPV keeps its sign\n"
"between low and high.")
def cmd_track(args) -> None:
case = load_case(Path(args.case))
option = find_option(case, args.option)
path = Path(args.actuals)
if not path.exists():
sys.exit(f"Actuals file not found: {path}\nRun: python unit10/business_case.py init")
time_line = next((b for b in option["benefits"] if b["kind"] == "time"), None)
if time_line is None:
sys.exit("Tracking compares the 'time' benefit line; this option has none.")
plan = {r["month"]: r for r in monthly_flows(case, option)}
uplift = 1 + case.get("cost_contingency", 0.0)
cur = case["currency"]
print(f"Plan vs actual for '{option['id']}': clerk time saved and run cost. {cur}\n")
print(f"{'month':>5}{'used plan':>11}{'used act.':>11}{'min plan':>10}{'min act.':>10}"
f"{'value plan':>12}{'value act.':>12}{'cost plan':>11}{'cost act.':>11}")
tot_plan = tot_act = 0.0
with path.open(encoding="utf-8") as f:
for row in csv.DictReader(f):
m = int(row["month"])
items, used = float(row["items"]), float(row["used_share"])
minutes, run_cost = float(row["minutes_saved"]), float(row["run_cost"])
a = adoption(option, m)
value_plan = benefit_per_month(time_line, case["items_per_month"]) * a
actual_line = dict(time_line, minutes_saved=minutes)
value_act = benefit_per_month(actual_line, items) * used
# Plan run cost: the month's cost without one-time items, without contingency.
cost_plan = plan[m]["cost"] / uplift - sum(c["amount"] for c in option["one_time_costs"] if c["month"] == m)
tot_plan += value_plan
tot_act += value_act
print(f"{m:>5}{a:>11.0%}{used:>11.0%}{time_line['minutes_saved']:>10.1f}{minutes:>10.1f}"
f"{value_plan:>12,.0f}{value_act:>12,.0f}{cost_plan:>11,.0f}{run_cost:>11,.0f}")
share = tot_act / tot_plan if tot_plan else 0
print(f"\nCumulative time value: {tot_act:,.0f} actual vs {tot_plan:,.0f} plan ({share:.0%} of plan).")
print("Plan values here are before the confidence factor: you are checking the unadjusted promise.")
if share >= 0.9:
print("On track.")
elif share >= 0.7:
print("Behind plan. Find out whether usage or minutes saved is short, and fix that first.")
else:
print("Well behind plan. Take it to the sponsor with a recovery plan or a stop decision.")
def main() -> None:
p = argparse.ArgumentParser(description="Business case for an AI use case (made-up sample data).")
p.add_argument("command", choices=["init", "run", "sensitivity", "track"])
p.add_argument("--case", default=str(DEFAULT_CASE), help="path to the case JSON file")
p.add_argument("--actuals", default=str(DEFAULT_ACTUALS), help="path to the actuals CSV (track)")
p.add_argument("--option", default="build", help="option id to track (track)")
args = p.parse_args()
{"init": cmd_init, "run": cmd_run, "sensitivity": cmd_sensitivity, "track": cmd_track}[args.command](args)
if __name__ == "__main__":
main()
Wrote unit10/case/blocked_orders_case.json and unit10/case/actuals.csv
All numbers are made up. Next: python unit10/business_case.py run
Open unit10/case/blocked_orders_case.json in VS Code. Find the two options, build and standard. Each has benefits, one_time_costs, monthly_costs and per_item_costs. Every benefit line has a confidence and an evidence text. This file is the case: every number a reviewer might question is here, in one place.
Blocked-orders assistant: 36 months, discount rate 10% a year, cost contingency 20%
Compared with doing nothing. Benefits are multiplied by their confidence.
option PV benefits PV costs NPV ROI payback cost/item
build 457,449 375,584 81,866 22% month 23 1.36
standard 226,688 133,763 92,925 69% month 16 0.40
Amounts in EUR. cost/item = steady-state cost per blocked order handled.
Where the steady-state monthly value comes from (before confidence, at full adoption):
Build: custom assistant on SAP BTP
Clerk time saved 19,800 x confidence 0.8
Faster release, less cash tied up 1,788 x confidence 0.6
Fewer orders cancelled while blocked 6,000 x confidence 0.3
Buy: a standard feature, if one fits (placeholder values)
Clerk time saved 11,880 x confidence 0.7
Faster release, less cash tied up 1,073 x confidence 0.5
Wrote unit10/case/business_case.md
Read it like a reviewer:
Both options beat doing nothing, by about 82,000 and 93,000 EUR over three years. Neither is a landslide.
The standard option has the higher NPV and ROI but creates half the value. It wins because it costs far less, not because it does more.
Clerk time is most of the value. 6,000 orders x 6 minutes is 600 hours a month; at 55 EUR and 60% realization that is 19,800 EUR. The working-capital line is small, as "How it works" showed.
The cancellation line is large but uncertain. It is a sales estimate, so it carries a 0.3 confidence.
Cost per order is 1.36 EUR for the build, of which tokens are 0.02 EUR before contingency.
Open unit10/case/business_case.md and choose Open Preview to the Side (top right in VS Code). This is the one-page summary, with an empty "Decision asked for" line for you to fill in.
NPV when one input moves to its low or high value (others stay at base). EUR
option input base low high NPV at low NPV at high swing switching value
build benefits.0.realization 0.6 0.3 0.9 -111,742 275,473 387,215 0.47
build benefits.0.minutes_saved 6 3 8 -111,742 210,937 322,679 4.73
build monthly_costs.1.amount 3000 2000 8000 115,746 -87,537 203,282 5,416
build one_time_costs.0.amount 100000 80000 200000 105,676 -37,185 142,861 168,765
build benefits.0.hourly_cost 55 45 65 11,463 152,268 140,806 -
build go_live_month 4 3 8 98,041 18,432 79,609 -
build benefits.1.days_faster 0.2 0.05 0.5 62,191 121,214 59,023 -
build per_item_costs.0.amount 0.02 0.01 0.1 83,626 67,785 15,841 -
Base NPV: build 81,866, standard 92,925
switching value = the value of that input at which NPV reaches zero; '-' means NPV keeps its sign
between low and high.
Read it from the top. The rows are sorted by swing, widest first:
Realization and minutes saved decide the case. If only 47% of freed time is used, or clerks save under 4.73 minutes, the build loses money. These are the two numbers to protect after go-live.
Support cost is the third. If support grows from 3,000 to about 5,400 EUR a month, NPV reaches zero. Running costs are a quiet killer.
A late go-live hurts but doesn't flip it within the tested range: going live in month 8 instead of 4 still leaves 18,432 EUR.
Token price barely matters. Five times the token price moves NPV by under 16,000 EUR.
An empty result (- in the last column) is valid: it means the decision holds across the whole range you tested.
The build option's NPV falls from about 82,000 to about 19,000 EUR, and payback moves to month 30, close to the end of the horizon. The standard option loses less, because it has less cost to inflate.
Put the value back to 0.2 and save.
This is the conversation to have before approval, not after: which option still stands when costs are worse than planned, and by how much?
The sample includes six made-up months of actuals for the build option, months 4 to 9.
Run:
python unit10/business_case.py track
You should see:
Plan vs actual for 'build': clerk time saved and run cost. EUR
month used plan used act. min plan min act. value plan value act. cost plan cost act.
4 30% 22% 6.0 5.1 5,940 3,579 6,036 6,200
5 60% 41% 6.0 5.6 11,880 7,703 6,072 6,300
6 80% 55% 6.0 5.8 15,840 11,229 6,096 6,500
7 90% 63% 6.0 6.1 17,820 12,471 6,108 6,200
8 90% 66% 6.0 6.0 17,820 13,177 6,108 6,300
9 90% 70% 6.0 6.0 17,820 14,322 6,108 6,400
Cumulative time value: 62,480 actual vs 87,120 plan (72% of plan).
Plan values here are before the confidence factor: you are checking the unadjusted promise.
Behind plan. Find out whether usage or minutes saved is short, and fix that first.
Read the gap. Minutes saved reached the plan's 6.0 by month 7. Usage is the problem: 70% of orders by month 9 against a plan of 90%. The fix is adoption, such as training, putting the assistant where clerks already work, or finding out why some teams don't use it. A better model would not help.
As of October 2026, SAP's tools cover three parts of the case: finding candidates, estimating the cost of SAP features, and tracking that cost. The benefit evidence is still yours.
SAP Business AI Feature Catalog (SAP Discovery Center). SAP Learning describes it as listing embedded AI features with their use cases, value and metrics. An SAP Learning course for enterprise architects adds that it includes "Value 3-Pager" content for the value narrative.
SAP Business AI value calculator (sap.com). You enter line of business, industry, number of employees, annual revenue and currency. It returns an "expected annual EBIT increase", split into productivity and effectiveness, and the top use cases by value. SAP states the results are estimates, not a quote or an offer.
Cost and ROI estimator tools in SAP Discovery Center. SAP News (June 2026) says they take details such as customizations, hyperscalers and number of users.
SAP explains that its value estimates rest on its industry expertise, benchmarking data, third-party research and early customer feedback, and weigh efficiency and effectiveness. That maps onto this topic's benefit kinds: time is efficiency, per-item effects are effectiveness. Put SAP's figure in the evidence field with a low confidence until your pilot replaces it.
Base AI is included in standard cloud subscriptions at no additional cost
A base feature's per-item cost is zero; its costs are configuration, training and change
Premium AI is powered by AI Units, priced per user per month or by consumption
Per-user packages are a monthly cost; consumption pricing is a per-item cost
AI Units are bought annually and expire after 12 months if unused
Buy close to the forecast; unused units are a cost with no benefit
AI Units can be used across SAP solutions
One pool can serve several use cases, which helps when one ramps up slower than planned
Example rates: Joule Premium from 8 to 1 AI Units per user per month by volume; Document Grounding 0.005 AI Units per record
Use these to check that an estimate's order of magnitude is plausible, not as your price
To turn this into a per-item cost:
Open SAP Discovery Center and the SAP Business AI Feature Estimator ("My AI Feature Estimates"). SAP Learning says it estimates the AI Units a feature needs so you can budget.
Read the AI Features Guide Price List that SAP Learning points to. It holds the conversion factors that turn business usage (documents, records, requests) into AI Units.
Request a quote. SAP Learning says consumed units are priced in increments of 100, at the standard list price at the contract start, with negotiated discounts applied.
Divide the yearly cost by the yearly volume to get the per_item_costs value for the standard option.
For a custom build on SAP AI Core, the per-item cost comes from Token economics: tokens converted to capacity units, plus the services around the model. Joule Studio and SAP Document AI are paid through SAP BTP credits, according to the same pricing page.
SAP Learning's course on selling SAP Business AI says SAP for Me shows the AI Unit balance and consumption, a monthly consumption graph, usage by product and feature updated hourly, and downloadable balance statements. That is the actual cost column for a premium feature in your track file. For custom builds on SAP BTP, use the BTP cockpit's usage view and the budget alerts described in Token economics.
SAP Signavio's value case creation agent drafts value cases from process mining insights. Its product page says it identifies inefficiencies such as rework or delays, estimates financial impact using configurable cost and effort parameters, and produces editable drafts with problem summaries, root causes and expected benefits. It is activated with AI Units. When checked on 7 October 2026, the page offered registration for beta testing, so treat it as not generally available until SAP says otherwise. If your company runs SAP Signavio, the same process data is also your best source of baselines, as Defining success metrics with the business shows.
Ownership. Name a business owner for each benefit line and a technical owner for each cost line. The process owner protects the switching values; the platform team watches cost per item.
Data in the case. A business case can contain salaries, customer names and volumes. Use loaded hourly rates by role, not personal salaries, and store the case file where only the project and finance can read it. Never commit real case files to a public repository.
Security and authorization costs are real costs. Authorization design, penetration tests and data protection reviews belong in one-time costs. Grounding on SAP data with authorizations shows why they take time; Unit 11 covers more.
Stage gates. Fund in stages: pilot, first team, all teams. Rerun the case at each gate with the new actuals, and write down beforehand which result stops the project.
Cost drift. Running cost grows quietly: more prompts, more tools, a larger model, more support. Track cost per item monthly next to benefit per item.
AI Unit planning. Forecast AI Units from the adoption ramp, not from full adoption on day one. SAP for Me shows when consumption runs ahead of or behind plan.
Clean core. Count the cost of keeping custom code upgrade-safe. Side-by-side builds on released APIs (CAP and side-by-side extensions) cost less to maintain than modifications.
Model and price changes. Model prices and SAP AI Core model availability change. Keep rates in the case file with the date and source, and rerun the case when they change.
Counting hours as headcount. Promising fewer people from hours saved sets the case up to fail. Name where the freed time goes and use a realization share.
One option. A case without "do nothing" and "use what SAP ships" invites the first question you can't answer.
Pilot numbers at full scale on day one. Adoption ramps. Plan the ramp and the fixed costs that run during it.
Forgetting running people. Support, prompt and model updates, evaluation, and security reviews continue every month.
Precision theatre. An NPV of 81,866 EUR is not more correct than "about 80,000". Round in the summary; keep the precision in the file.
Hiding uncertainty. Confidence factors and switching values make a case more credible, not less.
Mixing up ROI and NPV. Rank options by NPV and coverage; use ROI to compare options of similar size.
No tracking plan. Without monthly actuals, the case is never tested and the next one is less likely to be believed.
Open unit10/case/my_case.json. Change the name to your use case.
In the build option, set per_item_costs from the cost per order in your unit10/cost-model.md (from Token economics). If you didn't do that exercise, keep 0.02.
Set the minutes_saved and evidence of the time line from your unit08/results/metric_agreement.md or your own estimate. Set its confidence to 0.8 if it was measured in a pilot, 0.5 if estimated by the process owner, or 0.3 if it is a guess.
Look up one SAP feature for your use case in the SAP Business AI Feature Catalog. If one fits, keep the standard option and write the feature name and the catalog date in its title. If none fits, delete the standard option, including its curly braces, and the comma before it.
Run:
python unit10/business_case.py run --case unit10/case/my_case.json
python unit10/business_case.py sensitivity --case unit10/case/my_case.json
Open unit10/case/business_case.md and replace the line under "Decision asked for" with one sentence: the option you recommend, the budget, and the gate where it will be reviewed. Below it, add one sentence naming the input with the widest swing and its switching value.
Save your work:
git add unit10/case/my_case.json unit10/case/business_case.md
git commit -m "Add business case for my use case"
Done whenrun prints NPV and payback for your options, sensitivity prints at least five rows, and business_case.md holds your decision sentence and the most important switching value. Keep it: the opportunity assessment in Unit 14 and the portfolio projects in Unit 15 start from a case like this one.
Pick one answer for each question. The explanation appears after you choose.
1In the sample, clerks release blocked orders 0.2 days faster. Why is the working-capital benefit only about 1,788 EUR a month?
Answer: B. Releasing orders 0.2 days faster frees about 268,000 EUR of cash, once. The recurring value is what that cash would cost to finance, at 8% a year about 1,788 EUR a month, before confidence. Confidence and discounting reduce it further, but the formula already makes it small.
2How does monthly_flows() treat fixed monthly costs and per-item costs differently during the adoption ramp?
Answer: D. Per-item costs are multiplied by items times adoption, so they grow with use. Fixed monthly costs start at their from_month in full. That is why every AI case loses money in its first months.
3The sensitivity table shows realization with a switching value of 0.47. What does that tell the process owner?
Answer: C. A switching value is the input value at which NPV reaches zero. Here, if less than 47% of the freed clerk time goes to useful work, the build option loses money. It is a level to protect, not a probability or a usage target.
4The build option has a lower ROI and NPV than the standard option, yet creates about twice the benefit. How should you present this?
Answer: B. NPV and ROI favor the standard option because it costs much less, while the build covers all block reasons. The decision is whether the extra coverage justifies the extra money, which is a business judgement. Hiding either option takes that choice away from the sponsor.
5You must fill in per_item_costs for an SAP premium feature. Which path matches the SAP tools described in this topic?
Answer: D. The Feature Estimator estimates AI Units for a feature, the price list holds conversion factors, and the quote sets the price. Dividing yearly cost by yearly volume gives the per-item value. The value calculator estimates benefits, not cost, and SAP for Me only shows consumption after you buy.
6Six months after go-live, track shows minutes saved at plan but usage at 70% instead of 90%. What would you do?
Answer: C. The assistant delivers the planned minutes when it is used, so the gap is usage. The fix is adoption: training, placing the assistant where clerks work, and finding out why some teams don't use it. A larger model would raise cost without addressing the gap, and changing the plan hides it.
7Why does the case keep realization, confidence and cost contingency as three separate inputs instead of one overall haircut?
Answer: A. Realization asks whether a saving becomes value, confidence asks how sure the saving is, and contingency corrects optimistic costs. Keeping them separate lets a reviewer challenge each one, and lets sensitivity analysis test each. A single haircut hides which assumption is weak.
8Your case file contains loaded hourly rates, customer volumes and a cancellation estimate from sales. What is the right production practice?
Answer: D. Business cases hold sensitive commercial data. Rates by role are accurate enough and avoid personal salary data. Real case files belong in an access-controlled location, never in a public repository.
Ask a question
Testing: only staff see this
Stuck on something in this layer? Ask it here. Questions are answered in the order they arrive, and the answer appears under My questions.
Sign in (free) to ask a question. You can ask anonymously.
Sources
Discovering SAP Business AI Feature Catalog and SAP Business AI Feature Estimator (SAP Learning)— Feature Catalog lists embedded AI features with use cases, value and metrics; Feature Estimator ('My AI Feature Estimates') estimates AI Units needed per feature for budgeting; conversion factors turn business metric usage into AI Units; consumed units priced in increments of 100 at the list price at contract start, with negotiated discounts; AI Features Guide Price List holds eligible services and conversion factors
SAP Business AI packages and pricing (sap.com)— base AI included in standard cloud subscriptions at no additional cost; premium AI powered by AI Units, per user per month or consumption-based; AI Units purchased annually, expire after 12 months if unused, usable across SAP solutions; Joule Premium tiers from 8 to 1 AI Units per user per month; Document Grounding 0.005 AI Units per record; Joule Studio and SAP Document AI through SAP BTP credits
SAP Business AI value calculator (sap.com)— inputs line of business, industry, employees, revenue and currency; outputs expected annual EBIT increase split into productivity and effectiveness, and top use cases by value; results are estimates, not an offer
Value case creation agent (SAP Signavio, sap.com)— turns process mining insights into editable value case drafts with problem summary, root causes and expected benefits, using configurable cost and effort parameters; activated with AI Units; page offered beta registration when checked on 7 October 2026
The Green Book 2026 (HM Treasury)— optimism bias as the tendency of appraisals to be over-optimistic; sensitivity analysis and switching values; discounting to a net present value; risk costs; five case model; benefits realisation planned in the management case