Orchestrate

The AI business case

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.

Updated Oct 7, 2026Foundational 9 minDeep 40 min
Foundational layer · 9 min read

The 60-second version

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:

  1. Compares options. Doing nothing, switching on a standard SAP feature, and building something custom are all options. "Build or not" is too narrow.
  2. Counts the full cost. Model tokens are usually a small part. People, platform, security reviews, training and support are most of it.
  3. Discounts benefits by how sure you are. A saving measured in a pilot is worth more than one a vendor estimated.
  4. Names the assumption that would flip the answer. For example: "if clerks save less than 4.7 minutes per order, this loses money."
  5. 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.

Why it matters to the business

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.

How SAP does 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.

A business case on one page

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.

Questions to ask

  • What are the options, including doing nothing and a standard SAP feature? Why was each rejected or kept?
  • Where does each benefit number come from: our own pilot, our system data, or a vendor estimate?
  • Who will use the freed time, and for what? Is that in someone's plan?
  • What does it cost to run per month, including people? What does it cost per order handled?
  • Which assumption, if wrong, turns the case negative? How likely is that?
  • For SAP premium features: what does the Feature Estimator say, what does the quote say, and what happens to unused AI Units at the end of the year?
  • Who owns tracking the benefits after go-live, how often, and what is the stop or fix trigger?

Common misconceptions

  • "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.

Key terms

  • 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.

Check yourself

Pick one answer for each question. The explanation appears after you choose.
  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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.
  6. 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.
  7. 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.
Deep layer · 40 min read

Mental model

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.

How it works

Start from options, not from the solution

List at least three options for every case:

  1. Do nothing. The baseline. Every other option is measured against it, so its NPV is zero by definition.
  2. 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.
  3. 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.

Benefit lines: three kinds, each with evidence

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.

Confidence, realization and optimism bias

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.

Adoption is a ramp, not a switch

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.

Full cost: tokens are the small part

Cost One-time or running Sample value
Build team One-time 100,000 EUR
Security review and authorization design One-time 20,000 EUR
Training and change management One-time 15,000 EUR
Platform (runtime, AI Core, vector store) Monthly 2,500 EUR
Support and model updates Monthly 3,000 EUR
Evaluation and monitoring Monthly 500 EUR
Model tokens Per order 0.02 EUR

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.

Time value: NPV, payback and ROI

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.

Sensitivity and switching values

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

Benefits tracking closes the loop

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.

Build it yourself: a business case calculator

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]

What you need

  • Your course folder with .venv from the setup topics.
  • About 45 to 60 minutes.
  • Cost: free. No model is called, no account is needed, and no network connection is used.

Step 1: Open your course folder and turn on the virtual environment

  1. Open VS Code, choose File > Open Folder, and open orchestrate-course.

  2. Open a terminal: Terminal > New Terminal.

  3. If the prompt doesn't start with (.venv), turn it on:

    • Windows (PowerShell):

      .venv\Scripts\Activate.ps1
    • macOS / Linux:

      source .venv/bin/activate
  4. Check your Python version (the same command on every system):

    python --version
    Python 3.13.16

    Any version from 3.10 up works.

Step 2: Create the script

  1. 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()

Step 3: Write the sample case

  1. Run:

    python unit10/business_case.py init
  2. You should see:

    Wrote unit10/case/blocked_orders_case.json and unit10/case/actuals.csv
    All numbers are made up. Next: python unit10/business_case.py run
  3. 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.

Step 4: Run the case

  1. Run:

    python unit10/business_case.py run
  2. You should see:

    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
  3. 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.
  4. 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.

Step 5: Find the assumptions that matter

  1. Run:

    python unit10/business_case.py sensitivity
  2. You should see:

    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.
  3. 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.

Step 6: Make the case more cautious

Reviewers often ask: "what if you're too optimistic about everything?"

  1. Open unit10/case/blocked_orders_case.json. Change "cost_contingency": 0.2 to 0.4 and save.

  2. Run python unit10/business_case.py run again.

  3. You should see:

    option         PV benefits      PV costs           NPV    ROI   payback   cost/item
    build              457,449       438,181        19,268     4%  month 30        1.58
    standard           226,688       156,057        70,632    45%  month 19        0.47

    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.

  4. 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?

Step 7: Track benefits after go-live

The sample includes six made-up months of actuals for the build option, months 4 to 9.

  1. Run:

    python unit10/business_case.py track
  2. 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.
  3. 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.

