Forum Discussion

EvinMensler's avatar
EvinMensler
Regular Visitor
8 months ago
Solved

Calculating expression based on related tables

Hi.

I have a table with the forecast quantity of some products and another one with their ingredients and ratios.

Now i can get the needed ingredient quantities based on just these two tables; the problem is that some ingredients also have their components and i cant figure out how to calculate the total quantity.

pbi link 

the result:

ingredient_name = Ingredients_list[ingredient_name]

quantity_forecast = CALCULATE(SUM(Forecast[quantity]), SUMMARIZE(Ingredients, Forecast[product_name], Ingredients[ingredient_name]))
ratio_forecast = AVERAGE(Ingredients[ratio])
quantity_ingredients = [quantity_forecast] * [ratio_forecast]
ratio_components = AVERAGE(Components[ratio])
quantity_components = [quantity_ingredients] * [ratio_components]
quantity_components_i shoud get = ???
ingredient_namequantity_forecastratio_forecastquantity_ingredientsratio_componentsquantity_componentsquantity_components_i shoud get
Beef   0,99 35,07
Bun1270,5063,5   
Cheese5060,1996,1   
Dough5060,46232,8   
Flour   0,95 221,12
Lettuce2770,2876,2   
Meat1270,2126,7   
Oil5060,0735,40,301111,64
Onion1270,1012,7   
Salami5060,1155,7   
Salt   0,01 0,35
Sauce2770,0719,4   
Spices   0,20 3,79
Tomato sauce5060,1786,0   
Tomatoes1500,5075,00,947180,86

 

forecast:

product_nameproduct_idquantity
Salad100150
Pizza101506
Burger102127
Sandwich10312

ingredients:

product_nameproduct_idingredient_nameingredient_idratio
Salad100Lettuce10,45
Salad100Tomatoes20,5
Salad100Sauce30,05
Pizza101Dough110,46
Pizza101Tomato sauce120,17
Pizza101Oil2020,07
Pizza101Cheese130,19
Pizza101Salami140,11
Burger102Bun160,5
Burger102Lettuce180,1
Burger102Onion190,1
Burger102Sauce30,09
Burger102Meat220,21

components:

ingredient_nameingredient_idcomponent_namecomponent_idratio
Sauce3Spices2010,2
Sauce3Oil2020,8
Dough11Flour2030,95
Dough11Oil2020,05
Tomato sauce12Tomatoes20,94
Tomato sauce12Oil2020,06
Meat22Beef2060,99
Meat22Salt2090,01

ingredients_list:

ingredient_nameingredient_id
Spices201
Oil202
Flour203
Tomatoes2
Beef206
Salt209
Lettuce1
Sauce3
Dough11
Tomato sauce12
Cheese13
Salami14
Bun16
Lettuce18
Onion19
Meat22

 

Relations:

Ingredients_list[ingredient_id] 1:* Components[component_id]

Ingredients_list[ingredient_id] 1:* Ingredients[ingredient_id]

Ingredients[product_id] 1:* Forecast[product_id]

 

  • Hi EvinMensler ,

    This is a classic Bill of Materials (BOM) Explosion scenario. It is tricky because you are dealing with two layers of demand simultaneously:

    1. Direct Demand: When the product uses the item directly (e.g., Pizza uses Oil).

    2. Indirect (Derived) Demand: When the product uses an ingredient (like Dough), and that ingredient uses the item (Oil).

    The problem with your current calculation is Context. When you select "Oil" in your visual, Power BI filters everything to "Oil". It cannot "see" the demand for "Dough" or "Sauce" unless we explicitly tell DAX to look for the Parent Items of the selected component.

    Here is how you can calculate the Indirect Demand and add it to your total.

    The Solution Measure

    We need to iterate through the Components table to find all the "Parents" of the selected ingredient, calculate their demand, and then multiply by the component ratio.

    Total Quantity Required = 
    VAR DirectDemand = [quantity_ingredients]
    
    VAR IndirectDemand = 
        SUMX(
            -- Iterate through the rows in the Components table (filtered by the selected Ingredient)
            'Components',
            
            VAR ComponentRatio = 'Components'[ratio]
            VAR ParentID = 'Components'[ingredient_id] -- The ID of the Parent (e.g., Dough)
            
            -- Calculate the demand for the Parent Ingredient
            VAR ParentDemand = 
                CALCULATE(
                    [quantity_ingredients],
                    -- Step 1: Remove the filter from the selected Child (Oil)
                    ALL('Ingredients_list'),
                    -- Step 2: Apply the filter for the Parent (Dough)
                    'Ingredients_list'[ingredient_id] = ParentID
                )
                
            RETURN ParentDemand * ComponentRatio
        )
    
    RETURN DirectDemand + IndirectDemand

    How it works:

    1. DirectDemand: Keeps your existing logic for Level 1 usage.

    2. IndirectDemand: It looks at the Components table. If "Oil" is selected, it finds rows where Oil is a component (Parents: Dough, Sauce, etc.).

    3. CALCULATE: This is the magic part. It uses ALL to ignore that you selected "Oil", and then uses the ParentID to pretend we selected "Dough". It calculates how much Dough is needed using your existing measure.

    4. Result: It multiplies the Dough Demand * Oil Ratio and sums it up for all parents.

    Important Note: This solution assumes a 2-Level Hierarchy (Product > Ingredient > Component). If you have deeper levels (e.g., Component > Sub-Component), you would need a more complex recursive pattern using PATH functions or Python/SQL prep. But for the structure you shared, this DAX pattern is the most efficient way.

    Give this a try and let me know if the numbers match your expectations!


    If my response resolved your query, kindly mark it as the Accepted Solution to assist others. Additionally, I would be grateful for a 'Kudos' if you found my response helpful.
    This response was assisted by AI for translation and formatting purposes.

