Case study

How We Streamlined Customer Forecast Data for Production Planning

Vehicle makers send suppliers updated estimates of the parts they will need. For one automotive parts manufacturer, KS AI & Cloud Solutions built a workflow that compares each new forecast with the last one and flags large changes. Planners can then check the change against schedules, orders and stock before they change a plan.

Who this is for: manufacturing leaders, demand and production planners, purchasing teams, and data/IT teams. The first sections explain the business problem and planning decisions. The technical deep dive is for readers who want implementation detail.

Industry
Automotive parts manufacturing
Data source
Customer demand forecasts from SPS Commerce
Tools used
SharePoint, Power BI, Power Automate and Python
When it runs
On a schedule based on customer forecast updates
Time per forecast cycle
Almost 5 days before; less than 1 day after

What changed since the previous forecast

  • Previous forecast
  • Latest forecast
  • Moved more than 20%
What changed since the previous forecast Bar chart comparing the previous and the latest forecast for one part family over the next six months. Oct: previous 4,200, latest 4,150, change -1.2%; Nov: previous 3,600, latest 4,480, change +24.4%; Dec: previous 3,100, latest 3,180, change +2.6%; Jan: previous 3,350, latest 2,550, change -23.9%; Feb: previous 2,900, latest 3,050, change +5.2%; Mar: previous 2,700, latest 2,760, change +2.2%. November and January moved more than the 20% alert threshold and are flagged. 1k 2k 3k 4k 5k -1.2% Oct 4,200 → 4,150 +24.4% Nov 3,600 → 4,480 +2.6% Dec 3,100 → 3,180 -23.9% Jan 3,350 → 2,550 +5.2% Feb 2,900 → 3,050 +2.2% Mar 2,700 → 2,760

Swipe sideways to see all six months.

Illustrative example for one group of related parts. The bars compare the previous forecast with the latest one. Changes above the agreed alert limit are emailed to the team. These figures are examples, not customer production data.

Project at a glance

The challenge

Each vehicle maker sends forecasts in a different file format and may use its own part numbers. Planners had to compare new files with old files by hand. That made important changes hard to spot quickly. It was also hard to tell a real production-plan change from a shift in timing, a short-lived forecast swing, or a data problem.

What we built

We built a scheduled workflow using Microsoft 365 tools. Our team collects each forecast from SPS Commerce. A Python script puts the file into a common format in SharePoint. Power BI compares the latest forecast with the previous one, and Power Automate emails the planning and data teams about large changes or customer part numbers that are not mapped yet.

The result

A forecast cycle went from almost five days to less than one day (how this was measured). The workflow also gives planners a clear list of changes, keeps past forecasts, and flags customer part numbers that need to be mapped.

The challenge: customer forecasts keep changing, and nobody could see what changed

Vehicle makers send suppliers more than firm orders. They also send a rolling forecast: an updated estimate of the parts they may need in the coming months. Suppliers use it to plan production, materials and staffing. In this case, an automotive parts manufacturer received forecasts from several vehicle makers through SPS Commerce, a platform for sharing forecasts and orders.

A forecast is useful, but the key question is: what changed since the last version? For example, a 25% rise in a group of related parts two months from now is a signal to investigate—not an automatic instruction to buy more. Check the customer's production schedule, firm orders, stock on hand and supplier lead times first. A sharp drop should also trigger a check before you cut production or cancel material orders.

Key terms, in plain English

Trading partner
A customer that sends forecasts. In this case, a vehicle maker.
Forecast version
A copy of a customer's forecast as it stood on the date it was issued. Each new file becomes a new version.
Customer part number (SKU)
The code a customer uses to identify a part. Different customers may use different codes for the same part. Here, SKU means the customer's part number.
Internal part number
The code the manufacturer uses for that part in its own production and planning systems.
Part family
A group of related parts. Planners can review the group's demand before looking at each part one by one.
Reference table (Part)
A lookup table in SharePoint that links each customer part number to the manufacturer's internal part number and part family. The client's data team maintains it.
Supplier lead time
How long it usually takes a supplier to deliver material after an order is placed.

Getting that signal was hard work:

  • Each customer used a different file layout, so the files could not be compared directly.
  • Customer part numbers had to be linked to the manufacturer's part numbers before planners could trust part-level totals.
  • Earlier forecasts were often overwritten or hard to find, so there was no clear history.
  • Planners had to compare versions for every customer, part family and part number by hand.
  • When a customer used a new part number, the team might miss it. That demand could be left out of part-level reports.

A different layout per customer

Customers sent large files in different layouts. The same information appeared in different places and formats.

Part numbers that don't match

Each customer part number must be linked to the manufacturer's part number. If a new code is not linked, its demand can be missing from reports for individual parts.

No memory of past forecasts

Old forecasts were hard to find. Planners could not tell whether a change was new or part of a longer pattern.

Changes spotted late

Large changes were found late, leaving less time to adjust material orders, staffing or production plans.

One forecast cycle, before and after

Every step the team used to do by hand, and what happens now. The biggest difference is time.

