You are here

commerce_discount_extra.rules.inc in Commerce Discount Extra 7

Provides Rules module support for extra discounts.

File

commerce_discount_extra.rules.inc
View source
<?php

/**
 * @file
 * Provides Rules module support for extra discounts.
 */

/**
 * Implements hook_rules_condition_info_alter().
 */
function commerce_discount_extra_rules_condition_info() {
  $inline_conditions = inline_conditions_get_info();

  // Line item: user has role.
  $conditions['commerce_discount_extra_line_item_user_has_role'] = array(
    'label' => t('User has role'),
    'parameter' => array(
      'entity' => array(
        'type' => 'entity',
        'label' => t('Entity'),
        'wrapped' => TRUE,
      ),
      'roles' => array(
        'type' => 'list<text>',
        'label' => t('Roles'),
      ),
    ),
    'group' => t('Commerce discounts extra'),
  );

  // Order: user has role.
  $conditions['commerce_discount_extra_order_user_has_role'] = array(
    'label' => t('User has role'),
    'parameter' => array(
      'entity' => array(
        'type' => 'entity',
        'label' => t('Entity'),
        'wrapped' => TRUE,
      ),
      'roles' => array(
        'type' => 'list<text>',
        'label' => t('Roles'),
      ),
    ),
    'group' => t('Commerce discounts extra'),
  );

  // Line item: line item price comparison.
  $conditions['commerce_discount_extra_line_item_price_comp'] = array(
    'label' => t('Line item price comparison'),
    'parameter' => array(
      'commerce_line_item' => array(
        'type' => 'commerce_line_item',
        'label' => t('Line item'),
        'wrapped' => TRUE,
      ),
      'price' => array(
        'type' => 'commerce_price',
        'label' => t('Price'),
        'description' => t('User-defined price for comparison against product'),
      ),
      'operator' => array(
        'type' => 'text',
        'label' => t('Operator'),
        'default value' => '>=',
        'options list' => '_commerce_discount_operator_options',
      ),
      'method' => array(
        'type' => 'text',
        'label' => t('Method'),
        'default value' => 'base',
        'options list' => '_commerce_discount_extra_comp_method_options',
      ),
    ),
    'group' => t('Commerce discounts extra'),
  );

  // Line item: total items in order.
  $conditions['commerce_discount_extra_line_item_items_in_order'] = array(
    'label' => t('Total items in order'),
    'parameter' => array(
      'entity' => array(
        'type' => 'entity',
        'label' => t('Entity'),
        'wrapped' => TRUE,
      ),
      'number' => array(
        'type' => 'integer',
        'label' => t('Number of items in order'),
      ),
      'operator' => array(
        'type' => 'text',
        'label' => t('Operator'),
        'default value' => '>=',
        'options list' => '_commerce_discount_operator_options',
      ),
    ),
    'group' => t('Commerce discounts extra'),
  );

  // Line item: order has quantity product.
  $conditions['commerce_discount_extra_line_item_has_specific_quantity_products'] = array(
    'label' => t('Order has specific products'),
    'parameter' => array(
      'commerce_line_item' => array(
        'type' => 'commerce_line_item',
        'label' => t('Line item'),
        'wrapped' => TRUE,
      ),
      'products' => array(
        'type' => 'text',
        'label' => t('Product SKU(s)'),
        'description' => t('Products SKU to look for on the order. Enter a comma-separated list of product SKU(s).'),
      ),
      'operator' => array(
        'type' => 'text',
        'label' => t('Operator'),
        'description' => t('The operator used with the product quantity value below.'),
        'default value' => '>=',
        'options list' => '_commerce_discount_operator_options',
      ),
      'quantity' => array(
        'type' => 'integer',
        'label' => t('Quantity'),
        'description' => t('Quantity value to be compared against each selected product(s).'),
      ),
    ),
  );

  // Order: total items in order.
  $conditions['commerce_discount_extra_order_items_in_order'] = array(
    'label' => t('Total items in order'),
    'parameter' => array(
      'entity' => array(
        'type' => 'entity',
        'label' => t('Entity'),
        'wrapped' => TRUE,
      ),
      'number' => array(
        'type' => 'integer',
        'label' => t('Number of items in order'),
      ),
      'operator' => array(
        'type' => 'text',
        'label' => t('Operator'),
        'default value' => '>=',
        'options list' => '_commerce_discount_operator_options',
      ),
    ),
    'group' => t('Commerce discounts extra'),
  );
  $conditions['commerce_discount_extra_line_item_compare_order_amount'] = array(
    'label' => t('Order amount comparison'),
    'parameter' => array(
      'commerce_line_item' => array(
        'type' => 'commerce_line_item',
        'label' => t('Line item'),
        'description' => t('The line item.'),
        'wrapped' => TRUE,
      ),
      'operator' => array(
        'type' => 'text',
        'label' => t('Operator'),
        'description' => t('The operator used with the order amount value below.'),
        'default value' => '>=',
        'options list' => '_commerce_discount_operator_options',
      ),
      'total' => array(
        'type' => 'commerce_price',
        'label' => t('Order amount'),
        'default value' => '',
        'description' => t("The value to compare against the passed line item's order amount."),
      ),
      'line_item_types' => array(
        'type' => 'list<text>',
        'label' => t('Line item types'),
        'description' => t('Select the line item types that should be included in the order total amount. Discount created line items are always excluded.'),
        'default value' => commerce_order_compare_order_amount_options_default(),
        'options list' => 'commerce_order_compare_order_amount_options_list',
        'optional' => TRUE,
      ),
    ),
    'group' => t('Commerce discounts extra'),
  );
  $conditions['commerce_discount_extra_line_item_has_product_type'] = array(
    'label' => t('Product types'),
    'parameter' => array(
      'commerce_line_item' => array(
        'type' => 'commerce_line_item',
        'label' => t('Line item'),
        'wrapped' => TRUE,
      ),
      'product_types' => array(
        'type' => 'list<text>',
        'label' => t('Product types'),
        'options list' => 'commerce_product_type_options_list',
        'multiple' => TRUE,
      ),
    ),
  );
  foreach ($conditions as $name => $condition) {
    $conditions[$name]['callbacks'] = array(
      'execute' => $inline_conditions[$name]['callbacks']['build'],
    );
  }
  return $conditions;
}

