2

I have a field collection with unlimited values.

Inside this field collection, there is a field called Price (field_price).

I would like to addup all prices that are inside the field collection to get the total value (which I will then set to the total value field that is inside the content type).

I know I can get all the prices like this:

enter image description here

But the tricky part is adding them up.

I tried adding Calculate a value to the loop but for data selectors I only get something like this:

enter image description here

And list-item:field-price is not available either.

All signs point that this is not the right approach.

Race ya to the answer.


And list-item:field-price is not available either.

Turns out that I was in the right path all along, the reason this was not available to me when I tried to do calculate a value is because I had changed my price_field type to text-field. You can't do calculations on non numeric fields. After I changed it back to decimal, I was able to use list-item:field_price.

4
  • Is this your next Rules challenge? yes By when do you need a "solution" the sooner the better. You want an answer, or a hint (in the format of a comment) sounds like you already know the answer?.
    – No Sssweat
    Commented Nov 22, 2016 at 10:21
  • This sounds like it will work. Give me a few mins to try.
    – No Sssweat
    Commented Nov 22, 2016 at 10:48
  • 1
    @Pierre.Vriens You have 5 mins left ... what the... lol. return that sum to the Rule (using "provides" for that component parm!!!) this is news to me.
    – No Sssweat
    Commented Nov 22, 2016 at 10:51
  • 1
    @Pierre.Vriens how does the passing back part work? does it do it automatically? or do I need to add some action? looks to me as if it is automatic.
    – No Sssweat
    Commented Nov 22, 2016 at 10:57

3 Answers 3

5

At first I thought your Rule needs to perform a Rules Component with 2 parameters (say 'current_total' and 'next_item') and simply calculate the sum of both, and return that sum to the Rule (using "provides" for that component parm). And then in your Rule update current_total with the provided sum, before the loop in your Rule ends.

However you don't need such Rules Component, only 1 rule will do. Have a look at this Rule (in Rules export format), which IMO is a prototype that does exactly what this question is about:

{ "rules_calculate_sum_of_prices_in_all_field_collection_items" : {
    "LABEL" : "Calculate sum of prices in all field collection items",
    "PLUGIN" : "reaction rule",
    "OWNER" : "rules",
    "REQUIRES" : [ "rules" ],
    "ON" : { "node_view--article" : { "bundle" : "article" } },
    "IF" : [
      { "entity_has_field" : { "entity" : [ "node" ], "field" : "field_article_details" } }
    ],
    "DO" : [
      { "drupal_message" : { "message" : "\u003Cstrong\u003EDrupal calculator\u003C\/strong\u003E started ..." } },
      { "variable_add" : {
          "USING" : { "type" : "decimal", "value" : "0" },
          "PROVIDE" : { "variable_added" : { "total_price" : "Price total" } }
        }
      },
      { "LOOP" : {
          "USING" : { "list" : [ "node:field-article-details" ] },
          "ITEM" : { "article_details_item" : "Article details item" },
          "DO" : [
            { "data_calc" : {
                "USING" : {
                  "input_1" : [ "total-price" ],
                  "op" : "+",
                  "input_2" : [ "article-details-item:field-price" ]
                },
                "PROVIDE" : { "result" : { "calculation_result" : "Calculation result" } }
              }
            },
            { "data_set" : { "data" : [ "total-price" ], "value" : [ "calculation-result" ] } },
            { "drupal_message" : { "message" : "After adding a price of \u003Cstrong\u003E[article-details-item:field-price]\u003C\/strong\u003E for field collection item with id \u003Cstrong\u003E[article-details-item:item-id]\u003C\/strong\u003E, subtotal is \u003Cstrong\u003E[calculation-result:value]\u003C\/strong\u003E." } }
          ]
        }
      },
      { "drupal_message" : { "message" : "The \u003Cstrong\u003ETotal price\u003C\/strong\u003E for all prices included as field collection items is \u003Cstrong\u003E[total-price:value]\u003C\/strong\u003E." } },
      { "drupal_message" : { "message" : "\u003Cstrong\u003EDrupal calculator\u003C\/strong\u003E ended ..." } }
    ]
  }
}

Some more details about this rule are below ...

Rules Event:

Content is viewed (of type Article), adapt the machine name of the content type article to whatever fits (or use any other Rules Event that fits).

Rules Condition:

Entity has field, whereas the entity is "node", and the machine name of my field collection field is field_article_details (adapt this machine name to whatever fits, but make sure you use the field collection field itself).

Rules Actions:

Here is where the magic will happen ... These are the Rules Actions involved:

  1. Show a message on the site, with a message like so:

    Drupal calculator started ...

  2. Add a variable, whereas it is a variable named total_price, decimal (2 digits), initial value 0.

  3. Add a loop, to iterate over each item of my field collection field (with machine name field_article_details), and perform these Rules Actions for each iteration:

    • Calculate a value, which calculates the sum of total_price (defined in Rules Action 2 above) and article-details-item:field-price (this is the machine name of the field in the field collection that contains the prices, decimal with 2 digits), and stores the result (sum) in variable calculation_result.

    • Set a data value, which simply copies the value stored in variable calculation_result in my total_price (defined in Rules Action 2 above). Remark: not sure (not tested), but maybe this calculation_result variable can be replaced straight by total_price (in the previous action), so that you would not need this action.

    • Show a message on the site, with a message like so:

    After adding a price of 3.40 for field collection item with id 3, subtotal is 15.00.

  4. Show a message on the site, with a message like so:

    The Total price for all prices included as field collection items is 26.23.

  5. Show a message on the site, with a message like so:

    Drupal calculator ended ...

