O2C Process Optimization Dashboard

Generate synthetic ERP data and build a Power BI dashboard to find process bottlenecks.

Introduction

30 Second Summary

Every business runs on a simple cycle: sell something, deliver it, and get paid. When any part of that cycle slows down, cash gets stuck in the pipeline and the business feels the squeeze.

In this project, you will generate 50,000+ rows of realistic order data using Python, load it into Power BI Desktop, and build an interactive dashboard that pinpoints exactly where delays are hiding in the payment cycle.

What You'll Build

A complete Order-to-Cash process analytics portfolio piece that uses synthetic ERP data, DAX cycle time calculations, and Power BI's AI-powered visuals to isolate bottlenecks and quantify their financial impact.

By the end of this project, you'll have:

  • A Python data generation script that produces 50,000+ realistic order records with intentional bottlenecks baked into specific regions and product lines.
  • A multi-page Power BI dashboard with KPI cards, stage breakdowns, and AI-driven root-cause analysis using the Decomposition Tree and Key Influencers visuals.
  • A structured business impact write-up that quantifies cash flow impact and recommends automation interventions for stakeholders.
  • Secret Mission: Build a Cash Flow Impact Simulator page that lets stakeholders adjust bottleneck parameters with slicers and see projected cash freed up in real-time.

Are there any prerequisites?

You need basic Python familiarity (enough to run a script) and a general understanding of business processes. No prior Power BI or DAX experience is required.

Before We Start

Every company has an Order-to-Cash (O2C) process. It starts when a customer places an order and ends when payment lands in the bank. Delays at any stage tie up working capital and frustrate customers.

In this step, you will install the three tools this project depends on: Python (to generate a realistic dataset), Power BI Desktop (to build the analytics dashboard), and VS Code (to write and run your script).

In this step, get ready to:
  • Install Python 3.10+ and the required data generation libraries.
  • Install Power BI Desktop on Windows.
  • Create your project folder and data generation file in VS Code.
Install Python and required packages

Python powers the data generation script that produces 55,000 realistic O2C records. You need version 3.10 or higher installed on your machine.

  • Press the Windows key to open your search bar.
  • Type cmd and press Enter to open Command Prompt.
  • Check your Python version by running this command:
python --version

✔️ I see version 3.10 or higher

Python is ready. Move on to installing the required libraries below.

ⓧ I see an older version

You have Python installed, but it is too old for this project. You need 3.10 or higher.

  • Open your browser and go to python.org/downloads.
  • Download the latest Python installer for Windows (64-bit).
  • Run the installer. On the first screen, check the box labelled Add python.exe to PATH, then click Install Now.
  • After installation completes, close and reopen Command Prompt.
  • Run python --version again to confirm it shows 3.10 or higher.

Still seeing the old version?

If the old version still appears after reinstalling, the old Python path may take priority. Uninstall the older version from Settings > Apps, then restart Command Prompt and try again.

help me fix my Python version on Windows

ⓧ Command not found

Python is not installed yet. Follow these steps to install it.

  • Open your browser and go to python.org/downloads.
  • Click the large download button for the latest Python release (Windows installer, 64-bit).
  • Run the downloaded installer. On the first screen, check the box labelled Add python.exe to PATH, then click Install Now.
  • Wait for the installation to complete, then close the installer.
  • Close and reopen Command Prompt.
  • Run python --version to confirm it shows 3.10 or higher.

Python still not recognized?

If Command Prompt still does not recognize python, make sure you checked Add python.exe to PATH during installation. You can re-run the installer, select Modify, and ensure the PATH option is enabled.

help me get Python recognized in my terminal

  • Install the three libraries this project needs by running this command:
pip install faker pandas numpy

What does this command do?

This installs three Python packages. faker generates realistic fake data (customer IDs, dates). pandas handles tabular data and CSV export. numpy provides statistical distributions for realistic delay patterns.

  • Verify the installation by running:
pip show faker

You should see package details including the name, version, and install location printed in your terminal.

Seeing a permission error?

If you see a permission denied error, try running the install with the --user flag:

pip install --user faker pandas numpy

help me install Python packages on Windows

Install Power BI Desktop

Power BI Desktop is the free analytics tool you will use to build your interactive dashboard. It is a Windows-only application.

What is Power BI Desktop?

Power BI Desktop is a free data visualization tool from Microsoft. You connect it to data sources, create calculated columns and measures using DAX, and build interactive reports. No paid license or account is needed for local authoring.

  • Open the Microsoft Store app on your Windows machine (press the Windows key, type Microsoft Store, and press Enter).
  • Search for Power BI Desktop in the Store search bar.
  • Click Install (or Get) on the Power BI Desktop page.
  • Wait for the download and installation to complete.

Microsoft Store blocked?

If your organization blocks the Microsoft Store, download the standalone 64-bit installer directly from the Microsoft Download Center. Run the .exe file and follow the setup wizard.

  • Once installed, press the Windows key, type Power BI Desktop, and press Enter to launch it.

✔️ It opens successfully

You should see the Power BI Desktop start screen with options like Get Data and recent reports. Power BI is ready.

  • Close Power BI Desktop for now. You will return to it in a later step.

ⓧ It did not open or I cannot find it

If Power BI Desktop does not appear in search, the installation may not have completed.

  • Reopen the Microsoft Store and check if the install is still in progress.
  • If the Store shows Open, click it to launch Power BI Desktop.
  • If it still fails, download the standalone installer from the Microsoft Download Center and run the .exe installer directly.

help me install Power BI Desktop on Windows

Why Power BI Desktop for this project?

Power BI Desktop includes built-in AI visuals (Key Influencers and Decomposition Tree) that are purpose-built for root-cause analysis. These visuals use machine learning behind the scenes to automatically identify which factors drive process delays. No other free BI tool offers this combination without plugins.

Set up your project folder

You need a dedicated folder to hold your Python script and the CSV dataset it generates. You will open this folder in VS Code to write the script.

  • Press the Windows key, type VS Code, and press Enter to open Visual Studio Code.

Don't have VS Code installed?