Step 8: Save your work

  1. Commit the script and the case:

    git add unit10/business_case.py unit10/case/
    git commit -m "Add business case calculator and sample case"

How the code works

Part What it does
SAMPLE_CASE, SAMPLE_ACTUALS The made-up case and six months of actuals that init writes to unit10/case/
adoption() Returns the share of items handled with the solution in a month, from the go-live month and the ramp
benefit_per_month() The formula for each benefit kind: time, per_item, working_capital or a fixed monthly amount
monthly_flows() Builds the month-by-month table: benefits times confidence and adoption, costs with contingency
summarize() Discounts each month to today and returns NPV, PV of benefits and costs, ROI, payback month and cost per item
get_path(), set_path(), npv_with() Read or change one input by its path, such as benefits.0.minutes_saved, and recompute NPV
switching_value() Halves the range between low and high until it finds where NPV crosses zero (bisection)
cmd_run(), cmd_sensitivity(), cmd_track() The three commands; run also writes business_case.md
load_case() Reads the case file and stops with a plain message if it is missing, not valid JSON or lacks a confidence

If something goes wrong

What you see What it means What to do
python is not recognized, or command not found Python isn't installed or isn't on the path Follow Set up your computer for this course again; on macOS or Linux try python3
ModuleNotFoundError You are running a different file, since this script uses only built-in modules Check the file name and that you saved the code in unit10/business_case.py
Case file not found You haven't run init, or you are in the wrong folder Run python unit10/business_case.py init from the orchestrate-course folder
... is not valid JSON: line 12, column 5 An edit broke the JSON, often a missing comma or quote Open the file at that line; or run init again to start over (it overwrites your edits)
needs a confidence between 0 and 1 A benefit line has no confidence or one above 1 Add "confidence": 0.5 (or your value) to that line
Asked for an API key, or a network or proxy error Not from this script: it uses no key and no network Check that you are running business_case.py, not an earlier script that calls a model
.venv\Scripts\Activate.ps1 cannot be loaded on Windows PowerShell blocks scripts by default Run Set-ExecutionPolicy -Scope CurrentUser RemoteSigned, answer Y, then activate again

The SAP way

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.

Finding candidates and value estimates

  • 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.

Estimating the cost of SAP premium features

SAP's pricing page, as of October 2026:

Fact What it means for the case
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:

  1. 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.
  2. 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.
  3. 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.
  4. 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.

Tracking cost after go-live

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.

Drafting value cases from process data

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.

Build vs. SAP

Situation Lean towards Why
A base SAP AI feature fits the use case Switch it on, track adoption and time saved No license cost; the case is mostly about change management
A premium SAP feature fits most of the volume Feature Estimator, then a quote, then a pilot Supported and upgrade-safe; the case turns on AI Units per item and coverage
No SAP feature fits, or the logic is company-specific Build side by side and carry the full running cost Coverage you can't buy, at the price of a team to run it
A standard feature covers part, a build covers all Compare both as options; consider buy first, build the gap The extra coverage must be worth its extra cost; switching values show whether it is
Value depends on a benefit with low confidence Pilot to measure it before the full case A 0.3-confidence line shouldn't carry an investment decision
Several SAP AI use cases are planned Model them together for AI Units Units are bought annually, expire after 12 months and are shared across solutions

Production concerns

  • 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.

Pitfalls

  • 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.

Exercise

Turn your own earlier results into a business case.

  1. Copy the sample: in the terminal, run the command for your system.

    • Windows (PowerShell):

      Copy-Item unit10\case\blocked_orders_case.json unit10\case\my_case.json
    • macOS / Linux:

      cp unit10/case/blocked_orders_case.json unit10/case/my_case.json
  2. Open unit10/case/my_case.json. Change the name to your use case.

  3. 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.

  4. 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.

  5. 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.

  6. 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
  7. 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.

  8. 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 when run 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.

Check yourself

Pick one answer for each question. The explanation appears after you choose.
  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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.
  6. 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.
  7. 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.
  8. 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.

Sources

Sign in to track your progress

We'll email you a one-time sign-in link. No password needed.

or

Tell us a little about you

Optional, every field. It helps us pitch answers to your questions at the right level and decide which topics to write next. It is never shown publicly, and you can change or clear it anytime from the account menu.

SAP areas you work in