Obviously, this rule is rather a prototype. After you're convinced it works as it should, just remove all Rules Actions with Show a message on the site. So that only item 2 and 3 (without its last sub-bullet) is left as Rules Actions.

Showtime ...

Here is a sample of my test results, i.e. the Drupal messages that are shown:

Drupal calculator started ...
After adding a price of 2.45 for field collection item with id 1, subtotal is 2.45.
After adding a price of 9.15 for field collection item with id 2, subtotal is 11.60.
After adding a price of 3.40 for field collection item with id 3, subtotal is 15.00.
After adding a price of 1.23 for field collection item with id 4, subtotal is 16.23.
The Total price for all prices included as field collection items is 26.23.
Drupal calculator ended ...

More info

Processing Field collection items with Rules is fun, really! If you're not familiar with that, try to first digest the answer to "How to iterate over all field collection items in an eMail body using Rules?".

5
  • 2
    Fascinating, excellent work @Pierre.Vriens, it's worth noting that this would (probably) inherently be more secure than the Computed Field route as it doesn't need to rely on inputting custom PHP code. There would also be more flexibility over when to trigger the calculation. Commented Nov 22, 2016 at 14:41
  • After re-reading your answer more carefully, seems like we cameup with the same answer, hence I am deleting mine.
    – No Sssweat
    Commented Nov 22, 2016 at 15:17
  • @NoSssweat : since "I" can still see your deleted answer (one of the perks for users with sufficient rep ...), I think you should undelete it. Even if it is only because of the Rules UI screenprint in your answer (easier to digest for those who don't speak the Rules-export language). If you do, you may also want to explain what the purpose of the "fetch entity by id" actio is in your answer (are you sure you need that?). Commented Nov 22, 2016 at 15:26
  • @Pierre.Vriens I think you meant by property. Yeah, that is not needed, it got there from testing other routes. I will make a correct screenshot of it tomorrow.
    – No Sssweat
    Commented Nov 22, 2016 at 15:30
  • @NoSssweat yes by property ... BTW why defer something to tomorrow, if you can still do so ... AFTER tomorrow ... Commented Nov 22, 2016 at 15:36
2

I've done this for a muti-valued entityreference field using calculated fields, the general premise is to add a hidden computed field to the entity holding the field collection, it's not particularly easy but it's possible - you'll need to write some custom PHP code to achieve this. I'm including my code (field names changed to generics) below:

// Setup Entity Metadata Wrapper.
$wrapper = entity_metadata_wrapper($entity_type, $entity);
// Loop through field_collection.
foreach ($wrapper->field_collection->getIterator() as $related_wrapper) {
  // Expose the parts of the commerce_price field type.
  $price_field_wrapper = commerce_price_wrapper_value($related_wrapper, 'commerce_price_field');
  // Get the amount from the price field.
  $price_field_amount = $price_field_wrapper['amount'];
  // Get the currency type.
  $price_field_currency_code = $price_field_wrapper['currency_code'];
  // Use the amount and currency to covert to a decimal value.
  $price = commerce_currency_amount_to_decimal($price_field_amount, $price_field_currency_code);
  // Increment $total by the decimal stored in $price.
  $total += $price;
}

// Set the field value to $total formatted to 2 decimal places, with no 1000's separator.
$entity_field[0]['value']  = number_format($total, 2, '.', '');
10
  • Part of the question is "with Rules" (eg: at the end of the title). Maybe I missing something, but nowhere in this answer I see anything that smells like it is related to Rules. Commented Nov 22, 2016 at 10:43
  • I appreciate your time and effort to help. Unfortunately, I am looking for a rules answer only, as I was tasked to do it with Rules. Hopefully this will help/teach others though, so don't delete your programmatic answer.
    – No Sssweat
    Commented Nov 22, 2016 at 10:44
  • I didn't see the comment chain before I posted this, that said you could use the value of the computed field in Rules - thus a Rules solution :-p - I'll work on a Rules solution. Commented Nov 22, 2016 at 10:48
  • @HomoTechsual : don't worry (too much). Note however that the title was there before any comments got posted ... Though with your latest comment you made me curious also about how to use that computed field vaue in a rule. Hopefully without adding custom PHP in a Rules Condition or Rules Action (which is stored in the database and hence adds an extra securiity risk). Commented Nov 22, 2016 at 11:00
  • @HomoTechsual (again, sorry) ... now I'm curious about your feedback about my answer, with no custom code involved ... And not that difficult either, would you agree? Just a matter of knowing some of the Rules features that are not commonly used ... Oeps, this comment crosses your comment below my answer already. You call it fascinating, I call it "having fun with a puzzle called Rules ...". Commented Nov 22, 2016 at 14:44
1

I came up with the same answer as the accepted answer, as per request I have un-deleted my answer.

Set the field_total to 0

Then in loop add field_total + list-item:field_price and set result to field_total.

enter image description here

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.