/**
 * Implements hook_rules_action_info().
 */
function commerce_discount_extra_rules_action_info() {
  $actions['commerce_discount_extra_per_quantity_discount'] = array(
    'label' => t('Discount a certain number of products in an order'),
    'group' => t('Commerce discounts extra'),
    'parameter' => array(
      'commerce_order' => array(
        'label' => t('Order'),
        'type' => 'commerce_order',
        'wrapped' => TRUE,
      ),
      'commerce_discount' => array(
        'label' => t('Commerce Discount'),
        'type' => 'token',
        'options list' => 'commerce_discount_entity_list',
      ),
    ),
  );
  $actions['commerce_discount_extra_fixed_product_price'] = array(
    'label' => t('Discount a product to a specified price'),
    'group' => t('Commerce discounts extra'),
    'parameter' => array(
      'entity' => array(
        'label' => t('Entity'),
        'type' => 'entity',
        'wrapped' => TRUE,
      ),
      'commerce_discount' => array(
        'label' => t('Commerce Discount'),
        'type' => 'token',
        'options list' => 'commerce_discount_entity_list',
      ),
    ),
  );
  $actions['commerce_discount_extra_per_quantity_category_discount'] = array(
    'label' => t('Discount a certain number of products based on their catalog categories'),
    'group' => t('Commerce discounts extra'),
    'parameter' => array(
      'commerce_order' => array(
        'label' => t('Order'),
        'type' => 'commerce_order',
        'wrapped' => TRUE,
      ),
      'commerce_discount' => array(
        'label' => t('Commerce Discount'),
        'type' => 'token',
        'options list' => 'commerce_discount_entity_list',
      ),
    ),
  );
  return $actions;
}

/**
 * Rules action callback: apply a per quantity discount to an order.
 */