Download VS Code for free from code.visualstudio.com. Run the installer and accept the defaults. After installation, relaunch it from the Windows search bar.

  • Open the built-in terminal in VS Code by pressing Ctrl+` (the backtick key, above Tab).
  • Move to your Desktop by running this command:
cd ~/Desktop
  • Create your project folder and move into it by running these commands:
mkdir o2c-optimization
cd o2c-optimization
  • Create the empty Python file for your data generation script by running:
type nul > generate_o2c_data.py
  • Confirm the file exists by running:
dir

You should see generate_o2c_data.py listed in the output.

  • Now open the project folder in VS Code by clicking File in the top menu bar.
  • Click Open Folder.
  • Navigate to your Desktop, select the o2c-optimization folder, and click Select Folder.

VS Code reloads with o2c-optimization as the workspace. You should see generate_o2c_data.py listed in the Explorer sidebar on the left.

Don't see the file in the sidebar?

Make sure you selected the o2c-optimization folder itself, not a parent folder. The Explorer sidebar should show the folder name at the top with generate_o2c_data.py listed directly below it.

help me open my project folder in VS Code

Your environment is ready. Next up, you will write the Python script that generates 55,000 rows of realistic O2C process data with intentional bottlenecks baked in.

Generate the Synthetic O2C Dataset

Your environment is ready with Python, the required libraries, and Power BI Desktop standing by. Now it's time to create the operational data you'll analyze.

Real O2C process data lives inside ERP systems that can take months to get access to. You'll generate your own realistic synthetic dataset using Python, producing 55,000 order records with intentional bottlenecks hidden inside for you to discover later in Power BI.

In this step, get ready to:
  • Set up configuration constants and a customer pool for 55,000 O2C order records.
  • Write a function that generates realistic order records with embedded process bottlenecks.
  • Run the script and validate that the bottlenecks appear in the output statistics.
Set up the script configuration

Before writing the generation logic, you need to define the constants that control your dataset. These include the regions, product lines, payment terms, and a pool of reusable customer IDs.

  • Switch to VS Code where your generate_o2c_data.py file is open.
  • Paste the following imports and configuration block at the top of the file:
import pandas as pd
import numpy as np
from faker import Faker
from datetime import timedelta, date

fake = Faker()
Faker.seed(42)
np.random.seed(42)

# Configuration
NUM_ORDERS = 55000
REGIONS = ["North America", "EMEA", "APAC", "LATAM"]
PRODUCT_LINES = ["Standard Goods", "Digital Services", "Custom Manufacturing", "Spare Parts"]
PAYMENT_TERMS = ["Net 30", "Net 45", "Net 60", "Net 90"]
ORDER_STATUSES = ["Completed", "Completed", "Completed", "Completed", "Completed",
                  "Completed", "Completed", "Cancelled", "Disputed", "In Progress"]

# Generate customer pool
NUM_CUSTOMERS = 2000
customer_ids = [f"CUST-{fake.unique.random_int(min=10000, max=99999)}" for _ in range(NUM_CUSTOMERS)]

What does this code do?

The imports bring in pandas for DataFrames, numpy for statistical distributions, Faker for realistic IDs and dates, and timedelta for date arithmetic.

Setting seeds with Faker.seed(42) and np.random.seed(42) makes the output reproducible. Every time you run the script, you get the same dataset.

The ORDER_STATUSES list is weighted by repetition. "Completed" appears 7 out of 10 times, giving a 70% completion rate.

The customer pool of 2,000 unique IDs is generated up front so multiple orders can share the same customer, mimicking real ERP data.

  • Save generate_o2c_data.py.

You won't see a change yet. The next chunk builds the function that uses these constants.

Write the order generation function with bottlenecks

Each order record simulates the full O2C lifecycle: order placement, credit check, warehouse pick, shipping, invoicing, and payment collection. You'll embed three intentional bottlenecks that your Power BI dashboard will later uncover.

  • In generate_o2c_data.py, paste the following function below the customer_ids line:
def generate_order(order_num):
    """Generate a single O2C order record with realistic stage dates."""
    order_id = f"ORD-{100000 + order_num}"
    customer_id = np.random.choice(customer_ids)
    region = np.random.choice(REGIONS, p=[0.35, 0.30, 0.20, 0.15])
    product_line = np.random.choice(PRODUCT_LINES, p=[0.40, 0.25, 0.20, 0.15])
    payment_terms = np.random.choice(PAYMENT_TERMS, p=[0.40, 0.30, 0.20, 0.10])
    order_status = np.random.choice(ORDER_STATUSES)

    order_date = fake.date_between(start_date=date(2023, 1, 1), end_date=date(2024, 12, 31))
    order_quarter = (order_date.month - 1) // 3 + 1

    if product_line == "Custom Manufacturing":
        order_value = round(np.random.uniform(5000, 75000), 2)
    elif product_line == "Digital Services":
        order_value = round(np.random.uniform(500, 15000), 2)
    elif product_line == "Spare Parts":
        order_value = round(np.random.uniform(100, 5000), 2)
    else:
        order_value = round(np.random.uniform(1000, 30000), 2)

    if region == "LATAM":
        credit_days = max(1, int(np.random.exponential(15)))
    else:
        credit_days = max(1, int(np.random.exponential(3)))

    credit_check_date = order_date + timedelta(days=credit_days)

    if product_line == "Custom Manufacturing":
        pick_days = max(1, int(np.random.exponential(9)))
    else:
        pick_days = max(1, int(np.random.exponential(3)))

    warehouse_pick_date = credit_check_date + timedelta(days=pick_days)

    ship_days = max(1, int(np.random.exponential(3)))
    ship_date = warehouse_pick_date + timedelta(days=ship_days)

    invoice_days = max(1, int(np.random.exponential(2)))
    invoice_date = ship_date + timedelta(days=invoice_days)

    base_payment_days = {"Net 30": 30, "Net 45": 45, "Net 60": 60, "Net 90": 90}
    payment_base = base_payment_days[payment_terms]

    if order_quarter == 4:
        payment_variation = int(np.random.normal(payment_base * 1.5, 15))
    else:
        payment_variation = int(np.random.normal(payment_base * 0.8, 10))

    payment_days = max(5, payment_variation)
    payment_received_date = invoice_date + timedelta(days=payment_days)

    if order_status == "Cancelled":
        warehouse_pick_date = None
        ship_date = None
        invoice_date = None
        payment_received_date = None
    elif order_status == "In Progress":
        payment_received_date = None

    return {
        "Order_ID": order_id,
        "Customer_ID": customer_id,
        "Product_Line": product_line,
        "Region": region,
        "Order_Date": order_date,
        "Credit_Check_Date": credit_check_date,
        "Warehouse_Pick_Date": warehouse_pick_date,
        "Ship_Date": ship_date,
        "Invoice_Date": invoice_date,
        "Payment_Received_Date": payment_received_date,
        "Order_Value": order_value,
        "Order_Status": order_status,
        "Payment_Terms": payment_terms,
    }

Where are the three bottlenecks?

The function uses exponential distributions to simulate realistic service times. The bottlenecks are embedded as conditional multipliers:

  • Region bottleneck: LATAM orders use np.random.exponential(15) for credit check days, while all other regions use np.random.exponential(3). This creates a 5x average delay.
  • Product line bottleneck: Custom Manufacturing uses np.random.exponential(9) for warehouse pick days versus np.random.exponential(3) for other products. This creates a 3x average delay.
  • Seasonal bottleneck: Q4 orders (October through December) multiply the payment base by 1.5 instead of 0.8, creating 2x longer collection times.

Cancelled orders have None for later stages since they never progressed. In Progress orders are missing only the final payment date.

Why exponential distributions?

In operations research, service times (how long a process step takes) typically follow an exponential distribution. Most orders clear quickly, but a long tail of slow ones drags up the average. This matches what you'd see in real ERP data.

  • Save generate_o2c_data.py.

You won't see output yet. The next section adds the code that runs this function 55,000 times and exports the results.

Run the script and validate the output

With the generation function complete, you need code that calls it for every order, exports the results to CSV, and prints validation statistics proving the bottlenecks exist.

  • In generate_o2c_data.py, paste the following execution block below the generate_order function:
print("Generating 55,000 O2C order records...")
orders = [generate_order(i) for i in range(NUM_ORDERS)]
df = pd.DataFrame(orders)

df.to_csv("o2c_orders.csv", index=False)
print(f"Dataset exported: {len(df)} rows")
print(f"\nDataset shape: {df.shape}")
print(f"\nOrder Status distribution:")
print(df["Order_Status"].value_counts())

What does this code do?

The list comprehension calls generate_order() 55,000 times, building a list of dictionaries. pd.DataFrame(orders) converts that list into a tabular DataFrame.

The to_csv() call writes the entire dataset to o2c_orders.csv in your project folder. Power BI will import this file in the next step.

  • Now paste the bottleneck validation block directly below:
completed = df[df["Order_Status"] == "Completed"].copy()
completed["Credit_Check_Days"] = (pd.to_datetime(completed["Credit_Check_Date"]) - pd.to_datetime(completed["Order_Date"])).dt.days
completed["Pick_Days"] = (pd.to_datetime(completed["Warehouse_Pick_Date"]) - pd.to_datetime(completed["Credit_Check_Date"])).dt.days

print("\n--- BOTTLENECK VALIDATION ---")
print("\nAvg Credit Check Days by Region:")
print(completed.groupby("Region")["Credit_Check_Days"].mean().round(1))
print("\nAvg Warehouse Pick Days by Product Line:")
print(completed.groupby("Product_Line")["Pick_Days"].mean().round(1))

completed["Payment_Days"] = (pd.to_datetime(completed["Payment_Received_Date"]) - pd.to_datetime(completed["Invoice_Date"])).dt.days
completed["Quarter"] = pd.to_datetime(completed["Order_Date"]).dt.quarter
print("\nAvg Payment Days by Quarter:")
print(completed.groupby("Quarter")["Payment_Days"].mean().round(1))

print("\nDataset generation complete! Open 'o2c_orders.csv' in Power BI Desktop.")

What does the validation section do?

It filters to only completed orders, then calculates the days between each process stage using pandas date arithmetic. The groupby() calls aggregate these calculated columns by Region, Product Line, and Quarter to confirm the bottlenecks appear.

You should see LATAM averaging around 15 days for credit checks, Custom Manufacturing averaging around 9 days for picks, and Q4 averaging roughly 1.5x the payment days of other quarters.

  • Save generate_o2c_data.py.
  • Open the integrated terminal in VS Code by pressing Ctrl+` (backtick key, top-left of keyboard).
  • Run the script by entering:
