Sep 12 2021 10:37 PM
Hello,
I have an interesting problem to work out and I'm sure there's a very efficient and elegant solution using Power Query. Before I start tinkering myself (in probably not the most efficient way): how would you approach this?
I have a dataset with parents, children and quantities of children in the parent.
I also have sales figures (quantities) of (some of the) parents and children. Some components are sold both as-is, and as part of a parent.
What I need is the quantity of all components, flattened. See below (and attached workbook):
Any help to put me on the right track would be greatly appreciated!
Note that:
Sep 18 2021 11:14 AM
Eventually you'll have to want to find out if you're making a profit! Since you're doing that now, why not incorporate that aspect now and find out if there will be issues now.
can you imagine creating a system to calculate the quantities then later on you realize you're going to have to change it "again".
Sep 18 2021 12:18 PM - edited Sep 18 2021 12:39 PM
That's an interesting thought, but in this case it's a question of how much (in quantity) parts we need to order. So I really just need the desired result as stated in the workbook (and the screenshot).
The actual use case is that we have a bunch of finished products with their parts and subparts. Based on the sales quantities of the finished products, I need the quantities of the corresponding parts & subparts.
Sep 18 2021 01:17 PM
I try to understand the BOM structure. So I made a BOM explosion.
I noticed that TOYCARSET1 and TOYCARSET2 are the top levels. Which is not in line with the sales table showing also TOYCAR and WHEEL.
Then I noticed that TYRE-OUTERTYRE and TYRE-INNERTYRE are on two levels.
Does that make sense?
Sep 18 2021 01:37 PM
That's great, thank for sharing. I'd only suggest to expand on any number of levels.
First, let create the function which calculates number of subparts in previous level. That is exactly your query defines as fntCalc() function:
( tCalc as table ) =>
let
Source = tCalc,
// NEXT IS INITAL QUERY, SKIPPED HERE
// ...
in
#"Renamed Columns1"
Next function fntCalcSubParts() iterates level by level and stops iterations if previous level table is identical to one on current level. This is function with recursion:
( tCalc as table ) =>
let
nextLevel = fntCalc( tCalc )
in
if tCalc = nextLevel
then nextLevel
else fntCalcSubParts (nextLevel)
Use it to calculate final subparts table
let
Source = Excel.CurrentWorkbook(){[Name="tBOM"]}[Content],
nextLevel = fntCalcSubParts( Source )
in
nextLevel
And use above as source for you tResult query.
All above is in Variant 02 group in attached.
Sep 18 2021 01:48 PM
@Detlef Lewin , here is not exact hierarchy.
A could be part of B
B could be part of C; and could be part of D; and could be sold as separate product
A could be part of D not as part of B but directly (in addition to be in B)
etc
Sep 18 2021 02:01 PM
Points to Ponder in your Parent Child hierarchy
if you manufacture 500 toy cars
and you can only purchase toy wheels by cases
each case contains 20 boxes of wheels
each box contains 100 wheels
How many cases of wheels do you have to buy in order to fulfill your manufacturing needs for the 500 toy cars.
considering each car only needs 4 wheels each never mind the cost of each wheel but the question is how many cases do you need to order for the 500 toy cars
and it boils down to the lowest unit of measure.
Sep 19 2021 02:11 AM - edited Sep 19 2021 02:17 AM
@Sergei Baklan This is exciting, we're really getting there! I nailed the result by changing my approach, so that part of the problem (the function in itself) is now solved.
But now it needs to be improved using a conditional loop, somewhat similar to what you proposed, but what I do now is calculate 5 separate tables and append them afterwards, so I don't really know how to implement your function strategy.
Could you give a hand please?
So the result is now exactly what I wanted:
And I do this using one function and the following queries:
So basically, what I need, in plain English, is:
If we can get this done, we're completely there!
Sep 19 2021 11:56 AM
SolutionI didn't dive a deep into calculations, here is only suggestion for iterations.
We keep as it is now tBOM and tCalc1.
Levels are iterated by fntCalcQty:
( tCalc as table, tBasis as table, tAccumulate as table ) =>
let
nextLevel = fnCalc( tCalc, tBasis ),
addTable = Table.Combine( {tAccumulate, nextLevel} )
in
if Table.RowCount( nextLevel ) = 0
then tAccumulate
else fntCalcQty (nextLevel, tBasis, addTable)
It iterates till first empty table appears.
Final result is calculated as
let
Source = fntCalcQty( tCalc1, tBOM, tCalc1),
addSales = Table.Combine( {Source, tSalesRenamed} ),
#"Grouped Rows" = Table.Group(
addSales, {"Subpart"},
{{"TotalQty", each List.Sum([TotalQty]), type nullable number}}
),
#"Sorted Rows" = Table.Sort(#"Grouped Rows",{{"Subpart", Order.Ascending}})
in
#"Sorted Rows"
Above is in Variant 03 group in attached.
Sep 19 2021 12:19 PM
Sep 19 2021 04:55 PM