1

Certain products on the site can't be shipped using a certain shipping service as they are classified as dangerous goods.

So I need to be able to loop through all the items in the cart, check to see if the product has is marked as dangerous (field_dangerous_goods on the commerce_product entity), if any of the products in the order are marked as dangerous goods then that shipping service shouldn't be shown to the user.

I've tried with various different rule components to try and create what I need with not even a glimmer of hope.

6

Figured it out.

Firstly, you'll need Conditional Rules.

Create a new rule component.

For the variables, add Commerce Line Item as a Parameter and add a Integer as Provided.

Under Conditions, add Entity has field, select commerce_product. Then add another Entity has field and select your field_dangerous_goods field from line-item:commerce-product. Lastly add a data comparison to check the status of the dangerous goods field. In my case I just had a checkbox, so I used Data value is empty and checked the Negate box.

This makes the condition return TRUE if the current line item is a dangerous item.

Under Actions, choose Add action and select the Set a data value option. For this set the parameter to the variable you created when you set up the component. Set the value to 1.

{ "rules_dangerous_goods" : {
    "LABEL" : "Dangerous goods",
    "PLUGIN" : "rule",
    "OWNER" : "rules",
    "REQUIRES" : [ "rules" ],
    "USES VARIABLES" : {
      "line_item" : { "label" : "Line Item", "type" : "commerce_line_item" },
      "dangerous_goods" : { "label" : "Dangerous Goods", "type" : "integer", "parameter" : false }
    },
    "IF" : [
      { "entity_has_field" : { "entity" : [ "line-item" ], "field" : "commerce_product" } },
      { "entity_has_field" : {
          "entity" : [ "line-item:commerce-product" ],
          "field" : "field_dangerous_goods"
        }
      },
      { "NOT data_is_empty" : { "data" : [ "line-item:commerce-product:field-dangerous-goods" ] } }
    ],
    "DO" : [ { "data_set" : { "data" : [ "dangerous-goods" ], "value" : "1" } } ],
    "PROVIDES VARIABLES" : [ "dangerous_goods" ]
  }
}

Then for your shipping service rule, under Actions, choose Add action. Select Add a variable, I called mine Contains Dangerous Goods (contains_dangerous_goods). Set the value to 0.

Add a loop.

Then Add action to the loop. Select your component. You'll need to set the variable name, this has to be different to the Contains Dangerous Goods variable from before, I just called mine Dangerous Good (dangerous_good).

Now choose Add conditional to the loop. Then for the conditional, choose Add if. Select the *dangerous_good* which we just set in the loop in the component action. Set the value to 1.

Within the If, choose Add action. Select Set a data value. For the field, choose the contains_dangerous_goods value. This is the initial variable we set for the shipping service rule. Set the value to 1.

Now outside of the loop, Add conditional. Then Add if. Choose the *contains_dangerous_goods* field and set the value to 0.

Then within the If, add your shipping rate.

{ "commerce_shipping_service_standard_up_to_0_5kg" : {
    "LABEL" : "Rate Standard - Up to 0.5kg",
    "PLUGIN" : "rule",
    "OWNER" : "rules",
    "REQUIRES" : [
      "commerce_order",
      "commerce_physical",
      "rules",
      "rules_conditional",
      "commerce_shipping"
    ],
    "USES VARIABLES" : { "commerce_order" : { "label" : "Order", "type" : "commerce_order" } },
    "IF" : [],
    "DO" : [
      { "variable_add" : {
          "USING" : { "type" : "integer", "value" : "0" },
          "PROVIDE" : { "variable_added" : { "contains_dangerous_goods" : "Contains Dangerous Goods" } }
        }
      },
      { "LOOP" : {
          "USING" : { "list" : [ "commerce-order:commerce-line-items" ] },
          "ITEM" : { "list_item" : "Current list item" },
          "DO" : [
            { "component_rules_dangerous_goods" : {
                "USING" : { "line_item" : [ "list-item" ] },
                "PROVIDE" : { "dangerous_good" : { "dangerous_good" : "Dangerous Goods" } }
              }
            },
            { "CONDITIONAL" : [
                {
                  "IF" : { "data_is" : { "data" : [ "dangerous-good" ], "value" : "1" } },
                  "DO" : [
                    { "data_set" : { "data" : [ "contains-dangerous-goods" ], "value" : "1" } }
                  ]
                }
              ]
            }
          ]
        }
      },
      { "CONDITIONAL" : [
          {
            "IF" : { "data_is" : { "data" : [ "contains-dangerous-goods" ], "value" : "0" } },
            "DO" : [
              { "commerce_shipping_service_rate_order" : {
                  "shipping_service_name" : "standard_up_to_0_5kg",
                  "commerce_order" : [ "commerce-order" ]
                }
              }
            ]
          }
        ]
      }
    ]
  }
}
1

I'd recommend a different route for this. Create a view of Commerce Line items with a conditional filter of Commerce Order ID. Add a relationship to the Commerce Product to get access to the field (make it required). Add a "Bulk Operations: Line items" field so that we can access this under conditions within views (this requires you to have Views Bulk Options installed). Add a filter to show products with field_dangerous_goods.

Once you have created the view then add a condition "Check number of results returned by a VBO View". Specify the view you just created and then set the minimum number to evaluate to TRUE as 1.

This is a bit easier and there's only one condition to add to your rule. Sorry if this is a bit late to the party but this seems like it's a bit easier to deal with.

  • I chose this option, which worked for me. One point to note - the view described in this solution won't work for anonymous users unless you disable SQL rewriting for the view. It returns zero results otherwise, regardless of what's in the cart. – millionleaves Nov 19 '15 at 23:45
  • I'm wondering why that worked for me originally. I think my anonymous users have the ability to view products of certain types which is why this works for me. Can you narrow down which permission(s) are granted for authenticated but not anonymous that are leading to this behavior? – dasginganinja Nov 20 '15 at 6:45
  • Disabling SQL rewriting on Views that interact with Commerce entities seems to be the best way to solve all sorts of problems. From what I've read, granting permissions to anonymous users to "View products of any type" is another. The most recent encounter I had with this was sending an order summary to anonymous users on a site where the rule to create new user account on checkout completion had been disabled. So if you've got this working without disabling SQL rewriting, I'd say that's the most likely scenario, although I'd be interested to know why you did that in the first place. – millionleaves Dec 11 '15 at 4:11
  • That site has "View product of any type" granted to the anonymous user. Thanks for the follow-up. – dasginganinja Dec 23 '15 at 15:16
  • Also, you're assuming someone is using Drupal as a front end view. I'm using it purely via services, so need a code-only based approach. – Matt Fletcher Apr 25 '16 at 18:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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