python generate_o2c_data.py

The script takes 10-30 seconds to generate all 55,000 records. You should see output confirming the row count, status distribution, and bottleneck averages.

What should I see?

Your terminal output should show the dataset exported with 55,000 rows, and the BOTTLENECK VALIDATION section should confirm:

  • LATAM credit check days averaging around 15, while other regions average around 3.
  • Custom Manufacturing pick days averaging around 9, while other product lines average around 3.
  • Q4 payment days noticeably higher than Q1-Q3.

Script not running?

  • If you see ModuleNotFoundError: No module named 'faker', make sure you installed the libraries in Step 1. Run pip install faker pandas numpy again.
  • If you see a SyntaxError, check that you pasted the code blocks in order: imports and config first, then the function, then the execution block.
  • Make sure your terminal is in the o2c-optimization folder. If not, run cd o2c-optimization first.

help me debug my generate_o2c_data.py script

  • Confirm the CSV file was created by running:
ls

You should see o2c_orders.csv alongside generate_o2c_data.py in the output.

✔️ Awesome, I've got everything!

Great. Double check you've saved your file and that o2c_orders.csv appears in your project folder.

ⓧ I'd like to double check the full code

Here is the complete generate_o2c_data.py file for comparison:

import pandas as pd
import numpy as np
from faker import Faker
from datetime import timedelta, date

fake = Faker()
Faker.seed(42)
np.random.seed(42)

# Configuration
NUM_ORDERS = 55000
REGIONS = ["North America", "EMEA", "APAC", "LATAM"]
PRODUCT_LINES = ["Standard Goods", "Digital Services", "Custom Manufacturing", "Spare Parts"]
PAYMENT_TERMS = ["Net 30", "Net 45", "Net 60", "Net 90"]
ORDER_STATUSES = ["Completed", "Completed", "Completed", "Completed", "Completed",
                  "Completed", "Completed", "Cancelled", "Disputed", "In Progress"]

# Generate customer pool
NUM_CUSTOMERS = 2000
customer_ids = [f"CUST-{fake.unique.random_int(min=10000, max=99999)}" for _ in range(NUM_CUSTOMERS)]

def generate_order(order_num):
    """Generate a single O2C order record with realistic stage dates."""
    order_id = f"ORD-{100000 + order_num}"
    customer_id = np.random.choice(customer_ids)
    region = np.random.choice(REGIONS, p=[0.35, 0.30, 0.20, 0.15])
    product_line = np.random.choice(PRODUCT_LINES, p=[0.40, 0.25, 0.20, 0.15])
    payment_terms = np.random.choice(PAYMENT_TERMS, p=[0.40, 0.30, 0.20, 0.10])
    order_status = np.random.choice(ORDER_STATUSES)

    order_date = fake.date_between(start_date=date(2023, 1, 1), end_date=date(2024, 12, 31))
    order_quarter = (order_date.month - 1) // 3 + 1

    if product_line == "Custom Manufacturing":
        order_value = round(np.random.uniform(5000, 75000), 2)
    elif product_line == "Digital Services":
        order_value = round(np.random.uniform(500, 15000), 2)
    elif product_line == "Spare Parts":
        order_value = round(np.random.uniform(100, 5000), 2)
    else:
        order_value = round(np.random.uniform(1000, 30000), 2)

    if region == "LATAM":
        credit_days = max(1, int(np.random.exponential(15)))
    else:
        credit_days = max(1, int(np.random.exponential(3)))

    credit_check_date = order_date + timedelta(days=credit_days)

    if product_line == "Custom Manufacturing":
        pick_days = max(1, int(np.random.exponential(9)))
    else:
        pick_days = max(1, int(np.random.exponential(3)))

    warehouse_pick_date = credit_check_date + timedelta(days=pick_days)

    ship_days = max(1, int(np.random.exponential(3)))
    ship_date = warehouse_pick_date + timedelta(days=ship_days)

    invoice_days = max(1, int(np.random.exponential(2)))
    invoice_date = ship_date + timedelta(days=invoice_days)

    base_payment_days = {"Net 30": 30, "Net 45": 45, "Net 60": 60, "Net 90": 90}
    payment_base = base_payment_days[payment_terms]

    if order_quarter == 4:
        payment_variation = int(np.random.normal(payment_base * 1.5, 15))
    else:
        payment_variation = int(np.random.normal(payment_base * 0.8, 10))

    payment_days = max(5, payment_variation)
    payment_received_date = invoice_date + timedelta(days=payment_days)

    if order_status == "Cancelled":
        warehouse_pick_date = None
        ship_date = None
        invoice_date = None
        payment_received_date = None
    elif order_status == "In Progress":
        payment_received_date = None

    return {
        "Order_ID": order_id,
        "Customer_ID": customer_id,
        "Product_Line": product_line,
        "Region": region,
        "Order_Date": order_date,
        "Credit_Check_Date": credit_check_date,
        "Warehouse_Pick_Date": warehouse_pick_date,
        "Ship_Date": ship_date,
        "Invoice_Date": invoice_date,
        "Payment_Received_Date": payment_received_date,
        "Order_Value": order_value,
        "Order_Status": order_status,
        "Payment_Terms": payment_terms,
    }

print("Generating 55,000 O2C order records...")
orders = [generate_order(i) for i in range(NUM_ORDERS)]
df = pd.DataFrame(orders)

df.to_csv("o2c_orders.csv", index=False)
print(f"Dataset exported: {len(df)} rows")
print(f"\nDataset shape: {df.shape}")
print(f"\nOrder Status distribution:")
print(df["Order_Status"].value_counts())

completed = df[df["Order_Status"] == "Completed"].copy()
completed["Credit_Check_Days"] = (pd.to_datetime(completed["Credit_Check_Date"]) - pd.to_datetime(completed["Order_Date"])).dt.days
completed["Pick_Days"] = (pd.to_datetime(completed["Warehouse_Pick_Date"]) - pd.to_datetime(completed["Credit_Check_Date"])).dt.days

print("\n--- BOTTLENECK VALIDATION ---")
print("\nAvg Credit Check Days by Region:")
print(completed.groupby("Region")["Credit_Check_Days"].mean().round(1))
print("\nAvg Warehouse Pick Days by Product Line:")
print(completed.groupby("Product_Line")["Pick_Days"].mean().round(1))

completed["Payment_Days"] = (pd.to_datetime(completed["Payment_Received_Date"]) - pd.to_datetime(completed["Invoice_Date"])).dt.days
completed["Quarter"] = pd.to_datetime(completed["Order_Date"]).dt.quarter
print("\nAvg Payment Days by Quarter:")
print(completed.groupby("Quarter")["Payment_Days"].mean().round(1))

