Forum Discussion
Sales Summary - Order Demand vs. Forecast Demand
Hello, I am trying to develop a better relationship with order data vs. forecast data for a range of forecasted materials. In the shared workbook, you will find a "Forecast" and an "Order vs. Forecast" query. These two are the primary focus.
Problem: This method currently works using the merge feature referencing the correct forecast "keys," but if there is no order demand there will also be no forecast demand listed.
Current Forecast = The forecast populated during a given month.
Locked Forecast = The forecast we used to submit orders to a plant. This would be the previous four months based on plant lead time.
Having the "0" order demand shown with the correct current and locked forecasts will be important to determine variances in sales. This will also help our team manage expected inventory across multiple plant locations.
Rather than using the merge feature, would there be another mehtod to create a better relationship between these two tables? I have tried loading these to the PowerPivot Data Model, and creating a bridge table with no luck. As you can image, I run into the problem with a many-to-many relationship that make the model ambiguous.
Any help is appreciated.
Hi jrhaney23 , yes, Use your dimension tables dCustomer, dMaterial and connect them:
fOrder[Customer] --> dCustomer[Customer]
fForecast[Customer] --> dCustomer[Customer]
fOrder[Forecast Material] --> dMaterial[Forecast Material]
fForecast[Forecast Material] --> dMaterial[Forecast Material]Use "ALL(dDate)" instead of "REMOVEFILTERS"
Use FILTER instead of direct column filters.
Please try below updated DAX measures.
Locked Forecast =
VAR SelectedMonth =
MAX ( dDate[Date] )VAR LockedMonth =
EDATE ( SelectedMonth, -4 )RETURN
CALCULATE (
SUM ( fForecast[Total Reel Demand] ),
ALL ( dDate ),
FILTER (
fForecast,
fForecast[Month] = SelectedMonth
&& fForecast[Forecast Key] = LockedMonth
)
)Current Forecast =
VAR SelectedMonth =
MAX ( dDate[Date] )RETURN
CALCULATE (
SUM ( fForecast[Total Reel Demand] ),
ALL ( dDate ),
FILTER (
fForecast,
fForecast[Month] = SelectedMonth
&& fForecast[Forecast Key] = SelectedMonth
)
)If you are facing any grand total inconsistencies, Please try below DAX.
Locked Forecast =
SUMX (
VALUES ( dDate[Date] ),
VAR SelectedMonth = dDate[Date]
VAR LockedMonth = EDATE ( SelectedMonth, -4 )
RETURN
CALCULATE (
SUM ( fForecast[Total Reel Demand] ),
ALL ( dDate ),
FILTER (
fForecast,
fForecast[Month] = SelectedMonth
&& fForecast[Forecast Key] = LockedMonth
)
)
)
18 Replies
- v-hashadapuCommunity Support
Hi jrhaney23 , Thank you for reaching out to the Microsoft Community Forum.
Your current approach is order-driven, so when orders don’t exist --> forecast disappears. But your requirement is forecast driven analysis, with orders as optional. Your M query starts from Source = Order. So, everything downstream depends on Order rows existing. If No Order --> no row --> no Forecast --> no Locked Forecast. That’s why JLG disappears unless you add a dummy order. Instead of Power Query, try to use DAX measures.
Please try the below steps to get the desire result.
- Create a fact + dimensions model, not merged tables:
Fact Tables (FactForecast, FactOrders)
Dimensions Tables (Material, Customer and Date (Month))- Please build the below relationships.
Orders --> Date Many-to-one
Forecast --> Date Many-to-one
Orders --> Customer Many-to-one
Forecast --> Customer Many-to-one
Orders --> Material Many-to-one
Forecast --> Material Many-to-one- Create Base measures.
Order Demand =
SUM ( Orders[Order Demand] )Current Forecast =
SUM ( Forecast[Total Reel Demand] )Locked Forecast =
CALCULATE (
SUM ( Forecast[Total Reel Demand] ),
DATEADD ( 'Date'[Date], -4, MONTH )
)Note: This measure will shifts the filter context 4 months back.
Forecast table must be connected to a proper Date table, not raw Month columns.
Date Table =
CALENDAR ( DATE(2024,1,1), DATE(2027,12,31) )Month = DATE(YEAR([Date]), MONTH([Date]), 1)
- jrhaney23Frequent Visitor
Hi v-hashadapu,
Getting a bit closer here. Unfortunately, it looks like the values for current and locked forecast are incorrect. Below are what my relationships look like.
Obviously, I would need to create a bridge table to solve the many-to-many relationship with the material.
Perhaps I would need to create another bridge table to filter the correct forecast dates/periods from fForecast?
Let me know your thoughts when possible, or if you need more data/context.
- v-hashadapuCommunity Support
Hi jrhaney23 , To understand and answer your issue completely, please provide sample data that covers your issue or question completely, in a usable format (not as a screenshot or a .pbix). Include all the necessary details, detailing your scenario and issue as clearly and fully as possible.
Do not include sensitive information. Do not include anything that is unrelated to the issue or question. Please show the expected outcome based on the sample data you provided.
Need help uploading data? https://community.fabric.microsoft.com/t5/Community-Blog/How-to-provide-sample-data-in-the-Power-BI-...
- BA_PeteSuper User
Hi jrhaney23 ,
I'm not able to download the file due to org restrictions but, if you're able to share small, copyable, examples of each of the tables you're referencing I'm happy to try and help.
The ideal format would be M code for the data based on JSON Binary (by using 'Enter Data' function in Power Query) if possible, as this can then include all of the correct data types etc.
Please remove/anonymise any sensitive data.
Pete
- jrhaney23Frequent Visitor
Hi BA_Pete,
I'll try my best to share some additional details here. Below you will find the three tables I'm working with.
Table 1 = Order
Table 2 = Forecast
Table 3 = Order vs Forecast
The goal is to analyze order demand vs. forecast date from a given period (current vs. locked forecast data (4 months prior)).
These photos highlight that since customer JLG has no order data, there is no forecast data populated. BUT there is locked forecast data 4 months prior (forecast key 11/1/2025) from which we had planned to produce. I would like to keep that data in my analysis.
Below is the M Code for the Order vs Forecast Query:
let Source = Order, #"Removed Other Columns" = Table.SelectColumns(Source,{"Forecast Material", "Customer", "Order Demand", "Month"}), #"Grouped Rows" = Table.Group(#"Removed Other Columns", {"Forecast Material", "Customer", "Month"}, {{"Order Demand", each List.Sum([Order Demand]), type nullable number}}), #"Reordered Columns" = Table.ReorderColumns(#"Grouped Rows",{"Forecast Material", "Customer", "Order Demand", "Month"}), #"Current Forecast Key" = Table.AddColumn(#"Reordered Columns", "Current Forecast Key", each [Month]), #"Locked Forecast Key" = Table.AddColumn(#"Current Forecast Key", "Locked Forecast Key", each Date.AddMonths([Month], -4)), #"Changed Type" = Table.TransformColumnTypes(#"Locked Forecast Key",{{"Current Forecast Key", type date}, {"Locked Forecast Key", type date}}), #"Current Forecast Merge" = Table.NestedJoin(#"Changed Type", {"Current Forecast Key", "Forecast Material", "Customer", "Month"}, Forecast, {"Forecast Key", "Forecast Material", "Customer", "Month"}, "Forecast", JoinKind.LeftOuter), #"Expanded Forecast" = Table.ExpandTableColumn(#"Current Forecast Merge", "Forecast", {"Total Reel Demand"}, {"Total Reel Demand"}), #"Renamed Columns1" = Table.RenameColumns(#"Expanded Forecast",{{"Total Reel Demand", "Current Forecast Demand"}}), #"Locked Forecast Merge" = Table.NestedJoin(#"Renamed Columns1", {"Locked Forecast Key", "Forecast Material", "Customer", "Month"}, Forecast, {"Forecast Key", "Forecast Material", "Customer", "Month"}, "Forecast", JoinKind.LeftOuter), #"Expanded Forecast1" = Table.ExpandTableColumn(#"Locked Forecast Merge", "Forecast", {"Total Reel Demand"}, {"Total Reel Demand"}), #"Renamed Columns2" = Table.RenameColumns(#"Expanded Forecast1",{{"Total Reel Demand", "Locked Forecast Demand"}}), #"Grouped Rows1" = Table.Group(#"Renamed Columns2", {"Forecast Material", "Customer", "Month"}, {{"Order", each List.Sum([Order Demand]), type nullable number}, {"Current Forecast", each List.Sum([Current Forecast Demand]), type nullable number}, {"Locked Forecast", each List.Sum([Locked Forecast Demand]), type nullable number}}), #"Sorted Rows" = Table.Sort(#"Grouped Rows1",{{"Month", Order.Descending}}), #"Replaced Value" = Table.ReplaceValue(#"Sorted Rows",null,0,Replacer.ReplaceValue,{"Current Forecast", "Locked Forecast"}), #"∆" = Table.AddColumn(#"Replaced Value", "∆", each [Order]-[Locked Forecast], type number) in #"∆"I'm thinking of pivoting from trying to solve this via M Code to DAX since this is evolving more into a calculation rather than ETL. I have tried a bridge table, but without the relevant data from 'Order', this would most likely result in the same situation.
Let me know your thoughs when possible, or if you need more information.
- BA_PeteSuper User
Hi jrhaney23 ,
The M code you've pasted still doesn't have any copyable data in it. This references a table I don't have, so can't recreate your scenario my side to start trying to help:
Use the Enter Data function in Power Query to just copy the tables you've made screenshots of (top left of table in PQ > 'Copy enttire table') and paste them in there then, when you 'OK' that pasted table, you'll see that there's M code for that table based on a JSON Binary. Copy that M code and paste here and that will give me a ready-made table in PQ that's exactly the same as what you're seeing in your screenshots.
To note: It's after 6pm here now so I'm unlikely to pick this up over the weekend to be honest.
Pete
- dufoq3Community Champion
Hi jrhaney23, I've edited your Order vs Forecast query a bit:
Is this what you want?let Source = Order, #"Removed Other Columns" = Table.SelectColumns(Source,{"Forecast Material", "Customer", "Order Demand", "Month"}), #"Grouped Rows" = Table.Group(#"Removed Other Columns", {"Forecast Material", "Customer", "Month"}, {{"Order Demand", each List.Sum([Order Demand]), type nullable number}}), #"Reordered Columns" = Table.ReorderColumns(#"Grouped Rows",{"Forecast Material", "Customer", "Order Demand", "Month"}), #"Current Forecast Key" = Table.AddColumn(#"Reordered Columns", "Current Forecast Key", each [Month]), #"Locked Forecast Key" = Table.AddColumn(#"Current Forecast Key", "Locked Forecast Key", each Date.AddMonths([Month], -4)), #"Changed Type" = Table.TransformColumnTypes(#"Locked Forecast Key",{{"Current Forecast Key", type date}, {"Locked Forecast Key", type date}}), // Full outer join! #"Current Forecast Merge" = Table.NestedJoin(#"Changed Type", {"Current Forecast Key", "Forecast Material", "Customer", "Month"}, Forecast, {"Forecast Key", "Forecast Material", "Customer", "Month"}, "Forecast", JoinKind.FullOuter), #"Expanded Forecast" = Table.ExpandTableColumn(#"Current Forecast Merge", "Forecast", {"Forecast Material", "Customer", "Month", "Total Reel Demand"}, {"Forecast Material.1", "Customer.1", "Month.1", "Current Forecast Demand"}), #"Locked Forecast Merge" = Table.NestedJoin(#"Expanded Forecast", {"Locked Forecast Key", "Forecast Material", "Customer", "Month"}, Forecast, {"Forecast Key", "Forecast Material", "Customer", "Month"}, "Forecast", JoinKind.LeftOuter), #"Expanded Forecast1" = Table.ExpandTableColumn(#"Locked Forecast Merge", "Forecast", {"Total Reel Demand"}, {"Locked Forecast Demand"}), FilledCols = Table.FromRecords(Table.TransformRows(#"Expanded Forecast1", each [ A = List.Select(Record.FieldNames(_), (x)=> Text.Contains(x, ".1")), B = _ & Record.Combine(List.Transform(A, (x)=> let B1 = Text.TrimEnd(x, Text.ToList(".1")) in Record.AddField([], B1, if Record.Field(_, B1) = null then Record.Field(_, x) else Record.Field(_, B1)))) ][B] ), Value.Type(Table.FirstN(#"Expanded Forecast1", 0)) ), #"Grouped Rows1" = Table.Group(FilledCols, {"Forecast Material", "Customer", "Month"}, {{"Order", each List.Sum([Order Demand]), type nullable number}, {"Current Forecast", each List.Sum([Current Forecast Demand]), type nullable number}, {"Locked Forecast", each List.Sum([Locked Forecast Demand]), type nullable number}}), #"Sorted Rows" = Table.Sort(#"Grouped Rows1",{{"Month", Order.Descending}}), #"∆" = Table.AddColumn(#"Sorted Rows", "∆", each ([Order] ?? 0) - ([Locked Forecast] ?? 0), type number) in #"∆"- jrhaney23Frequent Visitor
Hi dufoq3, I can see you may be trying to include a custom function to address the issue, but the values for the sum of "Current Forecast" and "Locked Forecast" are incorrect when I load to a pivot table.
I will focus on BL834R500FT/JLG for the period of March 2026 for example. Referencing the attached photo, the correct value that I would expect would be 0, as there was no forecast data (located by filtering the forecast tab) submitted by the customer for this month. For the locked forecast value, I would expect the value to be 0.12 since this value was forecasted 4 months prior for consumption in March 2026. The values work fine only if order demand actually occurs within the evaluated month.
To verify, I added a dummy order line in the "Deliveries" tab, but I would like to avoid this.
Let me know if this helps.
- v-hashadapuCommunity Support
Hi jrhaney23 , hope you are doing great. May we know if your issue is solved or if you are still experiencing difficulties. Please share the details as it will help the community, especially others with similar issues.
- v-hashadapuCommunity Support
Hi jrhaney23 ,
Hope you're doing okay! May we know if it worked for you, or are you still experiencing difficulties? Let us know — your feedback can really help others in the same situation.