4 Replies

  • Hi EvinMensler ,

    This is a classic Bill of Materials (BOM) Explosion scenario. It is tricky because you are dealing with two layers of demand simultaneously:

    1. Direct Demand: When the product uses the item directly (e.g., Pizza uses Oil).

    2. Indirect (Derived) Demand: When the product uses an ingredient (like Dough), and that ingredient uses the item (Oil).

    The problem with your current calculation is Context. When you select "Oil" in your visual, Power BI filters everything to "Oil". It cannot "see" the demand for "Dough" or "Sauce" unless we explicitly tell DAX to look for the Parent Items of the selected component.

    Here is how you can calculate the Indirect Demand and add it to your total.

    The Solution Measure

    We need to iterate through the Components table to find all the "Parents" of the selected ingredient, calculate their demand, and then multiply by the component ratio.

    Total Quantity Required = 
    VAR DirectDemand = [quantity_ingredients]
    
    VAR IndirectDemand = 
        SUMX(
            -- Iterate through the rows in the Components table (filtered by the selected Ingredient)
            'Components',
            
            VAR ComponentRatio = 'Components'[ratio]
            VAR ParentID = 'Components'[ingredient_id] -- The ID of the Parent (e.g., Dough)
            
            -- Calculate the demand for the Parent Ingredient
            VAR ParentDemand = 
                CALCULATE(
                    [quantity_ingredients],
                    -- Step 1: Remove the filter from the selected Child (Oil)
                    ALL('Ingredients_list'),
                    -- Step 2: Apply the filter for the Parent (Dough)
                    'Ingredients_list'[ingredient_id] = ParentID
                )
                
            RETURN ParentDemand * ComponentRatio
        )
    
    RETURN DirectDemand + IndirectDemand

    How it works:

    1. DirectDemand: Keeps your existing logic for Level 1 usage.

    2. IndirectDemand: It looks at the Components table. If "Oil" is selected, it finds rows where Oil is a component (Parents: Dough, Sauce, etc.).

    3. CALCULATE: This is the magic part. It uses ALL to ignore that you selected "Oil", and then uses the ParentID to pretend we selected "Dough". It calculates how much Dough is needed using your existing measure.

    4. Result: It multiplies the Dough Demand * Oil Ratio and sums it up for all parents.

    Important Note: This solution assumes a 2-Level Hierarchy (Product > Ingredient > Component). If you have deeper levels (e.g., Component > Sub-Component), you would need a more complex recursive pattern using PATH functions or Python/SQL prep. But for the structure you shared, this DAX pattern is the most efficient way.

    Give this a try and let me know if the numbers match your expectations!


    If my response resolved your query, kindly mark it as the Accepted Solution to assist others. Additionally, I would be grateful for a 'Kudos' if you found my response helpful.
    This response was assisted by AI for translation and formatting purposes.

      • burakkaragoz's avatar
        burakkaragoz
        Icon for Super User rankSuper User

        Haha, thank you EvinMensler ! I appreciate the kind words.

        DAX Context Transition can definitely feel like dark magic sometimes. But once you master how CALCULATE and ALL work together, you are the one controlling the wand.

        Since the formula is working for you, could you please mark the previous reply as the Accepted Solution? It helps others find this BOM pattern quickly when they get stuck on the same issue.

        Happy reporting!