function commerce_discount_extra_per_quantity_discount(EntityDrupalWrapper $order_wrapper, $discount_name) {
  $discount_wrapper = entity_metadata_wrapper('commerce_discount', $discount_name);
  $order = $order_wrapper
    ->value();

  // Exit now if the discount was not configured to apply to any products.
  if (!$discount_wrapper->commerce_discount_offer->commerce_discount_products
    ->value()) {
    return;
  }
  $offer_wrapper = $discount_wrapper->commerce_discount_offer;
  $offer_type = $offer_wrapper->type
    ->value();

  // Trigger product variables.
  $trigger_qty = $offer_wrapper->commerce_trigger_qty
    ->value();
  $trigger_product_ids = $offer_wrapper->commerce_trigger_products
    ->raw();

  // Discount product variables.
  $offer_product_ids = $offer_wrapper->commerce_discount_products
    ->raw();
  $offer_qty_per_trigger = $offer_wrapper->commerce_offer_qty
    ->value();

  // Determine the proper direction to use for sorting items. Offer direction
  // means the order in which eligible offer items are picked for discount.
  // Trigger direction means the order in which eligible offer-trigger items
  // ("x" in Buy x get y) are processed and thus marked as ineligible for
  // offers.
  switch ($offer_wrapper->commerce_pricing_strategy
    ->value()) {
    case NULL:
    case 'high_first':
      $offer_direction = 'descending';
      $trigger_direction = 'ascending';
      break;
    case 'low_first':
      $offer_direction = 'ascending';
      $trigger_direction = 'descending';
  }

  // Find the maximum number of times a discount can fire based on the
  // user-defined offer limit settings.
  $limit = $offer_wrapper->commerce_offer_limit
    ->value() * $offer_qty_per_trigger;

  // Create a manifest of products at each quantity position.
  foreach ($order_wrapper->commerce_line_items as $line_item_wrapper) {
    $type = $line_item_wrapper->type
      ->value();

    // Do not count free product line items.
    if ($line_item_wrapper
      ->value() && in_array($type, commerce_product_line_item_types()) && $type != 'product_discount') {
      $product_id = $line_item_wrapper->commerce_product->product_id
        ->value();
      if (empty($trigger_product_ids) || in_array($product_id, $trigger_product_ids) || in_array($product_id, $offer_product_ids)) {
        $pos = 0;
        while ($pos < $line_item_wrapper->quantity
          ->value()) {

          // Only count trigger and offer products. Some will happen to be both.
          // If the trigger product IDs array is empty, then the discount should
          // apply to any quantity of purchased products.
          if (empty($trigger_product_ids) || in_array($product_id, $trigger_product_ids) || in_array($product_id, $offer_product_ids)) {
            $manifest[] = $line_item_wrapper;
            $pos++;
          }
        }
      }
    }
  }

  // Allow other modules to alter the manifest prior to processing.
  drupal_alter('commerce_discount_per_quantity_manifest', $manifest);

  // Counter for discount amount.
  $amount = 0;

  // Track which line items play into the discount amount.
  $discounted_line_item_ids = array();

  // Track the original amounts of discounted line items.
  $discounted_line_item_amounts = array();

  // Counter for number of times the discount has fired.
  $n = 0;

  // Each loop takes care of one phase of "buy x get y". The loop repeats
  // until all of the offer/trigger products have been counted.
  while (!empty($manifest)) {

    // Counter for trigger products per phase.
    $t = 0;

    // Counter for offer products per phase.
    $o = 0;

    // First try to find a trigger product that is not an offer product, either
    // from the array of trigger product IDs or, if that is empty, any non-
    // offer product.
    foreach ($manifest as $i => $line_item_wrapper) {
      $product_id = $line_item_wrapper->commerce_product->product_id
        ->value();
      if ($t < $trigger_qty && (empty($trigger_product_ids) || in_array($product_id, $trigger_product_ids)) && !in_array($product_id, $offer_product_ids)) {
        $t++;
        unset($manifest[$i]);
      }
    }

    // If there are still not enough trigger products, look for any trigger
    // product, going in the order specified by the offer config.
    usort($manifest, 'commerce_discount_extra_sort_by_amount_' . $trigger_direction);
    foreach ($manifest as $i => $line_item_wrapper) {
      $product_id = $line_item_wrapper->commerce_product->product_id
        ->value();
      if ($t < $trigger_qty && (empty($trigger_product_ids) || in_array($product_id, $trigger_product_ids))) {
        $t++;
        unset($manifest[$i]);
      }
    }

    // If there are enough trigger products, look for some products to which we
    // can apply the offer.
    if ($t == $trigger_qty) {

      // Sort eligible offer products so that discounts are applied in the
      // correct order.
      usort($manifest, 'commerce_discount_extra_sort_by_amount_' . $offer_direction);
      foreach ($manifest as $i => $line_item_wrapper) {
        $product_id = $line_item_wrapper->commerce_product->product_id
          ->value();
        if ($o < $offer_qty_per_trigger && in_array($product_id, $offer_product_ids)) {
          $o++;
          unset($manifest[$i]);

          // Apply offer.
          if ($n < $limit || !$limit) {

            // Include the current line item ID in the list of discounted line
            // item IDs.
            $line_item_id = $line_item_wrapper->line_item_id
              ->value();
            if (!in_array($line_item_id, $discounted_line_item_ids)) {
              $discounted_line_item_ids[] = $line_item_id;
            }

            // Seed an initial amount so we can track offers that might be
            // applied to same line item multiple times.
            if (!isset($discounted_line_item_amounts[$line_item_id])) {
              $discounted_line_item_amounts[$line_item_id] = 0;
            }
            switch ($offer_type) {
              case 'per_quantity_fixed':
                $offer_amount = $offer_wrapper->commerce_fixed_amount->amount
                  ->value();
                $discounted_line_item_amounts[$line_item_id] += $offer_amount;
                $amount += $offer_amount;
                break;
              case 'per_quantity_percentage':
                $raw_price = $line_item_wrapper->commerce_unit_price->amount
                  ->value();
                $pct = $offer_wrapper->commerce_percentage
                  ->value();
                $offer_amount = $raw_price * $pct;
                $discounted_line_item_amounts[$line_item_id] += $offer_amount;
                $amount += $offer_amount;
                break;
            }
            $n++;
          }
        }
      }
    }
    else {
      break;
    }
  }
  if ($amount > 0) {
    $delta = $order_wrapper->commerce_discounts
      ->count();
    $order->commerce_discounts[LANGUAGE_NONE][$delta]['target_id'] = $discount_wrapper->discount_id
      ->value();

    // Add a discount line item.
    $discount_price = array(
      'amount' => -$amount,
      'currency_code' => $order_wrapper->commerce_order_total->currency_code
        ->value(),
    );
    module_load_include('inc', 'commerce_discount', 'commerce_discount.rules');

    // Attempt to update the existing discount line item with the new price.
    $discount_line_item = commerce_discount_set_existing_line_item_price($order_wrapper, $discount_name, $discount_price);

    // If it didn't already exist on the order, add it now.
    if (empty($discount_line_item)) {
      $discount_line_item = commerce_discount_add_line_item($order_wrapper, $discount_name, $discount_price, array(
        'discounted_line_item_ids' => $discounted_line_item_ids,
        'discounted_line_item_amounts' => $discounted_line_item_amounts,
      ));
    }
    else {

      // Otherwise, if it did exist, update the line item IDs and amounts arrays
      // in its data array to the correct values.
      $discount_line_item->data['discounted_line_item_ids'] = $discounted_line_item_ids;
      $discount_line_item->data['discounted_line_item_amounts'] = $discounted_line_item_amounts;
      commerce_line_item_save($discount_line_item);
    }

    // Allow other modules to react to the save of the discount line item.
    $unmodified_line_item = serialize($discount_line_item);
    module_invoke_all('commerce_discount_per_quantity_discount_line_item_save', $discount_line_item, $discount_name, $offer_type);

    // Save the discount line item if it changed.
    if (serialize($discount_line_item) !== $unmodified_line_item) {
      commerce_line_item_save($discount_line_item);
    }
  }
  else {
    commerce_discount_delete_discount_line_item_by_name($order_wrapper, $discount_name);
  }

  // Update the total order price, for the next rules condition (if any).
  commerce_order_calculate_total($order);
}