StepBefore: manualAfter: automated
1Getting the forecastsSomeone logged in to SPS Commerce and downloaded each customer's file, whenever time allowed.Our team downloads every trading partner's latest forecast from SPS Commerce on a fixed schedule, uploads it to that partner's SharePoint folder and emails the data team a file count and size check before the refresh.
2Making files comparableEach file was reformatted by hand into a common layout, every single time.A Python script does the heavy lifting on each file, then a short Power Query per partner adds the trading partner from the file name and gives every table the same structure, ready to append.
3Keeping historyA new file replaced the old one. Earlier versions were lost or scattered across inboxes.Every forecast version is kept and stamped with its forecast date, read from the file name.
4Matching part numbersEach customer part number (SKU) was looked up by hand to find the internal part number and part family.The reference table (Part) maps every SKU to its internal part number and part family automatically.
5Comparing versionsThe new forecast was lined up against the old one in spreadsheets, customer by customer and part by part.DAX compares the latest version with the previous one, or any earlier one, month by month and over the next 30, 90 and 120 days.
6Deciding what mattersJudged by eye. Different people flagged different things, and slow, steady drifts went unseen.Fixed rules: ±15% for a trading partner and ±20% for a part family or part number, largest change first.
7New part numbersA new SKU with no mapping went unnoticed, and its demand silently dropped out of part-level totals.Every unmapped SKU is listed in the email. The data team adds it to the reference table, and it counts from the next refresh.
8Telling the teamAn ad hoc email, if anyone had time, often days after the forecast arrived.One automatic email to the planning team and the data team, about an hour after each refresh.
9Looking backNo way to see how a customer's forecast had drifted over the months.Any two versions can be compared, so trends, noise and each customer's habits are visible.
10Time per cycleAlmost 5 days from download to the team knowing what had changed.Less than 1 day, with the alert email about an hour after the refresh.

Connecting customer forecasts to production planning

Customer forecast files give a manufacturer an early view of the parts and quantities a customer may need. We streamlined these inputs into a repeatable reporting workflow so the planning team could compare each new forecast with the previous version, see where demand had changed, and review the impact across 30-, 90- and 120-day horizons. This gives production and materials teams a clearer, shared view of expected demand for planning decisions.

The workflow turns incoming customer forecast data into information planners can review consistently. Comparing versions helps the team see what changed, by how much, and when the demand is expected. The 30-, 90- and 120-day views make near-term and longer-range changes easier to spot. The comparison does not automatically identify the reason for a change, or make production decisions on its own. Planners validate significant changes against customer schedules, firm orders, inventory, supplier lead times and source files before adjusting production or material plans.

Look-ahead periodWhat the change may meanWhat to check
30 daysA near-term change may mean the vehicle maker changed its schedule, moved demand earlier or later, or has a parts-supply issue.Check firm orders, the current production schedule, stock on hand, open purchase orders and capacity for the next few weeks.
90 daysA change may mean the production plan has changed, or demand has moved from one month to another.Compare the months side by side. Check supplier lead times and planned shifts. Did total demand change, or only its timing?
120 daysA longer-range change may reflect a revised production plan, a model change, or demand that is still uncertain.Check the customer plan, materials with long lead times, supplier commitments, stock risk and earlier forecast versions.

Possible reasons to check before changing the plan

  • Vehicle-maker plan changed: the customer may have changed its build schedule or the number of vehicles it plans to make.
  • Demand moved between months: one month may go up while another goes down, even if the total changes very little.
  • Short-lived forecast swing: a spike may reverse in the next version. Compare several versions before treating it as a trend.
  • Supplier lead time: material that takes a long time to arrive may need to be ordered before the demand date.
  • Stock and order timing: the amount needed may stay similar while the date to order material changes.
  • New or unmapped part number: a new customer code may not be linked to the correct internal part.
  • File or data problem: a missing file, duplicate row or changed layout may create a false change—or hide a real one.

How to connect the dots: from alert to planning action

Illustrative example: a part family's forecast is 25% higher in the 90-day view than it was in the previous version. The alert points to a change; it does not tell the planner what to do.

  1. Confirm the comparison. Make sure both versions cover the same future months and use the same part mapping.
  2. Check the cause. Compare the customer's production schedule and firm orders. Check stock, open purchase orders, supplier lead times and the source file.
  3. Choose the right action. If extra demand is confirmed, review material supply, order dates, capacity and shifts. If demand only moved between months, the timing may need to change more than the total quantity.
  4. Record and monitor. Note what evidence supported the decision, then see whether the next forecast confirms or reverses the change.

Important: an alert is a prompt to investigate, not an automatic instruction to buy, cancel an order or change production.

What to check next: start with the largest changes. Compare the same future months in both versions. Check for missing part mappings, then compare the result with orders, stock, supplier lead times and production schedules. Treat each possible cause as unconfirmed until the evidence supports it.

The solution in plain English

Think of it as track changes for customer forecasts. Each new file is saved. Power BI compares it with the previous version and shows where expected demand changed. An automated email flags changes that pass the agreed limits. A planner checks the alert before changing production or material plans.

  1. Each forecast cycle

    Collect

    Our team downloads each customer's latest forecast from SPS Commerce. A Python script cleans the file and puts it into a common format. We save it in that customer's SharePoint folder and confirm the upload to the data team.

  2. Scheduled, automatic

    Refresh

    Power BI reads the files, aligns their layouts and uses the reference table to link each customer part number to the manufacturer's internal part number and part family.

  3. Any time

    Compare

    Planners compare the latest forecast with the previous one for upcoming months. They can review totals by customer, part family or individual part number.

  4. About an hour later

    Alert

    Power Automate checks for large changes and missing part mappings, then emails the results to the planning and data teams.

The cycle follows the customers, not the other way round. The refresh and the alert flow are simple schedules, so the same build runs daily, monthly or on demand.

Change % = ( Latest forecast − Previous forecast ) ÷ Previous forecast This calculation shows the percentage change. It does not explain the cause. In this example, alerts are sent when total demand by customer changes by more than 15%, or a part family or part number changes by more than 20%. Set and test thresholds for the risks in each business.

Why we keep every forecast version

Keeping old forecast versions helps planners see whether a change is a one-off swing or part of a steady trend.

For example, two part families may both show lower demand in the latest forecast. One may have fallen steadily over six versions. The other may have moved up and down. The latest numbers alone look similar, but the planning response may be different.