print("\nDataset generation complete! Open 'o2c_orders.csv' in Power BI Desktop.")

Your synthetic dataset is ready. Next up, you'll import this CSV into Power BI Desktop and build DAX measures to calculate cycle times between every process stage.

Load Data and Create DAX Cycle Time Measures

Your dataset is ready. Now you need to bring it into Power BI Desktop and calculate how long each order spends in every stage of the O2C process.

Raw dates alone do not tell you where delays happen. By creating DAX calculated columns that measure the number of days between each stage, you turn timestamps into actionable cycle-time metrics that Power BI can aggregate, filter, and visualize.

In this step, get ready to:
  • Import the CSV dataset into Power BI and validate data types.
  • Create DAX calculated columns that measure days between each O2C stage.
  • Create DAX measures for dashboard KPIs.
Import the CSV into Power BI

Power BI needs a data source before you can build any visuals or calculations. You will use the Get Data connector to load your CSV file.

  • Switch to Power BI Desktop (it should still be open from Step 1).
  • Click Get Data in the Home tab ribbon.
  • Select Text/CSV from the list and click Connect.
  • Navigate to your o2c-optimization folder and select o2c_orders.csv.
  • Click Open.

A preview of your data appears. Power BI auto-detects column types based on the first 200 rows.

  • Click Transform Data to open the Power Query editor.
  • In the Power Query editor, verify that the date columns (Order_Date, Credit_Check_Date, Warehouse_Pick_Date, Ship_Date, Invoice_Date, Payment_Received_Date) show the Date type icon (a calendar icon in the column header).
  • Verify that Order_Value shows as a Decimal Number type (a "1.2" icon in the column header).
  • Click Close & Apply in the top-left of the Power Query ribbon to load the data into the model.

What just happened?

Power BI loaded all 55,000 rows into an in-memory table. By default, Power BI names the table after the CSV file. You should see a table called Orders (or o2c_orders) in the Fields pane on the right side. If it appears as o2c_orders, right-click the table name in the Fields pane and select Rename to change it to Orders.

Date columns showing as Text?

If any date column shows a text icon (ABC) instead of a calendar icon, click the column header in Power Query, then select Data Type > Date in the Transform tab. Then click Close & Apply again.

Help me fix my data types in Power Query

Create DAX calculated columns for stage cycle times

Each order passes through six stages. To measure how long each stage takes, you will create a calculated column for each pair of consecutive dates using the DATEDIFF function.

  • In the Fields pane on the right, right-click the Orders table.
  • Select New column from the context menu.
  • The formula bar appears at the top of the canvas. Type the following formula and press Enter to confirm:
Credit_Check_Days = DATEDIFF('Orders'[Order_Date], 'Orders'[Credit_Check_Date], DAY)

What does DATEDIFF do?

DATEDIFF counts the number of interval boundaries (in this case, days) between two dates. It takes three arguments: the start date, the end date, and the interval (DAY, WEEK, MONTH, QUARTER, or YEAR). A positive result means the second date is later than the first.

This formula calculates how many days elapsed between order creation and credit check completion for every row in the table.

  • Repeat the process: right-click the Orders table, select New column, and enter each of the following formulas one at a time, pressing Enter after each:
Pick_Days = DATEDIFF('Orders'[Credit_Check_Date], 'Orders'[Warehouse_Pick_Date], DAY)
Ship_Days = DATEDIFF('Orders'[Warehouse_Pick_Date], 'Orders'[Ship_Date], DAY)
Invoice_Days = DATEDIFF('Orders'[Ship_Date], 'Orders'[Invoice_Date], DAY)
Payment_Days = DATEDIFF('Orders'[Invoice_Date], 'Orders'[Payment_Received_Date], DAY)
Total_O2C_Days = DATEDIFF('Orders'[Order_Date], 'Orders'[Payment_Received_Date], DAY)

Why six separate columns?

Each column isolates one stage of the O2C process. When you visualize the data, you can compare stages independently to see exactly where delays occur. Total_O2C_Days captures the full end-to-end cycle for high-level KPIs.

  • After creating all six columns, confirm they appear in the Fields pane under the Orders table with a formula icon (a small fx symbol next to the column name).
  • Click the Table view icon on the left sidebar (a grid icon) to see your data. Scroll right to verify the new columns contain numeric day values.

Seeing blank values in cycle time columns?

Blank values are expected for cancelled and in-progress orders. Your Python script set those date fields to None, which imports as blank in Power BI. DATEDIFF returns blank when either input date is blank. This is correct behavior.

If you see blanks for rows where Order_Status is Completed, double-check that the date columns were imported as Date type in Power Query.

Help me troubleshoot blank values in my DAX calculated columns

Create summary measures for dashboard KPIs

Calculated columns store a value for every row. Measures are different. They calculate dynamically based on whatever filters and slicers are active in a report. You need measures for the KPI cards you will build in the next step.

  • In the Fields pane, right-click the Orders table and select New measure.
  • Type the following formula in the formula bar and press Enter:
Avg Total Cycle Time = AVERAGE('Orders'[Total_O2C_Days])

Calculated column vs measure

A calculated column computes once per row at data refresh and is stored in the model. A measure computes on demand and responds to report context (filters, slicers, visual groupings).

Use calculated columns when you need a per-row value to filter or group by. Use measures when you need dynamic aggregations in visuals.

  • Create three more measures by right-clicking Orders > New measure for each:
Avg Credit Check Time = AVERAGE('Orders'[Credit_Check_Days])
Total Order Value = SUM('Orders'[Order_Value])
Order Count = COUNTROWS('Orders')
  • Confirm that all four measures appear in the Fields pane with a calculator icon (distinct from the fx icon used by calculated columns).
  • Save your Power BI file by pressing Ctrl+S (Windows). Name it o2c-optimization.pbix and save it in your o2c-optimization project folder.

Formula bar not appearing?

If you do not see the formula bar after clicking New measure, make sure you are in Report view or Table view (icons on the left sidebar). The formula bar appears at the top of the canvas in both views.

Help me find the formula bar in Power BI Desktop

Your data model is complete. You have six calculated columns breaking down the O2C cycle into measurable stages, plus four measures ready to drive KPI cards. Next up, you will build the interactive dashboard that makes these numbers visual.

Build the Process Performance Dashboard

You have a data model with DAX-calculated cycle times for every stage of the O2C process. Now it is time to visualize the patterns hiding in those numbers.

In this step, you will build a two-page Power BI report that surfaces process performance at a glance. Page 1 gives executives a top-level view of cycle times, order volume, and regional variation. Page 2 breaks the O2C cycle into individual stages so you can pinpoint exactly where delays occur.

In this step, get ready to:
  • Build an Executive Overview page with KPI cards, a bar chart, and a line chart.
  • Build a Stage Breakdown page with a stacked bar chart and a matrix visual.
  • Add slicers and enable cross-filtering for interactive exploration.
Create Page 1: Executive Overview

The first report page gives stakeholders a high-level snapshot of O2C performance. Three card visuals show the headline KPIs, and two charts reveal where delays concentrate.

  • In Power BI Desktop, make sure you are in Report view (the chart icon in the left navigation pane).
  • Right-click the default Page 1 tab at the bottom of the canvas and select Rename.
  • Type Executive Overview and press Enter.

Now add three card visuals for your headline KPIs.

  • Click an empty area on the canvas to deselect anything.
  • In the Visualizations pane on the right, select the Card visual icon.
  • In the Data pane, expand Orders and drag the Avg Total Cycle Time measure into the Values field well.
  • Resize the card and position it at the top-left of the canvas.
  • Repeat for a second card: click an empty area, select the Card visual, and drag Total Order Value into Values.
  • Repeat once more for a third card using the Order Count measure.
  • Arrange the three cards side by side across the top of the page.

