Drupal 7 Commerce: считаем цену

Для расчёта текущей цены товара  в Drupal Commerce предлагают использовать модуль Rules. А как быть если его возможностей не хватает?

Попробуем использовать для этого  хуки. Прежде всего следует обратить внимание на функцию модуля Product Pricing:

/**
 * Returns the calculated sell price for the given product.
 *
 * @param $product
 *   The product whose sell price will be calculated.
 * @param $precalc
 *   Boolean indicating whether or not the pre-calculated sell price from the
 *     database should be requested before calculating it anew.
 *
 * @return
 *   A price field data array as returned by entity_metadata_wrapper().
 */

function commerce_product_calculate_sell_price($product, $precalc = FALSE) {
  // First create a pseudo product line item that we will pass to Rules.
  $line_item = commerce_product_line_item_new($product);

  // Allow modules to prepare this as necessary.
  drupal_alter('commerce_product_calculate_sell_price_line_item', $line_item);

  // Attempt to fetch a database stored price if specified.
  if ($precalc) {
    $module_key = commerce_product_pre_calculation_key();

    $result = db_select('commerce_calculated_price')
      ->fields('commerce_calculated_price', array('amount', 'currency_code', 'data'))
      ->condition('module', 'commerce_product_pricing')
      ->condition('module_key', $module_key)
      ->condition('entity_type', 'commerce_product')
      ->condition('entity_id', $product->product_id)
      ->condition('field_name', 'commerce_price')
      ->execute()
      ->fetchObject();

    // If a pre-calculated price was found...
    if (!empty($result)) {
      // Wrap the line item, swap in the price, and return it.
      $wrapper = entity_metadata_wrapper('commerce_line_item', $line_item);

      $wrapper->commerce_unit_price->amount = $result->amount;
      $wrapper->commerce_unit_price->currency_code = $result->currency_code;

      // Unserialize the saved prices data array and initialize to an empty
      // array if the column was empty.
      $result->data = unserialize($result->data);
      $wrapper->commerce_unit_price->data = !empty($result->data) ? $result->data : array();

      return $wrapper->commerce_unit_price->value();
    }
  }

  // Pass the line item to Rules.
  rules_invoke_event('commerce_product_calculate_sell_price', $line_item);

  return entity_metadata_wrapper('commerce_line_item', $line_item)->commerce_unit_price->value();
}

Вот эта строка:

drupal_alter('commerce_product_calculate_sell_price_line_item', $line_item);

говорит нам о том, что существует хук hook_commerce_product_calculate_sell_price_line_item()
Переопределим его:

function custom_commerce_product_calculate_sell_price_line_item_alter($line_item) {
  _custom_price_calculate($line_item);
}

Также нам понадобится переопределить еще один хук, чтобы корректно пересчитывать элементы корзины:

function custom_commerce_cart_line_item_refresh($line_item, $order_wrapper) {
  _custom_price_calculate($line_item);
}

А вот и сама функция расчёта цены:

function _custom_price_calculate($line_item) {
  if($line_item->type == 'product') {
    $line_wrapper = entity_metadata_wrapper('commerce_line_item', $line_item);
    $entity = $commerce_product = $line_wrapper->commerce_product->value();
    $product_wrapper = entity_metadata_wrapper('commerce_product', $entity);

    // Получили значение поля price
    $cprice = $product_wrapper->commerce_price->amount->value();
    // что-то с ним сделали...
    // и обновили ценe товара
     $product_wrapper->commerce_price->amount->set($cprice);

      // теперь обновим цену позиции товара. Для этого нужно применить
     //  commerce_line_item_unit_price_amount() чтобы корректно обнвились компоненты
     //  цены.
      module_load_include('rules.inc', 'commerce_line_item');
      commerce_line_item_unit_price_amount(
        $line_item,
        $cprice,
        'base_price',
        COMMERCE_ROUND_HALF_UP);
  }
}

У этого метода есть недостатки, например при массовом пересчете цен с кешированием (это пункт меню admin/commerce/config/product-pricing/pre-calculation) hook_commerce_product_calculate_sell_price_line_item() не вызывается.