One snapshot versus every version Left, only the latest snapshot: part family M7 shows 2,550 for March, down 12% on the previous forecast, below the alert line; part family K2 shows 2,000, down 22%, and triggers an alert. Both look like drops. Right, with every version kept: across six versions M7 fell steadily from 4,000 to 2,550, likely a trend to confirm before planning, while K2 bounced between about 1,950 and 2,600, noise to hold steady on. Figures are illustrative. Only the latest snapshot what you see if old versions are lost Part family M7, March 2,550 −12% vs previous forecast No alert Part family K2, March 2,000 −22% vs previous forecast Alert Both look like drops. Which one is the real change? Every version kept the same March forecast, version by version 2k 3k 4k v1 v2 v3 v4 v5 v6 oldestlatest M7 K2 M7: likely a trend Lower in every new version. Confirm with the customer, then review the plan. K2: noise Bouncing around 2,300. Hold the plan steady and check with the customer.

Swipe sideways to see both views.

The same two part families, seen as one snapshot and as a history of versions. In the report, planners see this by comparing the latest forecast with any earlier version. Figures are illustrative.

What the history makes possible

  • Tell a trend from noiseCompare the latest forecast with any earlier version and see whether demand is drifting steadily or just bouncing around.
  • Know each customer's habitsSome trading partners revise a lot, others hardly at all. History shows whose forecasts to plan on, and how much safety stock each one really needs.
  • Measure forecast accuracyOld versions can be checked against what customers actually bought later. Calculating forecast accuracy also requires actual sales or shipment data and a defined error metric; forecast history alone is not enough.
  • Back up customer conversationsWhen a customer cuts demand after material was bought, the stored versions show exactly what was forecast and when.

What goes wrong without it

  • Every change can look equally urgent, even when it reflects a timing shift, temporary volatility or a data issue.
  • Slow, steady drifts that stay under the alert line are never noticed.
  • There's nothing to measure forecast accuracy against.
  • There's no evidence to share when a customer's numbers move after commitments were made.
  • Buffers and safety stock are set by gut feel.
  • Knowledge lives in inboxes and leaves with the person who kept them.

Who uses it, and who does what

The workflow connects three jobs: planners decide how to respond, the client's data team keeps part mappings correct, and our team maintains the file collection and reporting workflow. Automation handles the repeated comparison and email steps.

  • Planning teamClient: demand and production planners

    Read the alert email after every refresh, open the report to drill from trading partner down to part number, and adjust production and material plans.

    Main report users
  • Supply chain and purchasingClient

    Use confirmed changes to bring material orders forward or push them back, before a swing reaches their own suppliers.

    Report users
  • Customer account managersClient

    Follow up with a trading partner when its forecast swings sharply or keeps reversing from one version to the next.

    Report users
  • Data teamClient

    Checks the upload confirmation and alert email. Adds each missing customer part number to the reference table, with its internal part number and part family. Keeps the location list up to date.

    Maintains the reference table
  • Our teamKS AI & Cloud Solutions

    Downloads each forecast, uses a Python script to standardise it, uploads it to SharePoint and confirms the upload. Our team also maintains the data model, report and alert flow.

    Collection and support
  • AutomationPower BI and Power Automate

    Refreshes on schedule, compares the versions, runs the four checks and sends the email. Nobody has to press a button.

    Runs on its own
Who does what, on every forecast cycle Five lanes. Customers publish a new forecast on SPS Commerce. Our team downloads, reshapes and uploads it to SharePoint, then emails the data team a confirmation of every file before the refresh. Power BI refreshes and compares the latest version with the previous one, then Power Automate runs four checks and sends one email. The planning team explores the report at any time and acts on big changes in production and material plans. The data team maps missing customer part numbers in the reference table, which Power BI uses at the next refresh. Customers trading partners Our team KS AI & Cloud Solutions Automatic Power BI, Power Automate Planning team the main report users Data team keeps the reference table Publish forecaston SPS Commerce, owncycle and own layout Collect & preparedownload, run Pythonscript, upload Refresh & comparePower BI model,latest vs previous Check & emailPower Automate runs4 checks, 1 email Explore the reportany time, partnerdown to part number Act on changesadjust productionand material plans Reference table(Part) in SharePoint,SKU → internal P/N Map missing SKUsadd each one to thereference table Check the uploadfile counts and sizes,before the refresh email email email adds the missing mapping used at thenext refresh

Swipe sideways to see every lane.

Who does what on every forecast cycle. Each lane is a person, a team or a system. The amber loop is the data team closing a mapping gap, which Power BI picks up at the next refresh.

Solution architecture

Follow the diagram from left to right: customer forecast, file collection and cleanup, SharePoint, Power BI, then the alert email. The loop back is important: the data team fixes missing part mappings, and Power BI uses those fixes at the next refresh. The next section explains the technical setup for data and IT teams.

Solution architecture, with who runs each step Customers publish demand forecasts on SPS Commerce. On every forecast cycle our team downloads each file, standardises it with a Python script, names it by forecast date, uploads it to its trading partner folder in SharePoint Online and emails the data team a confirmation, next to a location list and the reference table (Part) that maps customer part numbers (SKUs) to internal part numbers, kept by the client's data team. A scheduled refresh in the Power BI service, which runs automatically, standardises and appends the files with Power Query, models them with the reference tables, and compares the latest forecast with the previous one in DAX. It feeds an interactive report used by the planning team. About an hour after the refresh, a Power Automate flow runs four DAX queries against the model and sends one email to the planning team and the data team. The data team adds missing mappings to the reference table, which feeds the next refresh. Source Customer forecasts Customers Collection Download and standardise Part 1 Our team Storage SharePoint Online Part 1 Our team + data team Power BI Model and report Part 2 Automatic, on schedule Alerts Power Automate Part 3 Automatic SPS Commerce customer demandforecasts Partner A Partner B Partner C Each customer publishesnew forecasts on its owncycle, in its own layout Every cycle Download each file Python: standardise Name file by date Upload to its folder Email a confirmation Partner folders one per trading partner Partner A Partner B Partner C Every forecast version is kept Location list location → country Reference table Part: SKU → internal part number + family Kept by data team Semantic model Power Query standardise and append Data model Part, plant, calendar DAX measures latest vs previous Interactive report partner → part family → part number Used by planning team Alert flow After each refresh Run 4 DAX queries Build HTML tables Send one email DAX One emailchanges + missing SKUs Planning teamacts on big changes Data teammaps missing SKUs Data team adds missing mappings