What are card visuals?

Card visuals display a single number prominently on the report canvas. They are ideal for headline KPIs because readers can absorb the value instantly without interpreting axes or legends.

Next, add a clustered bar chart to compare cycle times across regions.

  • Click an empty area below the cards.
  • In the Visualizations pane, select the Clustered bar chart icon.
  • Drag Region from the Data pane into the Y-axis field well.
  • Drag Total_O2C_Days into the X-axis field well.
  • In the X-axis field well, click the dropdown arrow next to Total_O2C_Days and select Average.
  • Resize the chart to fill the left half of the canvas below the cards.

Why a bar chart for regions?

A clustered bar chart makes it easy to compare a single metric across categories. With regions on the Y-axis, the LATAM bar should visibly extend further than the others, confirming the credit-check bottleneck you programmed into the data.

Finally, add a line chart to reveal seasonal patterns in cycle time.

  • Click an empty area to the right of the bar chart.
  • In the Visualizations pane, select the Line chart icon.
  • Drag Order_Date into the X-axis field well.
  • Power BI may auto-expand the date into a hierarchy (Year, Quarter, Month, Day). In the X-axis field well, click the dropdown arrow next to the date hierarchy and remove all levels except Month so you see a continuous monthly trend.
  • Drag Total_O2C_Days into the Y-axis field well and set the aggregation to Average.
  • Resize the chart to fill the right half of the canvas below the cards.
  • Press Ctrl+S (Windows) or Cmd+S (macOS) to save your file.

Your Executive Overview page should now show three KPI cards at the top, a bar chart on the left revealing LATAM as the longest-cycle region, and a line chart on the right showing cycle-time spikes in Q4 months.

Bar chart showing Sum instead of Average?

If the X-axis shows very large numbers, you are likely seeing the Sum aggregation. Click the dropdown arrow next to Total_O2C_Days in the X-axis field well and change it to Average.

Help me fix my bar chart aggregation

Create Page 2: Stage Breakdown

The Executive Overview tells you which regions are slow. This second page answers the deeper question: which specific stages are causing those delays?

  • At the bottom of the canvas, click the + icon to add a new page.
  • Right-click the new tab and select Rename.
  • Type Stage Breakdown and press Enter.

Add a stacked bar chart that shows how each stage contributes to the total cycle time per region.

  • Click an empty area on the canvas.
  • In the Visualizations pane, select the Stacked bar chart icon.
  • Drag Region into the Y-axis field well.
  • Drag Credit_Check_Days into the X-axis field well and set the aggregation to Average.
  • Drag Pick_Days into the X-axis and set it to Average.
  • Repeat for Ship_Days, Invoice_Days, and Payment_Days, each set to Average.
  • Resize the chart to fill the top half of the page.

What does this chart reveal?

Each bar is broken into colored segments representing the average days spent in each stage. You should see LATAM's Credit Check segment dominating its bar, while other regions have more evenly distributed segments.

Now add a matrix visual to compare stage durations across product lines.

  • Click an empty area below the stacked bar chart.
  • In the Visualizations pane, select the Matrix visual icon.
  • Drag Product_Line into the Rows field well.
  • Drag Credit_Check_Days, Pick_Days, Ship_Days, Invoice_Days, and Payment_Days into the Values field well, each set to Average.
  • Resize the matrix to fill the bottom half of the page.

What does the matrix show?

A matrix is like a pivot table. Each row is a product line and each column is a process stage. You should see Custom Manufacturing with a noticeably higher Pick_Days average compared to other product lines.

  • Press Ctrl+S (Windows) or Cmd+S (macOS) to save.

Your Stage Breakdown page now shows exactly which stages cause delays in which dimensions.

Matrix showing totals you don't expect?

Make sure each value column in the matrix is aggregated as Average, not Sum. Click the dropdown on each field in the Values well to confirm.

Help me fix my matrix aggregation

Add slicers and enable cross-filtering

Slicers let stakeholders drill into subsets of the data without editing the underlying visuals. You will add four slicers so anyone viewing the report can filter by region, product line, order status, or date range.

  • Switch to the Executive Overview page tab.
  • Click an empty area on the canvas.
  • In the Visualizations pane, select the Slicer icon.
  • Drag Region from the Data pane into the slicer's Field well.
  • Resize the slicer and position it along the left edge or top of the page where it does not overlap your charts.
  • Repeat to create a second slicer using Product_Line.
  • Create a third slicer using Order_Status.
  • Create a fourth slicer using Order_Date. Power BI automatically creates a date range slicer with start and end date pickers.

What is a slicer?

A slicer is an on-canvas filter. When you select a value in a slicer, Power BI filters all other visuals on the same page to match that selection. This makes the report interactive without requiring the viewer to open a separate Filters pane.

Now copy these slicers to the Stage Breakdown page and verify cross-filtering.

  • Select all four slicers by holding Ctrl (Windows) or Cmd (macOS) and clicking each one.
  • Press Ctrl+C (Windows) or Cmd+C (macOS) to copy them.
  • Switch to the Stage Breakdown page tab.
  • Press Ctrl+V (Windows) or Cmd+V (macOS) to paste the slicers onto this page.
  • Reposition the slicers so they do not overlap your chart and matrix.

Cross-filtering is enabled by default in Power BI Desktop. Clicking a bar in any chart automatically highlights related data in other visuals on the same page.

  • To verify, go back to the Executive Overview page and click the LATAM bar in your clustered bar chart.
  • Confirm that the line chart and KPI cards update to reflect only LATAM data.
  • Click the bar again to deselect and restore the full dataset view.
  • Press Ctrl+S (Windows) or Cmd+S (macOS) to save your file.

Your dashboard is now fully interactive. Selecting any region in the slicer or clicking a bar in a chart filters all visuals on the page instantly.

Clicking a bar doesn't filter other visuals?

Cross-filtering is on by default. If it is not working, select the bar chart visual, then go to Format in the top ribbon and click Edit interactions. Make sure the filter icon (funnel) is selected on the other visuals, not the "none" icon.

Help me enable cross-filtering

Your two-page dashboard is complete. Next up, you will use Power BI's AI-powered visuals to automatically identify the root causes behind these delays.

Root-Cause Analysis with AI Visuals

Your dashboard now shows where delays are happening and how long each stage takes. The next question is why. Which combination of region, product line, and payment terms drives the longest O2C cycles?

Power BI includes built-in AI visuals that use machine learning to answer exactly this question. In this step, you will use the Key Influencers visual to statistically rank delay drivers and the Decomposition Tree visual to drill into the highest-delay paths automatically.

In this step, get ready to:
  • Build a Key Influencers page that ranks which factors most increase Total O2C Days.
  • Build a Decomposition Tree page that uses AI Splits to find the highest-delay path.
  • Create a process outliers table filtered to the 90th percentile.
Create Page 3: Key Influencers

The Key Influencers visual uses linear regression behind the scenes to identify which categorical values drive a continuous metric up or down. You point it at a metric and a set of explanatory fields, and it ranks the factors by statistical impact.

  • In Power BI Desktop, click the + icon next to your existing page tabs at the bottom of the canvas to create a new page.
  • Double-click the new page tab and rename it to Key Influencers.
  • In the Visualizations pane under Build visual, select the Key influencers icon (it looks like a person with radiating lines).
  • Resize the visual to fill most of the page. This visual works best when large.
  • From the Data pane, drag Total_O2C_Days into the Analyze field well.
  • In the dropdown that says What influences Total_O2C_Days to be, select Increase.
  • Drag these four fields into the Explain by field well: Region, Product_Line, Payment_Terms, and Order_Status.
  • Leave the Expand by field empty.

Power BI takes a moment to run its analysis. Once complete, you should see a ranked list of influencers on the left panel.

What should I see?

