--- 2532672-2.offer_type_fields_update.patch	2015-07-14 14:25:00.000000000 -0400
+++ 2532672-5.offer_type_fields_update_auto_add.patch	2015-07-23 16:16:50.000000000 -0400
@@ -1,5 +1,5 @@
 diff --git a/commerce_discount_extra.install b/commerce_discount_extra.install
-index e60c734..d4410b2 100644
+index e60c734..cb5d407 100644
 --- a/commerce_discount_extra.install
 +++ b/commerce_discount_extra.install
 @@ -6,7 +6,7 @@
@@ -75,7 +75,7 @@ index e60c734..d4410b2 100644
 +        'field_name' => 'commerce_trigger_products',
 +        'entity_type' => 'commerce_discount_offer',
 +        'bundle' => $bundle_name,
-+        'label' => t('Of any of these products'),
++        'label' => t('Of any of these trigger products'),
 +        'description' => t('Leave blank to trigger the discount when <em>any</em> product is purchased.'),
 +        'widget' => array(
 +          'type' => 'commerce_product_reference_autocomplete',
@@ -142,7 +142,7 @@ index e60c734..d4410b2 100644
 +        'field_name' => 'commerce_discount_products',
 +        'entity_type' => 'commerce_discount_offer',
 +        'bundle' => $bundle_name,
-+        'label' => t('Of any of these products'),
++        'label' => t('Of any of these offer products'),
 +        'required' => TRUE,
 +        'widget' => array(
 +          'type' => 'commerce_product_reference_autocomplete',
@@ -222,7 +222,7 @@ index e60c734..d4410b2 100644
      $field = array(
        'entity_types' => array('commerce_discount_offer'),
        'field_name' => 'commerce_offer_limit',
-@@ -148,98 +208,37 @@ function commerce_discount_extra_install_helper() {
+@@ -148,130 +208,115 @@ function commerce_discount_extra_install_helper() {
      );
      field_create_field($field);
    }
@@ -248,37 +248,61 @@ index e60c734..d4410b2 100644
          'widget' => array(
 -          'weight' => 5,
 -        )
--      );
--      field_create_instance($instance);
--    }
--  }
--
++          'weight' => 25,
++        ),
+       );
+       field_create_instance($instance);
+     }
+   }
+ 
 -  /*
 -   * Discount product (the product to discount)
--   */
++  /**
++   * Create the pricing strategy field if it doesn't exist. This instructs the
++   * action how to determine which products to discount first.
+    */
 -
 -  if (empty($fields['commerce_discount_products'])) {
--    $field = array(
++  if (empty($fields['commerce_pricing_strategy'])) {
+     $field = array(
 -      'entity_types' => array('commerce_discount_offer'),
 -      'field_name' => 'commerce_discount_products',
 -      'type' => 'commerce_product_reference',
--      'locked' => TRUE,
++      'settings' => array(
++        'allowed_values' => array(
++          'low_first' => t('Discount the cheapest eligible product(s) first.'),
++          'high_first' => t('Discount the most expensive eligible product(s) first.'),
++        ),
++        'allowed_values_function' => '',
++        'entity_translation_sync' => FALSE,
++      ),
++      'field_name' => 'commerce_pricing_strategy',
++      'type' => 'list_text',
+       'locked' => TRUE,
 -      'cardinality' => FIELD_CARDINALITY_UNLIMITED
--    );
--    field_create_field($field);
--  }
++      'cardinality' => '1',
+     );
+     field_create_field($field);
+   }
 -  foreach (array('per_quantity_fixed', 'per_quantity_percentage') as $type) {
 -    if (empty($instances['commerce_discount_offer'][$type]['commerce_discount_products'])) {
--      $instance = array(
++  foreach ($bundle_names as $bundle_name) {
++    if (empty($instances['commerce_discount_offer'][$bundle_name]['commerce_pricing_strategy'])) {
+       $instance = array(
 -        'field_name' => 'commerce_discount_products',
 -        'entity_type' => 'commerce_discount_offer',
 -        'bundle' => $type,
 -        'label' => t('product(s)'),
 -        'required' => TRUE,
--        'widget' => array(
++        'label' => t('Use the following discounting strategy'),
+         'widget' => array(
 -          'type' => 'commerce_product_reference_autocomplete',
 -          'weight' => 3
--        ),
++          'weight' => 30,
++          'type' => 'options_buttons',
++          'active' => TRUE,
++          'settings' => array(),
+         ),
 -      );
 -      field_create_instance($instance);
 -    }
@@ -304,13 +328,20 @@ index e60c734..d4410b2 100644
 -        'entity_type' => 'commerce_discount_offer',
 -        'bundle' => $type,
 -        'label' => t('product(s)'),
--        'required' => TRUE,
+         'required' => TRUE,
 -        'widget' => array(
 -          'type' => 'commerce_product_reference_autocomplete',
 -          'weight' => 1
-+          'weight' => 25,
++        'default_value' => array(
++          0 => array(
++            'value' => 'low_first',
++          ),
          ),
++        'field_name' => 'commerce_pricing_strategy',
++        'entity_type' => 'commerce_discount_offer',
++        'bundle' => $bundle_name,
        );
++
        field_create_instance($instance);
      }
    }
@@ -318,36 +349,43 @@ index e60c734..d4410b2 100644
 -  /*
 -   * BOGO pricing strategy
 +  /**
-+   * Create the pricing strategy field if it doesn't exist. This instructs the
-+   * action how to determine which products to discount first.
++   * Create the automatic "Add to Cart" behavior field if it doesn't exist. This
++   * field is used to determine when / how offer products should be added to the
++   * cart when triggering conditions are met.
     */
-   if (empty($fields['commerce_pricing_strategy'])) {
+-  if (empty($fields['commerce_pricing_strategy'])) {
++  if (empty($fields['commerce_auto_add_behavior'])) {
      $field = array(
        'settings' => array(
          'allowed_values' => array(
 -          'high_first' => 'Highest first',
 -          'low_first' => 'Lowest first',
-+          'low_first' => t('Discount the cheapest eligible product(s) first.'),
-+          'high_first' => t('Discount the most expensive eligible product(s) first.'),
++          'nothing' => t('Do not automatically add offer products to the cart.'),
++          'add_first_offer_product' => t('Add the first available offer product to the cart.'),
          ),
          'allowed_values_function' => '',
          'entity_translation_sync' => FALSE,
-@@ -251,18 +250,17 @@ function commerce_discount_extra_install_helper() {
+       ),
+-      'field_name' => 'commerce_pricing_strategy',
++      'field_name' => 'commerce_auto_add_behavior',
+       'type' => 'list_text',
+       'locked' => TRUE,
+-      'cardinality' => '1',
      );
      field_create_field($field);
    }
 -  foreach (array('per_quantity_fixed', 'per_quantity_percentage') as $type) {
 -    if (empty($instances['commerce_discount_offer'][$type]['commerce_pricing_strategy'])) {
 +  foreach ($bundle_names as $bundle_name) {
-+    if (empty($instances['commerce_discount_offer'][$bundle_name]['commerce_pricing_strategy'])) {
++    if (empty($instances['commerce_discount_offer'][$bundle_name]['commerce_auto_add_behavior'])) {
        $instance = array(
 -        'label' => 'Pricing strategy',
-+        'label' => t('Use the following discounting strategy'),
++        'label' => t('When enough trigger products are in the cart but no offer products have been added'),
          'widget' => array(
 -          'weight' => 0,
 -          'type' => 'options_select',
 -          'active' => 1,
-+          'weight' => 30,
++          'weight' => 35,
 +          'type' => 'options_buttons',
 +          'active' => TRUE,
            'settings' => array(),
@@ -357,10 +395,12 @@ index e60c734..d4410b2 100644
 +        'required' => TRUE,
          'default_value' => array(
            0 => array(
-             'value' => 'low_first',
-@@ -270,8 +268,9 @@ function commerce_discount_extra_install_helper() {
+-            'value' => 'low_first',
++            'value' => 'nothing',
+           ),
          ),
-         'field_name' => 'commerce_pricing_strategy',
+-        'field_name' => 'commerce_pricing_strategy',
++        'field_name' => 'commerce_auto_add_behavior',
          'entity_type' => 'commerce_discount_offer',
 -        'bundle' => $type,
 +        'bundle' => $bundle_name,
@@ -369,7 +409,7 @@ index e60c734..d4410b2 100644
        field_create_instance($instance);
      }
    }
-@@ -407,3 +406,85 @@ function commerce_discount_extra_update_7002() {
+@@ -407,3 +452,138 @@ function commerce_discount_extra_update_7002() {
      }
    }
  }
@@ -391,7 +431,7 @@ index e60c734..d4410b2 100644
 +
 +    // Update the trigger product instances.
 +    $instance = field_info_instance('commerce_discount_offer', 'commerce_trigger_products', $bundle_name);
-+    $instance['label'] = t('Of any of these products');
++    $instance['label'] = t('Of any of these trigger products');
 +    $instance['description'] = t('Leave blank to trigger the discount when <em>any</em> product is purchased.');
 +    $instance['required'] = FALSE;
 +    $instance['widget']['weight'] = 5;
@@ -406,7 +446,7 @@ index e60c734..d4410b2 100644
 +
 +    // Update the offer product instances.
 +    $instance = field_info_instance('commerce_discount_offer', 'commerce_discount_products', $bundle_name);
-+    $instance['label'] = t('Of any of these products');
++    $instance['label'] = t('Of any of these offer products');
 +    $instance['description'] = '';
 +    $instance['widget']['weight'] = 15;
 +    field_update_instance($instance);
@@ -455,7 +495,235 @@ index e60c734..d4410b2 100644
 +
 +  return st('Per-quantity discount field instances updated.');
 +}
++
++/**
++ * Add a field to determine automatic "Add to Cart" behavior for offer products.
++ */
++function commerce_discount_extra_update_7004() {
++  /**
++   * Create the automatic "Add to Cart" behavior field if it doesn't exist. This
++   * field is used to determine when / how offer products should be added to the
++   * cart when triggering conditions are met.
++   */
++  if (empty($fields['commerce_auto_add_behavior'])) {
++    $field = array(
++      'settings' => array(
++        'allowed_values' => array(
++          'nothing' => t('Do not automatically add offer products to the cart.'),
++          'add_first_offer_product' => t('Add the first available offer product to the cart.'),
++        ),
++        'allowed_values_function' => '',
++        'entity_translation_sync' => FALSE,
++      ),
++      'field_name' => 'commerce_auto_add_behavior',
++      'type' => 'list_text',
++      'locked' => TRUE,
++    );
++    field_create_field($field);
++  }
++  foreach (array('per_quantity_fixed', 'per_quantity_percentage') as $bundle_name) {
++    if (empty($instances['commerce_discount_offer'][$bundle_name]['commerce_auto_add_behavior'])) {
++      $instance = array(
++        'label' => t('When enough trigger products are in the cart but no offer products have been added'),
++        'widget' => array(
++          'weight' => 35,
++          'type' => 'options_buttons',
++          'active' => TRUE,
++          'settings' => array(),
++        ),
++        'required' => TRUE,
++        'default_value' => array(
++          0 => array(
++            'value' => 'nothing',
++          ),
++        ),
++        'field_name' => 'commerce_auto_add_behavior',
++        'entity_type' => 'commerce_discount_offer',
++        'bundle' => $bundle_name,
++      );
++
++      field_create_instance($instance);
++    }
++  }
++
++  return st('New field added to offer types to automatically add offer products to the cart.');
++}
 \ No newline at end of file
+diff --git a/commerce_discount_extra.module b/commerce_discount_extra.module
+index a1c6dfb..4484de4 100644
+--- a/commerce_discount_extra.module
++++ b/commerce_discount_extra.module
+@@ -553,3 +553,170 @@ function commerce_discount_extra_fix_limit_data(&$form, &$form_state) {
+     $limit = (int)'';
+   }
+ }
++
++/**
++ * Implements hook_commerce_cart_product_add().
++ *
++ * When a product is added to the cart, this function looks for any discounts
++ * that use per-quantity offers configured to automatically add their offer
++ * products to the cart when enough triggering products have been added.
++ */
++function commerce_discount_extra_commerce_cart_product_add($order, $product, $quantity, $line_item) {
++  // See if a product has already been added through this function. If so, bail
++  // as we only want to add the first available product to the cart.
++  $product_added = &drupal_static(__FUNCTION__);
++
++  if (isset($product_added)) {
++    return;
++  }
++  else {
++    $product_added = NULL;
++  }
++
++  // Build the base select query to find all per-quantity offer discounts.
++  $query = db_select('commerce_discount', 'cd', array('fetch' => PDO::FETCH_ASSOC))
++    ->fields('cd', array('discount_id', 'name'))
++    ->orderBy('cd.discount_id')
++    ->groupBy('cd.discount_id');
++
++  // Join to the relevant tables and retain their aliases.
++  $fcdo_alias = $query->leftJoin('field_data_commerce_discount_offer', 'fcdo', 'cd.discount_id = %alias.entity_id');
++  $cdo_alias = $query->leftJoin('commerce_discount_offer', 'cdo', $fcdo_alias . '.commerce_discount_offer_target_id = %alias.discount_offer_id');
++  $fcaab_alias = $query->leftJoin('field_data_commerce_auto_add_behavior', 'fcaab', $cdo_alias . '.discount_offer_id = %alias.entity_id');
++
++  // Add fields from the joined tables.
++  $query
++    ->fields($cdo_alias, array('type'))
++    ->fields($fcaab_alias, array('commerce_auto_add_behavior_value'));
++
++  // Add the conditions for the various tables.
++  $query
++    ->condition('cd.status', 1)
++    ->condition($fcdo_alias . '.entity_type', 'commerce_discount')
++    ->condition($fcaab_alias . '.entity_type', 'commerce_discount_offer')
++    ->where($fcaab_alias . ".commerce_auto_add_behavior_value IN ('add_first_offer_product')")
++    ->where($cdo_alias . ".type IN ('per_quantity_discount', 'per_quantity_percentage')");
++
++  // Fetch and loop over the results to find discount rules to evaluate.
++  $discount_rules = array();
++
++  foreach ($query->execute() as $result) {
++    $discount_rules[$result['discount_id']] = 'commerce_discount_rule_' . $result['name'];
++  }
++
++  // Load all of the relevant discount rules.
++  $rules = rules_config_load_multiple($discount_rules);
++
++  $order_wrapper = entity_metadata_wrapper('commerce_order', $order);
++
++  foreach ($discount_rules as $discount_id => $rule_name) {
++    // Evaluate each rule's conditions to see if the discount would apply to the
++    // current shopping cart.
++    $state = new RulesState();
++
++    if ($rules[$rule_name]->conditionContainer()->evaluate($state)) {
++      // Load the discount related to the Rule.
++      $discount = entity_load('commerce_discount', array($discount_id));
++      $discount = reset($discount);
++      $discount_wrapper = entity_metadata_wrapper('commerce_discount', $discount);
++
++      // Check to ensure the product that was added to the cart qualifies as a
++      // trigger product for the discount's offer.
++      if (!empty($discount_wrapper->commerce_discount_offer->commerce_trigger_products->raw()) &&
++        !in_array($product->product_id, $discount_wrapper->commerce_discount_offer->commerce_trigger_products->raw())) {
++        continue;
++      }
++
++      // If any of the current discount's offer products have been added to the
++      // cart, continue to the next discount because we don't know for certain
++      // one of the products wasn't already added as a result of this discount.
++      foreach ($order_wrapper->commerce_line_items as $delta => $line_item_wrapper) {
++        $line_item_product_id = $line_item_wrapper->commerce_product->raw();
++
++        if (in_array($line_item_product_id, $discount_wrapper->commerce_discount_offer->commerce_discount_products->raw())) {
++          continue 2;
++        }
++      }
++
++      // If so, see if sufficient trigger products exist in the cart to activate
++      // the discount. We do this by maintaining a simplified reversal of the
++      // manifest in the discount action's callback: instead of removing items
++      // from an array, we add to it as the quantity of various line items on
++      // the current order are identified as trigger products and prevent this
++      // function from recognizing the same trigger products on multiple offers.
++      // Additionally, since we can't be assured the relevant offer product
++      // hasn't already been added to the cart, do not include any offer
++      // products in the trigger product count.
++      $trigger_quantity = $discount_wrapper->commerce_discount_offer->commerce_trigger_qty->raw();
++      $offer_quantity = $discount_wrapper->commerce_discount_offer->commerce_offer_qty->raw();
++      static $triggering_line_items = array();
++      $trigger_count = 0;
++
++      foreach ($order_wrapper->commerce_line_items as $delta => $line_item_wrapper) {
++        $line_item_id = $line_item_wrapper->raw();
++        $line_item_quantity = $line_item_wrapper->quantity->raw();
++        $line_item_product_id = $line_item_wrapper->commerce_product->raw();
++
++        // If the line item has already been fully counted, skip it.
++        if (!empty($triggering_line_items[$line_item_id]) && $triggering_line_items[$line_item_id] == $line_item_quantity) {
++          continue;
++        }
++
++        // If any product may trigger the discount or if the current product is
++        // one of the specified trigger products, increment the count as much as
++        // needed and log that amount in the $triggering_line_items array.
++        if (empty($discount_wrapper->commerce_discount_offer->commerce_trigger_products->raw()) ||
++          in_array($line_item_product_id, $discount_wrapper->commerce_discount_offer->commerce_trigger_products->raw())) {
++          if (empty($trigger_line_items[$line_item_id])) {
++            $triggering_line_items[$line_item_id] = 0;
++          }
++
++          $increment = min($line_item_quantity - $triggering_line_items[$line_item_id], $trigger_quantity - $trigger_count);
++
++          $triggering_line_items[$line_item_id] += $increment;
++          $trigger_count += $increment;
++
++          // If we've reached the trigger quantity, exit the loop.
++          if ($trigger_count == $trigger_quantity) {
++            break;
++          }
++        }
++      }
++
++      // If by this time, the trigger quantity hasn't been met, do not add any
++      // offer product to the cart.
++      if ($trigger_count < $trigger_quantity) {
++        break;
++      }
++
++      // Loop over the discount products looking for one to add.
++      foreach ($discount_wrapper->commerce_discount_offer->commerce_discount_products as $delta => $product_wrapper) {
++        // Check stock for the current product to see if the full offer amount
++        // is available for purchase.
++        if (module_exists('commerce_stock')) {
++          // Prepare the variables needed for a stock check.
++          $quantity_ordered = commerce_stock_check_cart_product_level($product_wrapper->raw());
++          $stock_state = '';
++          $message = '';
++
++          // Fetch the stock check messages to see if it failed.
++          commerce_stock_check_product_rule($product_wrapper->value(), $offer_quantity, $quantity_ordered, $stock_state, $message);
++
++          // If the $stock_state is non-empty, the product is not in stock, so
++          // we continue to pass on to the next discounted product.
++          if (!empty($stock_state)) {
++            continue;
++          }
++        }
++
++        // Add the current dicount product to the cart.
++        $product_added = $product_wrapper->raw();
++        commerce_cart_product_add_by_id($product_wrapper->raw(), $offer_quantity);
++
++        // Reset $product_added so additional discount rules can activate an offer.
++        $product_added = NULL;
++        break;
++      }
++    }
++  }
++}
 diff --git a/commerce_discount_extra.rules.inc b/commerce_discount_extra.rules.inc
 index a24ea96..cd3360b 100644
 --- a/commerce_discount_extra.rules.inc