Swipe sideways to explore the diagram.

End-to-end architecture. Forecast files land in SharePoint, a Power BI semantic model compares forecast versions, and a Power Automate flow queries the model with DAX and emails the results.

Technical deep dive (for data and IT teams)

This section explains how the workflow is built. It covers file collection, the Power BI model and automated alerts. Manufacturing leaders and planners who only need the business process can skip to the results section.

Part 1: Collecting and standardising forecasts into SharePoint

SPS Commerce, a Python script and SharePoint Online. Run by our team.

The pipeline starts with the files themselves. On every forecast cycle, our team downloads the latest forecasts for each trading partner from SPS Commerce, using access the client provides. A Python script then puts every customer's file into one consistent, predictable format, and each file is uploaded to its partner's folder in a dedicated SharePoint site.

  1. Download from SPS CommerceIn the transactions search, our team picks one trading partner and its plants, filters to forecast documents in a recent date range, ticks the latest versions and downloads them as CSV files.
Downloading forecasts from the customer portal Simplified, illustrative view of the transactions search in SPS Commerce. One: the partner filter holds one trading partner and four of its plants. Two: the document type is set to Forecast and the date range to the last 30 days. Three: the two latest forecast documents, dated 12 October 2026, are ticked, while the two from 5 October are not. Four: the download button in the selection bar starts a CSV download, confirmed by a message. All names, document IDs and dates are invented. Supplier portal Our team › Parts manufacturer Fulfillment Dashboard Transactions Settings Item sourcing Contains Search for a document… Saved search Advanced search Date range Last 30 days Document direction InboundOutboundAll Archived documents ExcludeIncludeOnly Partner Partner A · North plantPartner A · South plant Partner A · Plant 3Partner A · Mexico Document type Forecast Status Any status Matching results:12 Clear fields Search i Your download has started 2 forecast files, CSV Document type Document ID Status Sender Receiver Date Forecast FC-2610-0117-N… Open Partner A · North plant Parts manufacturer Oct 12, 2026, 11:23 PM Forecast FC-2610-0117-S… Open Partner A · South plant Parts manufacturer Oct 12, 2026, 11:28 AM Forecast FC-2610-0098-N… Open Partner A · North plant Parts manufacturer Oct 5, 2026, 4:23 AM Forecast FC-2610-0098-S… Open Partner A · South plant Parts manufacturer Oct 5, 2026, 9:02 AM 2 Items selected Clear selected 1 22 33 4 1 Pick the partner and its plants 2 Forecasts only, last 30 days 3 Tick the latest versions 4 Download them as CSV

Swipe sideways to see the full screen.

A simplified, illustrative view of the download step in SPS Commerce, redrawn in our own style. Partner names, plants, document IDs and dates are invented.
  1. Standardise with a Python scriptEvery customer exports its forecast differently: extra header rows, multi-record layouts, dates written in different ways, one file or several per plant. A Python script does the heavy lifting with a small rule set for each trading partner, and writes clean files for each partner in a consistent format, cycle after cycle.
  2. Name by forecast dateThe script names each file with the date the forecast was issued, for example PartnerA_forecast_2026-10-12.csv. The model reads the date from the name, so nobody has to type it anywhere.
  3. Upload to the partner's folderThere's one folder per trading partner. Earlier versions stay where they are, and that is what builds the history.
  4. Confirm the upload to the data teamA second Python script counts the files for each trading partner and lists every file's name and size. Our team emails the result to the primary contact in the client's data team, as a final check before the scheduled refresh.
  5. Keep the reference data alongsideThe reference table (Part), which maps each customer part number (SKU) to the internal part number and part family, and a location-to-country list live in the same site. The client's data team maintains both.

Python: standardising a partner's files, simplified

from pathlib import Path
import pandas as pd

# One small rule set per trading partner
RULES = {
    "PartnerA": {"skip": 2, "fmt": "%m/%d/%Y",
                 "dates": ["Forecast Start Date"]},
    "PartnerB": {"skip": 0, "fmt": "%Y%m%d",
                 "dates": ["Ship Date"]},
}

def standardise(partner, downloads, forecast_date, out_dir):
    rule = RULES[partner]
    # one or more downloaded files per partner
    frames = [pd.read_csv(f, dtype=str, skiprows=rule["skip"])
              for f in downloads]
    data = pd.concat(frames, ignore_index=True).dropna(how="all")
    data.columns = data.columns.str.strip()   # predictable headers

    for col in rule["dates"]:                 # one date format
        data[col] = (pd.to_datetime(data[col], format=rule["fmt"])
                       .dt.strftime("%Y-%m-%d"))

    name = f"{partner}_forecast_{forecast_date}.csv"
    data.to_csv(Path(out_dir) / partner / name, index=False)

A confirmation before the refresh deadline

Power BI refreshes on a schedule, and the alert flow runs about an hour later. Both only see what is in SharePoint at that moment, so the upload has a deadline: every file has to be in place before the refresh starts. Once the upload is done, a second Python script counts the files for each trading partner and lists every file's name and size, and our team sends the result to the primary contact in the client's data team.

It takes seconds to read, and it catches problems while there's still time to fix them. A partner with zero files, or a file far smaller than usual, stands out before the refresh rather than in the alert email afterwards.

Python: the confirmation report, simplified

import os

TRADING_PARTNERS = ["PartnerA", "PartnerB", "PartnerC", "PartnerD",
                    "PartnerE", "PartnerF", "PartnerG"]

def get_files_by_partner(folder):
    """File names and sizes for each trading partner in the folder."""
    details = {partner: [] for partner in TRADING_PARTNERS}
    for root, _, files in os.walk(folder):
        for file in files:
            size_kb = os.path.getsize(os.path.join(root, file)) / 1024
            for partner in TRADING_PARTNERS:
                if partner.lower() in file.lower():
                    details[partner].append((file, f"{size_kb:.2f} KB"))
    return details