The Key Influencers panel shows statements like "When Region is LATAM, Total_O2C_Days increases by X." The LATAM region and Custom Manufacturing product line should appear near the top because of the bottlenecks you programmed into the dataset.

Clicking an influencer on the left shows a supporting chart on the right, comparing the selected factor against the average. The Top segments tab shows combinations of values (like LATAM + Net 90) that produce the longest cycle times.

Visual showing no influencers?

Make sure Total_O2C_Days is in the Analyze field, not in Explain by. The visual cannot find influencers if the metric is also listed as an explanatory factor.

If you see a message about too few observations, check that the Analysis Type is set to Continuous (not Categorical) in the Format visual pane under the Analysis card.

Help me troubleshoot my Key Influencers visual not showing results.

Create Page 4: Decomposition Tree

The Decomposition Tree visual lets you drill down through dimensions in any order. Its AI Split feature automatically picks the dimension that leads to the highest (or lowest) value at each level.

  • Click the + icon at the bottom of the canvas to create another new page.
  • Double-click the tab and rename it to Decomposition Tree.
  • In the Visualizations pane under Build visual, select the Decomposition tree icon.
  • Resize the visual to fill the top two-thirds of the page. You will add a table below it shortly.
  • From the Data pane, drag Total_O2C_Days into the Analyze field well. Power BI defaults to showing the sum.
  • Click the dropdown arrow on Total_O2C_Days in the field well and change it to Average.
  • Drag these four fields into the Explain by bucket: Region, Product_Line, Payment_Terms, and Order_Status.

Now use AI Splits to let Power BI find the path to the longest delays automatically.

  • Click the + (plus sign) next to the root node showing the overall average.
  • At the top of the field list, you will see an option marked with a light bulb icon. Select High value.
  • Power BI picks the dimension that leads to the highest average Total O2C Days and expands the tree.
  • Click the + next to the new node and select High value again.
  • Repeat one more time to drill three levels deep.

What does the AI Split reveal?

The Decomposition Tree uses a decision-tree algorithm to pick the dimension with the highest average at each level. You should see it select LATAM (credit delays) or Custom Manufacturing (pick delays) early in the path, confirming the bottlenecks you programmed into the dataset.

The tree can go up to 50 levels deep, showing the top 10 values per level. You can always click a different branch to explore alternative paths.

No light bulb option appearing?

AI Splits require the Analyze field to contain a measure or aggregate (not a raw column). Make sure you changed the aggregation to Average in the field well dropdown.

AI Splits are not supported in Power BI Report Server or Publish to Web scenarios. They work in Power BI Desktop.

Help me get AI Splits working in my Decomposition Tree visual.

Add a process outliers table

To complement the Decomposition Tree, you will add a table showing only the worst-performing orders. This isolates individual records above the 90th percentile for drill-through analysis.

  • First, create a calculated column to flag outlier orders. Switch to the Modeling tab in the ribbon, then click New Column and enter this DAX formula:
Is_P90_Outlier = 
IF(
    'Orders'[Total_O2C_Days] >= PERCENTILE.INC('Orders'[Total_O2C_Days], 0.90),
    "Yes",
    "No"
)

What does this formula do?

PERCENTILE.INC calculates the 90th percentile value across all rows in the Total_O2C_Days column. The IF statement then labels each order as "Yes" (above the threshold) or "No" (below it).

This gives you a simple filter field. Orders marked "Yes" represent the top 10% longest-running O2C cycles.

  • Now add the table visual. Click an empty area in the bottom third of the Decomposition Tree page.
  • In the Visualizations pane, select the Table visual icon.
  • From the Data pane, drag these columns into the table's Columns field well: Order_ID, Region, Product_Line, Total_O2C_Days, Credit_Check_Days, and Payment_Days.
  • With the table visual selected, expand the Filters on this visual section in the Filters pane.
  • Drag Is_P90_Outlier into the Filters on this visual area.
  • Under the filter card that appears, check only Yes. This limits the table to orders exceeding the 90th percentile.

You should see a table listing the worst-performing orders, dominated by LATAM region and Custom Manufacturing product lines.

  • Press Ctrl+S (Windows) or Cmd+S (macOS) to save your .pbix file.

Table is empty or showing all rows?

If the table is empty, confirm that your filter is set to show Yes (not No). If you see all 55,000 rows, the filter has not been applied. Make sure you dragged Is_P90_Outlier to Filters on this visual (not Filters on this page).

If you see an error on the Is_P90_Outlier column, check that your DAX formula references 'Orders'[Total_O2C_Days] with matching table and column names.

Help me fix my outlier table filter in Power BI.

Your AI visuals now statistically confirm the bottlenecks hiding in 55,000 rows of data. Next up, you will translate these findings into a structured business case with financial impact calculations.

Write the Business Impact Portfolio Piece

Your dashboard now tells a clear story about where the O2C process breaks down. The next challenge is translating those insights into something a stakeholder can act on.

In this step, you will calculate the financial impact of the bottlenecks you identified, write a structured business case document, and package all your deliverables into a polished portfolio piece.

In this step, get ready to:
  • Calculate the cash flow impact of process bottlenecks using your dashboard findings.
  • Write a structured business impact document with executive summary, root cause analysis, and automation recommendations.
  • Export your Power BI pages as PDF and finalize all project deliverables.
Calculate the financial impact

Data insights only drive change when you attach a dollar figure to them. Stakeholders need to see how many dollars are tied up because of process delays.

  • Switch back to your Power BI report and navigate to the Executive Overview page.
  • Note the average total cycle time for the LATAM region versus the overall average. The difference is the excess delay in days.
  • Note the total order value associated with LATAM orders. You can see this by clicking on the LATAM bar in your Region chart and reading the filtered Total Order Value KPI card.

How does the working capital formula work?

The formula for tied-up working capital is: (excess delay days / 365) multiplied by the total order value for the affected segment. For example, if LATAM orders average 12 extra days and their total order value is $50 million, the tied-up capital equals (12 / 365) * $50,000,000 = approximately $1.6 million.

This represents money the company cannot use for other purposes while waiting for the delayed process to complete. Apply the same formula to each bottleneck you identified (LATAM credit delays, Custom Manufacturing pick delays, Q4 payment delays).

  • Write down the excess delay days and total order value for each of the three bottlenecks: LATAM credit approval, Custom Manufacturing warehouse pick, and Q4 payment collection.
  • Calculate the tied-up working capital for each bottleneck using the formula above.
  • Keep these figures handy. You will use them in your business impact document next.
Write the structured recommendation document

A strong portfolio piece needs structure. You will create a Markdown document with clearly defined sections that mirror how a real consultant would present findings to a leadership team.

  • Switch to VS Code and create a new file called business-impact-writeup.md inside your o2c-optimization folder.
  • Copy the following template into the file:
# O2C Process Optimization: Business Impact Analysis

## Executive Summary

<!-- 2-3 sentences: what you found, the financial impact, and your top recommendation -->
Analysis of 55,000+ order records reveals three systemic bottlenecks in the Order-to-Cash cycle
that collectively tie up approximately $X million in working capital annually.
The primary recommendation is to implement automated credit approval triggers for the LATAM region,
which alone accounts for $X million in delayed capital.

## Methodology

- Generated 55,000 synthetic O2C order records using Python (Faker, pandas, numpy)
  with realistic probability distributions for stage-to-stage transitions
- Loaded data into Power BI Desktop and created DAX calculated columns
  using DATEDIFF to measure cycle times between each O2C process stage
- Applied Power BI's Key Influencers visual (linear regression)
  and Decomposition Tree (AI Splits) for root-cause analysis
- Calculated financial impact using the working capital formula:
  (excess delay days / 365) * segment order value

## Key Findings