/**
 * Rules action callback: apply a per quantity discount based on product
 * categories to an order.
 */
function commerce_discount_extra_per_quantity_category_discount(EntityDrupalWrapper $order_wrapper, $discount_name) {
  $discount_wrapper = entity_metadata_wrapper('commerce_discount', $discount_name);
  $order = $order_wrapper
    ->value();

  // Exit now if the discount was not configured to apply to any categories.
  if (!$discount_wrapper->commerce_discount_offer->commerce_offer_categories
    ->value()) {
    return;
  }
  $offer_wrapper = $discount_wrapper->commerce_discount_offer;
  $offer_type = $offer_wrapper->type
    ->value();

  // Trigger product / category variables.
  $trigger_qty = $offer_wrapper->commerce_trigger_qty
    ->value();
  $trigger_categories = explode(', ', $offer_wrapper->commerce_trigger_categories
    ->raw());

  // Discount product / category variables.
  $offer_categories = explode(', ', $offer_wrapper->commerce_offer_categories
    ->raw());
  $offer_qty_per_trigger = $offer_wrapper->commerce_offer_qty
    ->value();

  // Determine the proper direction to use for sorting items. Offer direction
  // means the order in which eligible offer items are picked for discount.
  // Trigger direction means the order in which eligible offer-trigger items
  // ("x" in Buy x get y) are processed and thus marked as ineligible for
  // offers.
  switch ($offer_wrapper->commerce_pricing_strategy
    ->value()) {
    case NULL:
    case 'high_first':
      $offer_direction = 'descending';
      $trigger_direction = 'ascending';
      break;
    case 'low_first':
      $offer_direction = 'ascending';
      $trigger_direction = 'descending';
  }

  // Find the maximum number of times a discount can fire based on the
  // user-defined offer limit settings.
  $limit = $offer_wrapper->commerce_offer_limit
    ->value() * $offer_qty_per_trigger;

  // Create a manifest of products in relevant categories at each quantity position.
  foreach ($order_wrapper->commerce_line_items as $line_item_wrapper) {
    $type = $line_item_wrapper->type
      ->value();

    // Do not count free product line items.
    if ($line_item_wrapper
      ->value() && in_array($type, commerce_product_line_item_types()) && $type != 'product_discount') {
      $product_id = $line_item_wrapper->commerce_product->product_id
        ->value();
      $product_categories = commerce_discount_extra_product_terms($product_id);
      if (empty($trigger_categories) || array_intersect($trigger_categories, $product_categories) != array() || array_intersect($offer_categories, $product_categories) != array()) {
        $pos = 0;
        while ($pos < $line_item_wrapper->quantity
          ->value()) {

          // Only count products in the trigger and offer product categories.
          // Some will happen to be both. If the trigger category is empty, then
          // the discount should apply to any quantity of purchased products in
          // any category.
          if (empty($trigger_categories) || array_intersect($trigger_categories, $product_categories) != array() || array_intersect($offer_categories, $product_categories) != array()) {
            $manifest[] = $line_item_wrapper;
            $pos++;
          }
        }
      }
    }
  }

  // Allow other modules to alter the manifest prior to processing.
  drupal_alter('commerce_discount_per_quantity_category_manifest', $manifest);

  // Counter for discount amount.
  $amount = 0;

  // Track which line items play into the discount amount.
  $discounted_line_item_ids = array();

  // Track the original amounts of discounted line items.
  $discounted_line_item_amounts = array();

  // Counter for number of times the discount has fired.
  $n = 0;

  // Each loop takes care of one phase of "buy x get y". The loop repeats
  // until all of the offer/trigger products have been counted.
  while (!empty($manifest)) {

    // Counter for trigger products per phase.
    $t = 0;

    // Counter for offer products per phase.
    $o = 0;

    // First try to find a trigger product that is not an offer product, either
    // from the the trigger product category or, if that is empty, any non-
    // offer product.
    foreach ($manifest as $i => $line_item_wrapper) {
      $product_id = $line_item_wrapper->commerce_product->product_id
        ->value();
      $product_categories = commerce_discount_extra_product_terms($product_id);
      if ($t < $trigger_qty && (empty($trigger_categories) || array_intersect($trigger_categories, $product_categories) != array()) && array_intersect($offer_categories, $product_categories) == array()) {
        $t++;
        unset($manifest[$i]);
      }
    }

    // If there are still not enough trigger products, look for any trigger
    // product, going in the order specified by the offer config.
    usort($manifest, 'commerce_discount_extra_sort_by_amount_' . $trigger_direction);
    foreach ($manifest as $i => $line_item_wrapper) {
      $product_id = $line_item_wrapper->commerce_product->product_id
        ->value();
      $product_categories = commerce_discount_extra_product_terms($product_id);
      if ($t < $trigger_qty && (empty($trigger_categories) || array_intersect($trigger_categories, $product_categories) != array())) {
        $t++;
        unset($manifest[$i]);
      }
    }

    // If there are enough trigger products, look for some products to which we
    // can apply the offer.
    if ($t == $trigger_qty) {

      // Sort eligible offer products so that discounts are applied in the
      // correct order.
      usort($manifest, 'commerce_discount_extra_sort_by_amount_' . $offer_direction);
      foreach ($manifest as $i => $line_item_wrapper) {
        $product_id = $line_item_wrapper->commerce_product->product_id
          ->value();
        $product_categories = commerce_discount_extra_product_terms($product_id);
        if ($o < $offer_qty_per_trigger && array_intersect($offer_categories, $product_categories) != array()) {
          $o++;
          unset($manifest[$i]);

          // Apply offer.
          if ($n < $limit || !$limit) {

            // Include the current line item ID in the list of discounted line
            // item IDs.
            $line_item_id = $line_item_wrapper->line_item_id
              ->value();
            if (!in_array($line_item_id, $discounted_line_item_ids)) {
              $discounted_line_item_ids[] = $line_item_id;
            }

            // Seed an initial amount so we can track offers that might be
            // applied to same line item multiple times.
            if (!isset($discounted_line_item_amounts[$line_item_id])) {
              $discounted_line_item_amounts[$line_item_id] = 0;
            }
            switch ($offer_type) {
              case 'per_quantity_category_fixed':
                $offer_amount = $offer_wrapper->commerce_fixed_amount->amount
                  ->value();
                $discounted_line_item_amounts[$line_item_id] += $offer_amount;
                $amount += $offer_amount;
                break;
              case 'per_quantity_category_percentage':
                $raw_price = $line_item_wrapper->commerce_unit_price->amount
                  ->value();
                $pct = $offer_wrapper->commerce_percentage
                  ->value();
                $offer_amount = $raw_price * $pct;
                $discounted_line_item_amounts[$line_item_id] += $offer_amount;
                $amount += $offer_amount;
                break;
            }
            $n++;
          }
        }
      }
    }
    else {
      break;
    }
  }
  if ($amount > 0) {
    $delta = $order_wrapper->commerce_discounts
      ->count();
    $order->commerce_discounts[LANGUAGE_NONE][$delta]['target_id'] = $discount_wrapper->discount_id
      ->value();

    // Add a discount line item.
    $discount_price = array(
      'amount' => -$amount,
      'currency_code' => $order_wrapper->commerce_order_total->currency_code
        ->value(),
    );
    module_load_include('inc', 'commerce_discount', 'commerce_discount.rules');

    // Attempt to update the existing discount line item with the new price.
    $discount_line_item = commerce_discount_set_existing_line_item_price($order_wrapper, $discount_name, $discount_price);

    // If it didn't already exist on the order, add it now.
    if (empty($discount_line_item)) {
      $discount_line_item = commerce_discount_add_line_item($order_wrapper, $discount_name, $discount_price, array(
        'discounted_line_item_ids' => $discounted_line_item_ids,
        'discounted_line_item_amounts' => $discounted_line_item_amounts,
      ));
    }
    else {

      // Otherwise, if it did exist, update the line item IDs and amounts arrays
      // in its data array to the correct values.
      $discount_line_item->data['discounted_line_item_ids'] = $discounted_line_item_ids;
      $discount_line_item->data['discounted_line_item_amounts'] = $discounted_line_item_amounts;
      commerce_line_item_save($discount_line_item);
    }

    // Allow other modules to react to the save of the discount line item.
    $unmodified_line_item = serialize($discount_line_item);
    module_invoke_all('commerce_discount_per_quantity_discount_line_item_save', $discount_line_item, $discount_name, $offer_type);

    // Save the discount line item if it changed.
    if (serialize($discount_line_item) !== $unmodified_line_item) {
      commerce_line_item_save($discount_line_item);
    }
  }
  else {
    commerce_discount_delete_discount_line_item_by_name($order_wrapper, $discount_name);
  }
}