def generate_email(details, dt):
    """Confirmation email: a count per partner, then every file."""
    body = (f"Subject: Demand forecast file report - {dt}\n\n"
            "Dear Team,\n\nPlease find below the details for the "
            f"folder with date {dt}:\n\n")
    for partner, files in details.items():
        noun = "file" if len(files) == 1 else "files"
        body += f"🔹 {partner}: {len(files)} {noun}\n"
        if files:
            body += "    📂 Files:\n"
            for name, size in files:
                body += f"      - {name} ({size})\n"
        body += "\n"
    return body + "Best Regards,\nAutomated Report System\n"
Demand forecast file report - 10-12-2026
From: Our team   To: Data team, primary contactBefore the scheduled refresh

Dear Team,

Please find below the details for the folder with date 10-12-2026:

  • Partner A: 1 fileFiles:- PartnerA_forecast_2026-10-12.csv (64.10 KB)
  • Partner B: 2 filesFiles:- PartnerB_forecast_2026-10-12.csv (371.25 KB)- PartnerB_plant2_forecast_2026-10-12.csv (31.40 KB)
  • Partner C: 1 fileFiles:- PartnerC_forecast_2026-10-12.csv (179.82 KB)
  • Partner D: 1 fileFiles:- PartnerD_forecast_2026-10-12.csv (2.74 KB)
  • Partner E: 1 fileFiles:- PartnerE_forecast_2026-10-12.csv (8.05 KB)
  • Partner F: 0 files Stands out before the refresh
  • Partner G: 1 fileFiles:- PartnerG_forecast_2026-10-12.csv (82.33 KB)

Best Regards,
Automated Report System

Swipe sideways to see the full email.

Sample upload confirmation, with invented partner names, files and sizes. The amber tag is our annotation, not part of the email.

Why SharePoint is the right landing zone

We could have pushed files straight into a database. For a file-based process owned by business users, SharePoint gave us more:

  • Every forecast version, keptEach file stays in its folder, so the model holds every past version. That is the foundation of the history described above.
  • Add a partner without a rebuildA new trading partner means a new folder, one new rule set in the script and one new query. Nothing downstream changes.
  • Maintained by the businessThe data team updates the reference table in Excel, in SharePoint, without ever opening Power BI.
  • Easy collaborationOur team, the data team and the planners all open the same CSV files, from SPS Commerce download to Power BI source, so everyone sees exactly what the report is built on.
  • No gateway, no stored passwordsSharePoint Online is a cloud source, so the Power BI service refreshes it with organizational sign-in and no on-premises gateway.
  • An audit trail for freeEvery file is a record of what each customer sent, kept separate from the logic that interprets it.

Part 2: The Power BI model and report

Power Query, data modeling, DAX and report design. Refreshes automatically.

This is the heart of the solution. The model has to turn a pile of differently shaped files into one question: how did each customer's view of the coming months change since its previous forecast? Four problems stood between the raw files and that answer.

Problem 1: many partner files, one table

Because the Python script has already done the heavy lifting, Power Query only has light work left. Each partner folder has its own short query that does four things: a light clean-up, renaming columns to the common names, adding a Trading Partner column taken from the file name so every row knows which customer it came from, and giving the table exactly the same columns and order as every other query. Where a partner dates its forecast buckets differently, its query also shifts the start date so every partner lines up on the same calendar.

That identical structure is what makes the next step possible: one append stacks every partner into a single forecast table.

From partner files to one forecast table Step one: clean files sit in SharePoint, one per trading partner, already standardised by our team's Python script, which handled header rows, multi-record layouts and date formats. Step two: Power Query runs one light query per partner automatically on every refresh. Each query does four things: a light clean-up, renaming columns to common names such as Buyer Part to SKU, adding a Trading Partner column taken from the file name so it is the same on every row, and giving the table exactly the same columns and order as every other query. Step three: because every query has an identical structure, one append stacks them into a single forecast table with trading partner, SKU, location, quantity and start date. 1Clean files in SharePoint Python script, our team 2One light query per partner Automatic, on refresh 3Append into one table Automatic Partner A PartnerA_forecast_2026-10-12.csv Partner A 1 2 3 4 Partner B PartnerB_forecast_2026-10-12.csv Partner B 1 2 3 4 Partner C PartnerC_forecast_2026-10-12.csv Partner C 1 2 3 4 Partner D PartnerD_forecast_2026-10-12.csv Partner D 1 2 3 4 Append Table.Combine from the file name Trading partner SKU Location QTY Start date PartnerA 41-7730 PLT-1 120 2 Nov PartnerB CX48213 — 64 9 Nov PartnerC 5512-B PLT-4 210 2 Nov PartnerD AB-0091 — 38 16 Nov One table, one structure. SKU is the customer's part number. Heavy lifting already done by the Python script: header rows, multi-record layouts, date formats. What every partner query does 1 Light clean-up keep the columns the model needs and set data types 2 Common column names Buyer Part → SKU, Forecast Qty → QTY 3 Add Trading Partner taken from the file name, the same on every row 4 Identical structure same columns and order in every query, ready to append

Swipe sideways to see the full table.

Python has already cleaned each file, so every partner's query is short and ends in the same structure. That is what lets one append build the forecast table. Sample rows are illustrative.

Power Query (M), simplified

// Each partner query: trading partner from the file name...
#"Added Partner" = Table.AddColumn(#"Kept Columns", "Trading Partner",
    each Text.BeforeDelimiter([Source.Name], "_"), type text),

// ...common column names...
#"Renamed Columns" = Table.RenameColumns(#"Added Partner", {
    {"Buyer Part", "SKU"},
    {"Forecast Qty", "QTY"},
    {"Forecast Start Date", "Start Date"}
}),