### Finding 1: LATAM Credit Approval Bottleneck
- Average credit check time: ~15 days (vs. ~3 days for other regions)
- Excess delay: ~12 days per order
- Root cause: Manual credit verification processes for emerging-market customers
- **Financial impact: $X million in tied-up working capital**

<!-- Screenshot: Executive Overview page showing LATAM bar highlighted -->

### Finding 2: Custom Manufacturing Warehouse Pick Delays
- Average pick time: ~9 days (vs. ~3 days for other product lines)
- Excess delay: ~6 days per order
- Root cause: Build-to-order process requiring custom assembly before shipping
- **Financial impact: $X million in tied-up working capital**

<!-- Screenshot: Stage Breakdown page showing Custom Manufacturing row -->

### Finding 3: Q4 Payment Collection Slowdown
- Average payment time in Q4: ~2x other quarters
- Root cause: Year-end budget freezes and cash-hoarding behavior by customers
- **Financial impact: $X million in delayed receivables**

<!-- Screenshot: Executive Overview line chart showing Q4 spike -->

## Root Cause Analysis

### Key Influencers Output
The Key Influencers visual identified that when Region is LATAM,
Total O2C Days increases by the largest margin. Product Line = Custom Manufacturing
was the second-strongest influencer.

### Decomposition Tree Path
Using AI Splits (High Value), the longest cycle time path was:
Region: LATAM > Product Line: Custom Manufacturing > Payment Terms: Net 90

This confirms that LATAM Custom Manufacturing orders on Net 90 terms
represent the worst-case scenario for cash conversion.

## Financial Impact Quantification

| Bottleneck | Excess Days | Segment Value | Tied-Up Capital |
|---|---|---|---|
| LATAM Credit Delays | ~12 days | $X million | $X million |
| Custom Mfg Pick Delays | ~6 days | $X million | $X million |
| Q4 Payment Delays | ~15 days | $X million | $X million |
| **Total** | | | **$X million** |

## Recommendations

### 1. Automated Credit Approval Triggers (LATAM)
- Implement rule-based auto-approval for customers with clean payment history
- Set threshold: orders under $10,000 from customers with 12+ month tenure
  and zero disputed invoices auto-approve without manual review
- Expected reduction: 80% of LATAM orders processed in 1-2 days instead of 15

### 2. Warehouse Pick Prioritization (Custom Manufacturing)
- Implement priority queue for Custom Manufacturing orders in warehouse systems
- Pre-stage common custom components based on historical order patterns
- Expected reduction: average pick time from 9 days to 4 days

### 3. Early-Payment Discount Program (Q4)
- Offer 2/10 Net 30 terms for Q4 invoices (2% discount for payment within 10 days)
- Target customers with highest Q4 payment delays
- Expected reduction: 30-40% improvement in Q4 collection velocity

What does this template include?

The template follows a standard consulting deliverable structure. The Executive Summary hooks the reader with the dollar impact. The Methodology section builds credibility. Key Findings present the evidence. The Financial Impact Quantification table makes the business case at a glance.

The Recommendations section proposes specific automation interventions with expected outcomes. This structure is the same format used in real systems transformation consulting engagements.

  • Replace each $X million placeholder with the actual figures you calculated from your dashboard in the previous substep.
  • Replace the <!-- Screenshot --> comments with actual screenshots of your dashboard pages. In VS Code, you can reference images using Markdown syntax or paste them into the document if using Word instead.
  • Save business-impact-writeup.md.

Use your actual dashboard numbers

Your exact figures will depend on the random seed output from your data generation script. The template uses approximate values from the expected distributions. Replace them with the real numbers from your Power BI KPI cards and filtered views.

Polish the portfolio deliverable

A complete portfolio piece includes more than just the write-up. You need the exported dashboard visuals and a README so anyone reviewing your work understands how to reproduce the analysis.

  • Switch back to Power BI Desktop.
  • Click File in the top menu bar.
  • Select Export, then click Export to PDF.
  • Save the exported PDF as o2c-dashboard-export.pdf inside your o2c-optimization folder.

What does Export to PDF include?

Power BI Desktop exports all visible report pages into a single PDF file, one page per PDF page. The export reflects the current state of your slicers and filters. Make sure all slicers are cleared before exporting so you capture the full unfiltered view.

  • Save your Power BI report by pressing Ctrl+S.
  • Switch back to VS Code and create a new file called README.md inside your o2c-optimization folder.
  • Copy the following into the file:
# O2C Process Optimization Dashboard

## Scenario

A fictional company suspects that certain regions and product lines are causing
significant delays in their Order-to-Cash (O2C) cycle, tying up working capital.
This project generates synthetic ERP data, analyzes it in Power BI, and produces
a business case with quantified recommendations.

## Tools Used

- **Python 3.10+** with Faker, pandas, and numpy for synthetic data generation
- **Power BI Desktop** (March 2026 or later) for dashboard and AI-powered analysis
- **VS Code** for scripting and documentation

## Folder Structure

```
o2c-optimization/
├── generate_o2c_data.py       # Data generation script (55,000 orders)
├── o2c_orders.csv             # Generated dataset
├── o2c-optimization.pbix      # Power BI report (4 pages)
├── o2c-dashboard-export.pdf   # PDF export of dashboard pages
├── business-impact-writeup.md # Structured business case
└── README.md                  # This file
```

## How to Reproduce

1. Run `python generate_o2c_data.py` to generate the CSV dataset
2. Open `o2c-optimization.pbix` in Power BI Desktop
3. If prompted, update the data source path to point to your local `o2c_orders.csv`
4. Refresh the data model to load the latest CSV

## Key Findings

- LATAM region has 5x longer credit approval times (avg 15 days vs 3 days)
- Custom Manufacturing product line has 3x longer warehouse pick times
- Q4 orders experience 2x longer payment collection cycles
- Combined bottlenecks tie up approximately $X million in working capital

## Dashboard Pages

1. **Executive Overview** - KPI cards, regional comparison, seasonal trends
2. **Stage Breakdown** - Per-stage cycle times by region and product line
3. **Key Influencers** - AI-identified top drivers of process delays
4. **Decomposition Tree** - AI Split drill-down to worst-case paths

What does this README do?

The README gives context to anyone viewing your project on GitHub or in a portfolio folder. It explains the scenario, lists the tools, shows the folder structure, and provides reproduction steps. This is the standard format hiring managers and technical reviewers expect.

  • Replace the $X million placeholder in the Key Findings section with your actual calculated total from the business impact write-up.
  • Save README.md.
  • Verify your project folder contains all six files by running:
ls

You should see generate_o2c_data.py, o2c_orders.csv, o2c-optimization.pbix, o2c-dashboard-export.pdf, business-impact-writeup.md, and README.md.

Don't see the PDF file?

Make sure you saved the PDF export into your o2c-optimization folder and not your Downloads folder. Power BI may default to Downloads. Move the file manually if needed.

help me find where my Power BI PDF export was saved

✔️ Awesome, I've got everything!

Great. Double check that you have saved both Markdown files and the Power BI report.

ⓧ I'd like to double check the full code

Here is the full business-impact-writeup.md for comparison:

# O2C Process Optimization: Business Impact Analysis

## Executive Summary

<!-- 2-3 sentences: what you found, the financial impact, and your top recommendation -->
Analysis of 55,000+ order records reveals three systemic bottlenecks in the Order-to-Cash cycle
that collectively tie up approximately $X million in working capital annually.
The primary recommendation is to implement automated credit approval triggers for the LATAM region,
which alone accounts for $X million in delayed capital.

## Methodology

- Generated 55,000 synthetic O2C order records using Python (Faker, pandas, numpy)
  with realistic probability distributions for stage-to-stage transitions
- Loaded data into Power BI Desktop and created DAX calculated columns
  using DATEDIFF to measure cycle times between each O2C process stage
