I have a client that needs to be able to set a flat rate based on product type. I saw that rfay set a flat rate based on weight, using a component in a loop. So I figure there should be some method to have the flat rate based on product type be triggered in a similar fashion.

My client only offers one flat rate shipping service for $12.00.

But the client offers two products, tins and bags. If there are bags in the cart, the flat rate rule/component needs to divide the total number of bags by 16, then "return ceil($value)" and multiply that by the shipping rate. IOW, every 16 bags gets charged $12 to ship.

I have created the component and it is firing properly based on Rules evaluation. But when the calculation tries to call on the data from this set it is saying the value is empty. Not sure if this is a rules bug, my error in calling things properly, or just my own ineptitude. Feel free to blame me. :-)

Attached are my rules/component. Thanks for the help!


timodwhit’s picture

Working on this still. I figured out how to get the fields and products to come into scope. Also, was ale to get different types to have different shipping prices, but because my line items have selectable qualities. For example: Small Cheese Popcorn and Small Caramel Popcorn, are the same product and product type but are different line-items.

What happens is that when the loop is applied, each line item is examined instead of products....

Going to keep working I'll post updates and hopefully my rules, components etc.

timodwhit’s picture

Found a nice piece of code posted on the Video's by rfay. It is posted by Will in the comments section.

Issue is now the numbering system: every 16 bags needs to charged a separate $12. If we have 7 bags of type A, 9 bags of type B, and 6 bags of type C. This would come in as 3 different line items, thus when looping over the line-items, it would add the charge of $12 3 times, instead of 2.

Is there is a rules work around for this? Has anyone faced a similar issue?

timodwhit’s picture

Thought: Feel free to let me know if I'm on the right track or head down a dark and ugly path....

When we are evaluating shipping we are given the option [commerce-line-items:line-items:order:commerce:line-item:0:quantity],[...:1:quantity], [...:2:quantity], and so on and so forth. These represent the array of items that in your cart where 0 is the first line, and 2 would be the 3 line item. So would it be possible to examine the list for the count of line-items, then return a range starting with zero, ending with the total count minus 1. Insert each value into a string dynamically, then return the the sum of quantities?

If I'm not making sense, I apologize. Too much time with rules and trying to get this to work. :-)

timodwhit’s picture

Status:Active» Closed (fixed)

Ended up creating a custom action that evaluated based on line_item_type.

Used the code from Ryan:

 function ACTION NAME(){
// Wrap the order object with the Entity Metadata Wrapper.
  $order_wrapper = entity_metadata_wrapper('commerce_order', $pf_list);
// Get the total quantity of its product line items.
  $quantity = commerce_line_items_quantity(
      $order_wrapper->commerce_line_items, $type = array();
return array();
rphillipsfeynman’s picture

new14.94 KB

Finally, after some time working with Drupal, I can make a small contribution. I had the same problem described in this issue. To solve it I edited the file commerce_order.rules.inc, adding a new condition in function commerce_order_rules_condition_info():

  $conditions['commerce_order_contains_product_of_type'] = array(
    'label' => t('Order contains a product of a particular type'),
    'parameter' => array(
      'commerce_order' => array(
        'type' => 'commerce_order',
        'label' => t('Order'),
        'description' => t('The order whose line items should be checked for the specified product type. If the specified order does not exist, the comparison will act as if it is against a quantity of 0.'),
      'product_id' => array(
        'type' => 'text',
        'label' => t('Product type'),
        'description' => t('The type of the product to look for on the order.'),
      'operator' => array(
        'type' => 'text',
        'label' => t('Operator'),
        'description' => t('The operator used with the quantity value below to compare the quantity of the specified product on the order.'),
        'default value' => '>=',
        'options list' => 'commerce_numeric_comparison_operator_options_list',
        'restriction' => 'input',
      'value' => array(
        'type' => 'text',
        'label' => t('Quantity'),
        'default value' => '1',
        'description' => t('The value to compare against the quantity of the specified product type on the order.'),
    'group' => t('Commerce Order'),
    'callbacks' => array(
      'execute' => 'commerce_order_rules_contains_product_of_type',

Also I added the callback function commerce_order_rules_contains_product_of_type($order, $ptype, $operator, $value):

 * Condition callback: checks to see if there are products of a particular type on an order
 * in the specified quantity.
function commerce_order_rules_contains_product_of_type($order, $ptype, $operator, $value) {
  $products = array($ptype => 0);

  // If we actually received a valid order...
  if (!empty($order)) {
    $order_wrapper = entity_metadata_wrapper('commerce_order', $order);

    // Populate the array of the quantities of the products on the order.
    foreach ($order_wrapper->commerce_line_items as $delta => $line_item_wrapper) {
      if (in_array($line_item_wrapper->type->value(), commerce_product_line_item_types())) {
        // Extract a product ID and quantity from the line item.
        $line_item_ptype = $line_item_wrapper->commerce_product->type->value();
        $quantity = $line_item_wrapper->quantity->value();

        // Update the product's quantity value.
        if (empty($products[$line_item_ptype])) {
          $products[$line_item_ptype] = $quantity;
        else {
          $products[$line_item_ptype] += $quantity;

  // Make a quantity comparison based on the operator.
  switch ($operator) {
    case '<':
      return $products[$ptype] < $value;
    case '<=':
      return $products[$ptype] <= $value;
    case '=':
      return $products[$ptype] == $value;
    case '>=':
      return $products[$ptype] >= $value;
    case '>':
      return $products[$ptype] > $value;

  return FALSE;

Everything works when creating a new Flat Rate Service with the condition to meet a specific product type. I hope it helps. Regards,

rphillipsfeynman’s picture

I found there is another module for this as pointed out in http://drupal.org/node/1370770 but I haven't tried it.

lsolesen’s picture

@rphillipsfeynman Sounds like you should take a stab on making a patch either for commerce or for the other contrib module.

rphillipsfeynman’s picture

Perhaps you should do it @lsolesen.