// ...and the same columns, in the same order, as every other query
#"Same Structure" = Table.SelectColumns(#"Renamed Columns", {
    "Trading Partner", "SKU", "Location", "QTY", "Start Date", "Source.Name"
}),

// Identical structures, so one append builds the forecast table
Source = Table.Combine({PartnerA, PartnerB, PartnerC, PartnerD, PartnerE}),
#"Removed Errors" = Table.RemoveRowsWithErrors(Source)

Problem 2: which forecast version is this row from?

Each file holds forecast quantities by delivery date, but the model also needs to know which version a row belongs to. We made the file name the source of truth. A calculated column reads the ten-character date just before the file extension, so every row is stamped with its forecast date the moment it loads.

DAX calculated column

Forecast Date =
VAR FileName = TRIM ( 'OEM forecast Table'[Source.Name] )
RETURN
    // "PartnerA_forecast_2026-10-12.csv" → 12 Oct 2026
    DATEVALUE ( MID ( FileName, LEN ( FileName ) - 13, 10 ) )

Problem 3: customer part numbers (SKUs) aren't internal part numbers

Customers forecast against their own part numbers (SKUs), and the manufacturer plans against its own. The reference table (Part), an Excel workbook in SharePoint that the client's data team maintains, maps each SKU to its internal part number and part family, and becomes the Part dimension. Plant and location tables add the plant name and country, and a calendar table with a rolling next-six-months flag keeps every view focused on the planning horizon. Kit part families are filtered out of the part-level pages and alerts.

Data model A forecast fact table sits at the centre. It relates many-to-one to the Part table, which is the reference table kept by the client's data team, on SKU equals customer part number; to a Plant table and a Location list on location ID; and to a calendar table on start date. Two calculated tables, Forecast versions and Previous versions, have no relationships and are read by the measures to pick the two forecast versions to compare. Forecast table SKU Start date Location ID Trading partner QTY Forecast date Part Customer P/N (SKU) Internal P/N Part family Plant Location ID Plant name Calendar Date Month-year Next 6 months flag Location list Location ID Country Forecast versions Forecast date Previous versions Forecast date reference table Kept by the data team *1 *1 *1 *1 The two version tables have no relationships. They feed the two slicers, and the measures read them.

Swipe sideways to see the full model.

A simple star around the forecast table, plus two disconnected version tables that drive the comparison.

Problem 4: comparing any two forecast versions

Two small calculated tables, Forecast Versions and Previous Versions, list every forecast date and have no relationships at all. They feed two slicers, and the measures read the selections. By default the current version is the latest file and the previous version is the one before it, but a planner can pick any earlier version to compare against. This is where the stored history pays off.

DAX, names simplified

Previous Forecast Demand =
VAR CurrentVersion =
    SELECTEDVALUE (
        'Forecast Versions'[Forecast Date],
        MAX ( 'Forecast Versions'[Forecast Date] )
    )
VAR VersionBefore =
    CALCULATE (
        MAX ( 'OEM forecast Table'[Forecast Date] ),
        FILTER (
            ALL ( 'OEM forecast Table' ),
            'OEM forecast Table'[Forecast Date] < CurrentVersion
        )
    )
VAR Compared =
    SELECTEDVALUE ( 'Previous Versions'[Forecast Date], VersionBefore )
RETURN
    CALCULATE (
        SUM ( 'OEM forecast Table'[QTY] ),
        'OEM forecast Table'[Forecast Date] = Compared
    )

The headline cards compare the two versions over three fixed windows of 30, 90 and 120 days, each starting on the first day of the month after the current forecast. Fixed windows keep the comparison fair: both versions are judged on exactly the same future dates.

Which versions, which months Top: five forecast versions dated 14 September to 12 October 2026. By default the latest, 12 October, is the current forecast and 5 October is the previous forecast, and the planning team can pick any earlier version instead. Bottom: the 30, 90 and 120 day comparison windows all start on 1 November, the first day of the month after the current forecast, and end on 1 December, 30 January and 1 March. Pick two forecast versions Chosen by planners v1 14 Sep v2 21 Sep v3 28 Sep v4 5 Oct v5 12 Oct Current forecast Previous forecast Or compare with any earlier version Compare the same future window Oct Nov Dec Jan Feb Mar 12 Oct windows start 1 Nov 30 days to 1 Dec 90 days to 30 Jan 120 days to 1 Mar

Swipe sideways to see the full timeline.

Two slicers choose the versions. Fixed windows decide which future months are compared.

Three levels of detail, one layout

The report opens on a home page that shows when the data last refreshed, then offers three analysis pages with the same layout. Planners start broad and drill down: trading partner, then part family, then internal part number. Because every page looks the same, nobody has to relearn where things are.

Report page: Trading partner Illustrative recreation of the trading partner page: a header with page navigation, a row of slicers and the two compared forecast versions, three cards comparing the next 30, 90 and 120 days, a line chart of both versions by month with percentage changes, a matrix titled Trading partner demand overview that shows both versions by month with heat-map shading, and a column chart by month. OEM demand analysis Trading partner Part family Part number Trading partner All Location All Country All Current forecast All Previous (optional) All 12 Oct 2026Current forecast 5 Oct 2026Previous forecast Next 30 days 10,420current 10,180previous +2.36% ▲ +240 Next 90 days 24,900current 25,610previous -2.77% ▼ -710 Next 120 days 31,800current 30,950previous +2.75% ▲ +850 Demand by month, current vs previous forecast PreviousCurrent 5k 7.5k 10k -16.0% Oct -0.4% Nov +8.9% Dec -0.7% Jan +11.8% Feb +19.6% Mar Trading partner demand overview Partner / version Oct Nov Dec Jan Feb Mar Partner A 12 Oct 2,590 4,710 3,740 2,215 2,066 1,860 5 Oct 3,430 4,950 3,310 2,200 2,190 1,885 Partner B 12 Oct 960 3,380 1,730 1,810 2,060 2,440 5 Oct 2,155 3,090 1,530 1,810 1,350 1,490 By month (k) PreviousCurrent Oct Nov Dec Jan Feb Mar