- Applied Power BI's Key Influencers visual (linear regression)
  and Decomposition Tree (AI Splits) for root-cause analysis
- Calculated financial impact using the working capital formula:
  (excess delay days / 365) * segment order value

## Key Findings

### Finding 1: LATAM Credit Approval Bottleneck
- Average credit check time: ~15 days (vs. ~3 days for other regions)
- Excess delay: ~12 days per order
- Root cause: Manual credit verification processes for emerging-market customers
- **Financial impact: $X million in tied-up working capital**

<!-- Screenshot: Executive Overview page showing LATAM bar highlighted -->

### Finding 2: Custom Manufacturing Warehouse Pick Delays
- Average pick time: ~9 days (vs. ~3 days for other product lines)
- Excess delay: ~6 days per order
- Root cause: Build-to-order process requiring custom assembly before shipping
- **Financial impact: $X million in tied-up working capital**

<!-- Screenshot: Stage Breakdown page showing Custom Manufacturing row -->

### Finding 3: Q4 Payment Collection Slowdown
- Average payment time in Q4: ~2x other quarters
- Root cause: Year-end budget freezes and cash-hoarding behavior by customers
- **Financial impact: $X million in delayed receivables**

<!-- Screenshot: Executive Overview line chart showing Q4 spike -->

## Root Cause Analysis

### Key Influencers Output
The Key Influencers visual identified that when Region is LATAM,
Total O2C Days increases by the largest margin. Product Line = Custom Manufacturing
was the second-strongest influencer.

### Decomposition Tree Path
Using AI Splits (High Value), the longest cycle time path was:
Region: LATAM > Product Line: Custom Manufacturing > Payment Terms: Net 90

This confirms that LATAM Custom Manufacturing orders on Net 90 terms
represent the worst-case scenario for cash conversion.

## Financial Impact Quantification

| Bottleneck | Excess Days | Segment Value | Tied-Up Capital |
|---|---|---|---|
| LATAM Credit Delays | ~12 days | $X million | $X million |
| Custom Mfg Pick Delays | ~6 days | $X million | $X million |
| Q4 Payment Delays | ~15 days | $X million | $X million |
| **Total** | | | **$X million** |

## Recommendations

### 1. Automated Credit Approval Triggers (LATAM)
- Implement rule-based auto-approval for customers with clean payment history
- Set threshold: orders under $10,000 from customers with 12+ month tenure
  and zero disputed invoices auto-approve without manual review
- Expected reduction: 80% of LATAM orders processed in 1-2 days instead of 15

### 2. Warehouse Pick Prioritization (Custom Manufacturing)
- Implement priority queue for Custom Manufacturing orders in warehouse systems
- Pre-stage common custom components based on historical order patterns
- Expected reduction: average pick time from 9 days to 4 days

### 3. Early-Payment Discount Program (Q4)
- Offer 2/10 Net 30 terms for Q4 invoices (2% discount for payment within 10 days)
- Target customers with highest Q4 payment delays
- Expected reduction: 30-40% improvement in Q4 collection velocity

And here is the full README.md:

# O2C Process Optimization Dashboard

## Scenario

A fictional company suspects that certain regions and product lines are causing
significant delays in their Order-to-Cash (O2C) cycle, tying up working capital.
This project generates synthetic ERP data, analyzes it in Power BI, and produces
a business case with quantified recommendations.

## Tools Used

- **Python 3.10+** with Faker, pandas, and numpy for synthetic data generation
- **Power BI Desktop** (March 2026 or later) for dashboard and AI-powered analysis
- **VS Code** for scripting and documentation

## Folder Structure

```
o2c-optimization/
├── generate_o2c_data.py       # Data generation script (55,000 orders)
├── o2c_orders.csv             # Generated dataset
├── o2c-optimization.pbix      # Power BI report (4 pages)
├── o2c-dashboard-export.pdf   # PDF export of dashboard pages
├── business-impact-writeup.md # Structured business case
└── README.md                  # This file
```

## How to Reproduce

1. Run `python generate_o2c_data.py` to generate the CSV dataset
2. Open `o2c-optimization.pbix` in Power BI Desktop
3. If prompted, update the data source path to point to your local `o2c_orders.csv`
4. Refresh the data model to load the latest CSV

## Key Findings

- LATAM region has 5x longer credit approval times (avg 15 days vs 3 days)
- Custom Manufacturing product line has 3x longer warehouse pick times
- Q4 orders experience 2x longer payment collection cycles
- Combined bottlenecks tie up approximately $X million in working capital

## Dashboard Pages

1. **Executive Overview** - KPI cards, regional comparison, seasonal trends
2. **Stage Breakdown** - Per-stage cycle times by region and product line
3. **Key Influencers** - AI-identified top drivers of process delays
4. **Decomposition Tree** - AI Split drill-down to worst-case paths

Your portfolio piece is complete. You now have a full project folder with a data generation script, an interactive Power BI dashboard, an exported PDF, a structured business case, and a professional README. This is the kind of deliverable that demonstrates end-to-end analytical thinking in interviews and stakeholder presentations.

Secret mission

Cash Flow Impact Simulator

Your dashboard proves that LATAM credit delays and Custom Manufacturing pick times are bleeding working capital. But stakeholders want to see the payoff of fixing these bottlenecks. Build an interactive What-If page that lets them drag sliders to model reduction scenarios and see projected cash freed in real-time.

Clean Up Your Resources

Clean Up Your Resources

This project runs entirely locally, so there are no ongoing costs or cloud resources to worry about. Decide whether to keep your files and tools installed, or clean them up to free disk space.

Resources you used:

  • The o2c-optimization project folder (Python script, CSV dataset, Power BI file, write-up, PDF export, README).
  • Python packages installed via pip: faker, pandas, numpy.
  • Power BI Desktop application.

Keep everything running

No action needed. Choose this if you plan to continue refining your dashboard, adding more visuals, or reusing the dataset for other analyses.

  • Your o2c-optimization folder stays on your Desktop with all files intact.
  • Power BI Desktop remains installed for future projects.
  • The faker, pandas, and numpy packages remain available for other Python scripts.

Pause - I'll come back to this later

Since this project is entirely local, there is nothing to pause or shut down. Your files will be waiting for you when you return.

  • Close Power BI Desktop to free up memory.
  • Close VS Code if you have the project folder open.
  • Your files, packages, and Power BI Desktop will all be here when you come back.

Delete - I don't want to use this again

Remove all project files and optionally uninstall the tools you installed for this project.

  • Delete the project folder by running this command in your terminal:

macOS

cd ~/Desktop
rm -rf o2c-optimization

Windows

cd ~\Desktop
rmdir /s /q o2c-optimization
  • Uninstall the Python packages by running:
pip uninstall faker pandas numpy -y
  • Uninstall Power BI Desktop: open Settings, select Apps, then Installed apps. Search for Power BI Desktop, click the three-dot menu, and select Uninstall.

Nice Work!

Nice Work!

You've built a complete Order-to-Cash process analytics portfolio piece from scratch. From generating synthetic ERP data to delivering AI-powered root-cause analysis and a structured business case, this is an interview-ready deliverable.

You've learned how to:

  • Generated 55,000 rows of realistic synthetic O2C data with intentional bottlenecks using Python, Faker, and numpy to simulate real-world ERP operational patterns.
  • Built DAX calculated columns using DATEDIFF to measure cycle times between each O2C stage, and created a multi-page Power BI dashboard with KPIs, stage breakdowns, and cross-filtering.
  • Used Power BI's Key Influencers and Decomposition Tree AI visuals to isolate root causes of process delays, then wrote a structured business case quantifying the financial impact and recommending automation interventions.
  • Secret Mission: Built a What-If simulator page that lets stakeholders adjust bottleneck reduction parameters with slicers and see projected cash freed in real-time.

Ready to quiz yourself?