/**
 * Usort callback: sort line items by amount ascending.
 */
function commerce_discount_extra_sort_by_amount_ascending($a, $b) {
  $a_amount = $a->commerce_unit_price->amount
    ->value();
  $b_amount = $b->commerce_unit_price->amount
    ->value();
  if ($a_amount == $b_amount) {
    return 0;
  }
  return $a_amount < $b_amount ? -1 : 1;
}

/**
 * Usort callback: sort line items by amount descending.
 */
function commerce_discount_extra_sort_by_amount_descending($a, $b) {
  $a_amount = $a->commerce_unit_price->amount
    ->value();
  $b_amount = $b->commerce_unit_price->amount
    ->value();
  if ($a_amount == $b_amount) {
    return 0;
  }
  return $a_amount > $b_amount ? -1 : 1;
}

/**
 * Rules action callback: set product line item to specific amount.
 *
 * @param \EntityDrupalWrapper $wrapper
 *   The line item wrapper the fixed price discount would be applied to.
 * @param string $discount_name
 *   The discount name.
 */
function commerce_discount_extra_fixed_product_price(EntityDrupalWrapper $wrapper, $discount_name) {
  $discount_wrapper = entity_metadata_wrapper('commerce_discount', $discount_name);
  $discount_price = $discount_wrapper->commerce_discount_offer->commerce_fixed_amount
    ->value();

  // Check whether this discount was already added as a price component.
  $price_data = $wrapper->commerce_unit_price->data
    ->value();
  foreach ($price_data['components'] as $component) {
    if (!empty($component['price']['data']['discount_name']) && $component['price']['data']['discount_name'] == $discount_wrapper
      ->getIdentifier()) {
      return;
    }
  }

  // Figure out proper discount amount to apply in order to set proper price.
  $line_item_amount = $wrapper->commerce_unit_price->amount
    ->value();

  // If the discount amount is more expensive than the line item's price, skip.
  if ($line_item_amount < $discount_price['amount']) {
    return;
  }
  module_load_include('inc', 'commerce_discount', 'commerce_discount.rules');
  $discount_price['amount'] = -($line_item_amount - $discount_price['amount']);
  commerce_discount_add_price_component($wrapper, $discount_name, $discount_price);
}

Functions

Namesort descending Description
commerce_discount_extra_fixed_product_price Rules action callback: set product line item to specific amount.
commerce_discount_extra_per_quantity_category_discount Rules action callback: apply a per quantity discount based on product categories to an order.
commerce_discount_extra_per_quantity_discount Rules action callback: apply a per quantity discount to an order.
commerce_discount_extra_rules_action_info Implements hook_rules_action_info().
commerce_discount_extra_rules_condition_info Implements hook_rules_condition_info_alter().
commerce_discount_extra_sort_by_amount_ascending Usort callback: sort line items by amount ascending.
commerce_discount_extra_sort_by_amount_descending Usort callback: sort line items by amount descending.