Swipe sideways to see the full page.

Illustrative recreations of the three report pages, used day to day by the planning team. Switch pages with the tabs: the cards and charts stay the same, and the matrix in the bottom left drills from trading partner to part family to part number. Partner names and figures are invented.
Horizon cardsThe current forecast against the previous one for the next 30, 90 and 120 days, with the % change and the unit difference
Trend chartsBoth forecast versions by month, each point labelled with its % change, plus a trend line
Overview matrixBoth versions side by side for the next six months, shaded by volume, by partner, family or part number
FiltersPartner, part family or part number, location, country, the current version and an optional comparison version, with one-click clear

Part 3: Automated alerts with Power Automate

Power Automate, the Power BI connector and Office 365 Outlook. Runs on its own.

A report only helps if someone opens it. The alert flow makes sure the important changes reach the team either way. It runs about an hour after each scheduled refresh, and instead of exporting visuals it asks the published semantic model direct questions in DAX, using the Run a query against a dataset action of the Power BI connector.

  1. RecurrenceScheduled to match the forecast cycle, about an hour after the refresh.
  2. Run a query against a dataset, four timesFour DAX queries run against the published semantic model, one per check.
  3. Parse JSONEach result is parsed so its columns can be used in later steps.
  4. Create HTML table and ComposeEach result becomes a bordered HTML table.
  5. Send an email (V2)One email to the planning team and the data team, with all four tables and a link to the reference table workbook.

Four checks on every new forecast

  • Trading partner±15%Total demand per partner, largest change first
  • Part family±20%Kit families excluded, largest change first
  • Part number±20%Internal part numbers, kit parts excluded, largest change first
  • Missing mappingAny new SKUCustomer part numbers (SKUs) not yet in the reference table (Part)

What the alert email looks like

All four checks land in one email, always in the same order, so readers know exactly where to look. The numbers below match the markers in the sample.

  1. Subject and recipients. A fixed subject, sent automatically to the planning team and the data team after each refresh.
  2. Link to the reference table. One click opens the Excel workbook where the data team adds missing mappings.
  3. Missing part numbers. Each customer part number (SKU) not yet in the reference table, with the trading partner that sent it.
  4. Trading partner changes. Partners whose total demand moved more than ±15%.
  5. Part family changes. Families that moved more than ±20%, kit families excluded.
  6. Part number changes. Internal part numbers that moved more than ±20%, kit parts excluded.

Every change table has the same columns: current forecast, previous forecast, change in % and the difference in units, sorted by the largest difference first.

1Scheduled email: missing SKU and change percent alert
From: System generated   To: Planning team; Data teamAfter each refresh

Hello team,

Please check below for missing part numbers and significant changes in the trading partner, part family and part number analysis.

3Missing part numbers in the reference table (Part) 2 new
Customer part number (SKU)Trading partner
CX-48213Partner B
77-1290-APartner D
4Trading partner change, threshold ±15%
Trading partnerCurrent forecastPrevious forecastChange (%)Difference
Partner C6,2405,180+20.461,060
5Part family change, threshold ±20%
Part familyCurrent forecastPrevious forecastChange (%)Difference
Family K21,9801,590+24.53390
Family M7640842−23.99−202
6Part number change, threshold ±20%
Internal part numberCurrent forecastPrevious forecastChange (%)Difference
40-2271410318+28.9392
40-118596132−27.27−36

Regards,
System generated

Swipe sideways to see the full email.

The queries behind the email

Because the queries call the same measures as the report, the email and the dashboard can never disagree. Changing a threshold is a one-line edit in the flow.

DAX query: trading partner changes beyond ±15%

EVALUATE
FILTER (
    SUMMARIZE (
        'OEM forecast Table', 'OEM forecast Table'[Trading Partner],
        "Current Forecast", [Current Forecast Demand],
        "Previous Forecast", [Previous Forecast Demand],
        "Change", ROUND ( [% Chg] * 100, 2 ),
        "Diff", [Current Forecast Demand] - [Previous Forecast Demand]
    ),
    [Change] > 15 || [Change] < -15
)
ORDER BY ABS ( [Diff] ) DESC

Catching part numbers that aren't mapped yet

When a customer starts forecasting a part number (SKU) that isn't in the reference table (Part), its demand still counts in the trading partner totals, but it has no internal part number or part family, so it drops out of the part-level pages. The fourth query catches this with a set difference: every SKU in the forecasts, minus every customer part number already in the reference table.

DAX query: unmapped SKUs

EVALUATE
VAR Unmapped =
    EXCEPT (
        VALUES ( 'OEM forecast Table'[SKU] ),
        VALUES ( Part[Customer P/N] )
    )
RETURN
    SUMMARIZE (
        FILTER (
            'OEM forecast Table',
            'OEM forecast Table'[SKU] IN Unmapped
        ),
        'OEM forecast Table'[SKU],
        'OEM forecast Table'[Trading Partner]
    )

The email lists each unmapped SKU with its trading partner and links to the reference table workbook. The client's data team fills in the internal part number and part family, and from the next refresh that part's demand rolls up correctly.

The missing-mapping loop, and who closes it A cycle of four steps. A customer starts using a new part number (SKU) in a forecast. The automatic check finds SKUs that are not in the reference table (Part). The automatic email lists them for the data team with a link to the workbook. The data team adds the internal part number and part family, so from the next refresh the demand counts at part level. A new SKU appears in a customer forecast Customer The check finds it not in the reference table Automatic The email lists it for the data team, with a link Automatic email The data team maps it SKU → internal P/N, family Data team Next refresh: demand counts at part level

Swipe sideways to see the full loop.

Who closes a mapping gap. A customer introduces a new SKU, the automation finds and reports it, and the data team maps it. The next refresh counts its demand at part level.

The results

The time for one forecast cycle fell from almost five days to less than one day. A cycle here means the elapsed time from downloading the customer forecasts to the planning team knowing what had changed. The reported time saving reflects the project team's account of the implementation. Detailed time-study records have not been published, so the result has not been independently verified.

The workflow also gives planners a change summary, flags unmapped customer part numbers and keeps past forecast versions for review.

4automated checks on every new forecast
3levels of drill-down, from partner to part number
<1 dayper forecast cycle, down from almost 5 days
1email with everything that needs attention
  • Calmer, proportionate planningPlanners respond to measured changes and fixed rules, then validate the likely cause before changing production or purchasing plans.
  • Faster forecast cyclesFrom almost five days to under one day per cycle, with the alert email arriving about an hour after each refresh.
  • Trend versus noiseEvery version is kept, so a steady drift stands out from a spike that reverses next time.
  • A shared calculation pathThe email and report use the same model and measures. Their results still depend on a successful, sufficiently recent refresh and a successful flow run.
  • Unmapped customer parts surfacedCustomer part numbers missing from the reference table are flagged for follow-up. Mapping coverage still depends on the data team maintaining the reference data.
  • Designed to extendNew partners, checks, thresholds or schedules can often be added, subject to source-file differences, model changes, testing and capacity constraints.

Evidence, methodology and limitations

KS AI & Cloud Solutions OPC Pvt Ltd published this implementation case study. The client's name, customer names and part identifiers are withheld for confidentiality. This section explains what the case study is based on and what readers should check for their own situation.

Implementation described

The workflow described here uses forecast files from SPS Commerce, Python-based standardisation, SharePoint, Power BI, DAX and Power Automate. These details are the publisher's account of the solution; the client's identity and production records are not published.

Illustrative chart data

The month-by-month quantities and percentage changes in the chart are illustrative examples, not customer production records. The 20% alert threshold is shown as an example rule in this case study; a deployment should set and document thresholds for its own risks and planning process.

Cycle-time outcome

The cycle went from almost five days to less than one day, measured as elapsed time from downloading the forecasts to the planning team knowing what had changed. This is the project team's account. Detailed time-study records have not been published, so the result has not been independently verified.

Demand-change hypotheses

A comparison identifies the size and location of a forecast change, but does not prove its cause. Production-plan changes, demand shifting between months, volatility, lead-time effects, inventory timing and mapping or source-data issues should be tested against customer schedules, purchase orders, inventory, lead times and source files.

Primary technical references

The following Microsoft documentation supports the connector, API permission, licensing-context and query-limit statements in this article. Platform details can change; verify them against the target tenant before implementation.

Technical documentation links checked on 24 October 2026.

Where else this approach works

This approach can also help other manufacturers and businesses that receive regular updates from customers, suppliers or internal systems. The goal is the same: compare each new file with an earlier one, find important changes and give the right team time to respond.

  • Automotive and truck suppliersSuppliers receiving rolling forecasts and release schedules from vehicle makers.
  • Aerospace and defenceSuppliers tracking rolling demand from prime contractors, part by part.
  • Consumer goodsBrands comparing retailer forecasts by customer and SKU.
  • Industrial distributionDistributors watching demand shifts by branch, customer and product family.
  • Electronics manufacturingContract manufacturers comparing customer build forecasts version by version.
  • Any recurring data dropPrice lists, inventory snapshots or order books where the change matters more than the total.

Frequently asked questions

What could explain changes across 30-, 90- and 120-day forecasts?

Possible causes include a revised vehicle production plan, demand moving between months, a forecast swing that may reverse, supplier lead times, stock or order timing, new part numbers, or a file/mapping problem. Check the forecast history, customer schedule, firm orders, stock and source files before treating any cause as confirmed.

How does this solution help investigate forecast changes?

It compares the new forecast with the previous version for the same future periods. It keeps old versions, flags large changes and identifies customer part numbers that are not mapped. Planners still need to check schedules, orders, stock, supplier lead times and source files to confirm the cause.

Why keep old forecast versions?

Old versions help planners tell a steady trend from a one-off swing and show how often each customer changes its forecast. They also support later forecast-accuracy checks when actual orders or shipments are available. Without history, every change can look equally urgent.

How often can it run?

The workflow can be scheduled around each customer's forecast cycle, subject to Microsoft refresh, connector, licensing and flow limits. In this design, the refresh and email run on a schedule. The alert is not instant unless the file collection and automation are built for immediate updates.

Who keeps the part mapping up to date?

The client's data team. The alert email lists customer part numbers that are missing from the reference table. The team adds the matching internal part number and part family. Power BI picks up the change at the next refresh.

Can Power Automate read data from a Power BI report?

Yes. Power Automate can run a query against a published Power BI data model and return the matching rows. The flow can then put those rows into an email, table or file. The required setup and permissions must be checked for your environment.

Do we need Power BI Premium for this?

There is no single yes/no answer for every company. The Power BI connector is listed as Standard in Power Automate, but the query API, workspace, user access and sharing rules still affect licensing. Before rollout, have the Microsoft 365 or Power BI administrator confirm the licences and permissions for the exact workspace and users. The required tenant setting must also be enabled.

What happens if a customer's file is missing?

Before a refresh, our team sends the data team a file check. It lists how many files arrived for each customer, plus each file's name and size. A zero count or a file that is much smaller than usual can be checked before the report refreshes. A late file is picked up by the next scheduled refresh if it arrives in time.

Will this work with our forecast source or ERP exports?

Usually, yes—if the source can provide the forecast as a file or through an API. The collection step would change to fit that source. The comparison model, report and alert process may stay similar, but they still need to be tested with the new data.

Still comparing forecasts by hand?

KS AI & Cloud Solutions builds Power BI, Power Automate and Microsoft Fabric solutions that turn recurring data drops into decisions. Tell us which forecasts you receive and how often, and we'll show you what they look like automated.

Our team includes Microsoft-certified Power BI practitioners. Meet the team.

Client names and figures are withheld or illustrative to protect confidentiality.