1

I'm trying to define a default rule in a small module based on Commerce Price Table and Commerce Rules Extra. The rule is to set price based on the quantity of produtcs that contains a partial sku in the current order and the price table of the line item.

I'm new to Rules... and commerce. I'm not sure if I set the default rule correctly to get the current order or if the error is in the definition of the action (or if it's to late at night for me to see it!).

The error I got : Error: Missing configuration for parameter commerce_order.

Here is the current state of the code:

/**
* Based on Commerce Price Table and Commerce Rules Extra
*/

/**
 * Implements hook_default_rules_configuration().
 */
function commerce_price_table_sku_contains_default_rules_configuration() {
  $rules = array();

  // Add a reaction rule to update a shopping cart order's status to "Shopping
  // cart" when a product is added to or removed from the order.
  $rule = rules_reaction_rule();

  $rule->label = t('Override price with price table if sku contains');
  $rule->active = TRUE;

  $rule
    ->event('commerce_product_calculate_sell_price')
    ->condition('entity_has_field', array(
      'entity:select' => 'commerce-line-item',
      'field' => 'commerce_product',
    ))
    ->condition('entity_has_field', array(
      'entity:select' => 'commerce-line-item:commerce-product',
      'field' => 'field_price_table',
    ))
    ->action('commerce_price_table_sku_contains_set_price', array(
      'site:current-cart-order' => 'commerce_order', // Wrong? Error: Missing configuration for parameter commerce_order.
      'product_id' => '',
      'commerce_line_item:select' => 'commerce-line-item',
      'price_table:select' => 'commerce-line-item:commerce-product:field-price-table',
      'component_name' => 'base_price',
    ));

  $rule->weight = -9;

  $rules['commerce_price_table_sku_contains_override_price'] = $rule;

  return $rules;
}

/**
 * Implements hook_rules_action_info().
 */
function commerce_price_table_sku_contains_rules_action_info() {
  $actions = array();

  $actions['commerce_price_table_sku_contains_set_price'] = array(
    'label' => t('Set the unit price to a table based price'),
    'parameter' => array(
      'commerce_order' => array( // Wrong? Error: Missing configuration for parameter commerce_order.
        'type' => 'commerce_order',
        'label' => t('Order'),
        'description' => t('The order whose line items should be checked for the specified product. If the specified order does not exist, the quantity is 0.'),
      ),
      'product_id' => array(
        'type' => 'text',
        'label' => t('Product(s) SKU(s)'),
        'description' => t('The SKU of the product must contain this information. Each SKUs on a newline'),
      ),
      'commerce_line_item' => array(
        'type' => 'commerce_line_item',
        'label' => t('Line item'),
      ),
      'price_table' => array(
        'label' => t('Price table'),
        'type' => 'list<commerce_price_table>',
      ),
      'component_name' => array(
        'type' => 'text',
        'label' => t('Price component type'),
        'description' => t('Price components track changes to prices made during the price calculation process, and they are carried over from the unit price to the total price of a line item. When an order total is calculated, it combines all the components of every line item on the order. When the unit price is altered by this action, the selected type of price component will be added to its data array and reflected in the order total display when it is formatted with components showing. Defaults to base price, which displays as the order Subtotal.'),
        'options list' => 'commerce_line_item_price_component_options_list',
        'default value' => 'base_price',
      ),
    ),
    'group' => t('Commerce price table sku contains'),
  );

  return $actions;
}

/**
 * Rules callback: executes the "Replace the price for a price table" action.
 */
function commerce_price_table_sku_contains_set_price($order, $sku, $line_item, $price_table, $component_name) {
  // If the line item contains a product...
  if (in_array($line_item->type, commerce_product_line_item_types())) {
    // Load its referenced product.
    $line_item_wrapper = entity_metadata_wrapper('commerce_line_item', $line_item);
    $product = $line_item_wrapper->commerce_product->value();

    // Bail now if the unit price is unset.
    $unit_price = commerce_price_wrapper_value($line_item_wrapper, 'commerce_unit_price');

    if (empty($unit_price)) {
      return;
    }

    // Fetch the quantity of product that contains the partials skus in the order.
    $quantity = commerce_price_table_sku_contains_partial_sku($order, $sku);

    // Fetch the table based price for the current product quantity.
    $table_price = commerce_price_table_get_amount_qty($product, $quantity, $price_table);

    // If we got a valid table price...
    if (!empty($table_price)) {
      // If the currency is different from the current currency, convert it.
      if ($unit_price['currency_code'] != $table_price['currency_code']) {
        $line_item_wrapper->commerce_unit_price->amount = commerce_currency_convert($unit_price['amount'], $unit_price['currency_code'], $table_price['currency_code']);
        $line_item_wrapper->commerce_unit_price->currency_code = $table_price['currency_code'];

        // Convert the currency code of the price's components.
        if (!empty($unit_price['data']['components'])) {
          foreach ($unit_price['data']['components'] as $key => &$component) {
            $component['price']['amount'] = commerce_currency_convert($component['price']['amount'], $component['price']['currency_code'], $table_price['currency_code']);
            $component['price']['currency_code'] = $table_price['currency_code'];
          }

          $wrapper->commerce_unit_price->data = $unit_price['data'];
        }
      }

      // Calculate the difference between the current unit price amount and the
      // table price and create a price array representing the difference.
      $current_amount = $unit_price['amount'];
      $updated_amount = $table_price['amount'];

      $difference = array(
        'amount' => $updated_amount - $current_amount,
        'currency_code' => $table_price['currency_code'],
        'data' => array(),
      );

      // Set the amount of the unit price and add the difference as a component.
      $line_item_wrapper->commerce_unit_price->amount = $updated_amount;

      $line_item_wrapper->commerce_unit_price->data = commerce_price_component_add(
        $line_item_wrapper->commerce_unit_price->value(),
        $component_name,
        $difference,
        TRUE
      );
    }
  }
}


/**
 * Checks to see if a particular products exists on an order
 * and calculates the total quantity, based on a sku fragment.
 */
function commerce_price_table_sku_contains_partial_sku($order, $skus) {
  // Initialize
  $total_quantity = 0;

  // Don't do anything without 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 SKU and quantity from line item.
        $line_item_sku = $line_item_wrapper->commerce_product->sku->value();
        $quantity = $line_item_wrapper->quantity->value();

        // Does the SKU exist partially in the order SKU?
        // multiple skus on newlines
        $sku = strtok($skus, PHP_EOL);
        while ($sku !== false) {
            if (strpos($line_item_sku, $sku) !== FALSE) {
              // Update quantity.
              $total_quantity += $quantity;
            }
            $sku = strtok( PHP_EOL );
        }
      }
    }
  }

  return $total_quantity;
}

Thx!

  • The error is in the _default_rules_configuration(). I changed the "site:current-cart-order" to "commerce-line-item:order". But the same error appears. When I edit the rule using the interface the order dataselector is empty. If I select there commerce-line-item:order the rule works. How do I define commerce-line-item:order correctly in the _default_rules_configuration()? Thx! – Dadaisme Jun 28 '16 at 17:12
0

The error was simple and in the _default_rules_configuration().

It was just the array key name that was wrong... it was late last night.

The second modification is to use "commerce-line-item:order" insted of "site:current-cart-order".

Here is the code that works:

<?php
/**
 * Based on Commerce Price Table and Commerce Rules Extra
 */

/**
 * @file
 * Default rule configurations for Cart.
 */

/**
 * Implements hook_default_rules_configuration().
 */
function commerce_price_table_sku_contains_default_rules_configuration() {
    $rules = array();

    // Add a reaction rule to update a shopping cart order's status to "Shopping
    // cart" when a product is added to or removed from the order.
    $rule = rules_reaction_rule();

    $rule->label = t('Override price with price table if sku contains');
    $rule->active = TRUE;

    $rule
        ->event('commerce_product_calculate_sell_price')
        ->condition('entity_has_field', array(
            'entity:select' => 'commerce-line-item',
            'field' => 'commerce_product',
        ))
        ->condition('entity_has_field', array(
            'entity:select' => 'commerce-line-item:commerce-product',
            'field' => 'field_price_table',
        ))
        ->action('commerce_price_table_sku_contains_set_price', array(
            'order' => 'commerce-line-item:order',
            'product_id' => '',
            'commerce_line_item:select' => 'commerce-line-item',
            'price_table:select' => 'commerce-line-item:commerce-product:field-price-table',
            'component_name' => 'base_price',
        ));

    $rule->weight = -9;

    $rules['commerce_price_table_sku_contains_override_price'] = $rule;

    return $rules;
}

/**
 * Based on Commerce Price Table and Commerce Rules Extra
 */


/**
 * Implements hook_rules_action_info().
 */
function commerce_price_table_sku_contains_rules_action_info() {
    $actions = array();

    $actions['commerce_price_table_sku_contains_set_price'] = array(
        'label' => t('Set the unit price to a table based price'),
        'parameter' => array(
            'order' => array( 
                'type' => 'commerce_order',
                'label' => t('Order'),
                'description' => t('The order whose line items should be checked for the specified product. If the specified order does not exist, the quantity is 0.'),
            ),
            'product_id' => array(
                'type' => 'text',
                'label' => t('Product(s) SKU(s)'),
                'description' => t('The SKU of the product must contain this information. Each SKUs on a newline'),
            ),
            'commerce_line_item' => array(
                'type' => 'commerce_line_item',
                'label' => t('Line item'),
            ),
            'price_table' => array(
                'label' => t('Price table'),
                'type' => 'list<commerce_price_table>',
            ),
            'component_name' => array(
                'type' => 'text',
                'label' => t('Price component type'),
                'description' => t('Price components track changes to prices made during the price calculation process, and they are carried over from the unit price to the total price of a line item. When an order total is calculated, it combines all the components of every line item on the order. When the unit price is altered by this action, the selected type of price component will be added to its data array and reflected in the order total display when it is formatted with components showing. Defaults to base price, which displays as the order Subtotal.'),
                'options list' => 'commerce_line_item_price_component_options_list',
                'default value' => 'base_price',
            ),
        ),
        'group' => t('Commerce price table sku contains'),
    );

    return $actions;
}

/**
 * Rules callback: executes the "Replace the price for a price table" action.
 */
function commerce_price_table_sku_contains_set_price($order, $sku, $line_item, $price_table, $component_name) {
    // If the line item contains a product...
    if (in_array($line_item->type, commerce_product_line_item_types())) {
        // Load its referenced product.
        $line_item_wrapper = entity_metadata_wrapper('commerce_line_item', $line_item);
        $product = $line_item_wrapper->commerce_product->value();

        // Bail now if the unit price is unset.
        $unit_price = commerce_price_wrapper_value($line_item_wrapper, 'commerce_unit_price');

        if (empty($unit_price)) {
            return;
        }

        // Fetch the quantity of product that contains the partials skus in the order.
        $quantity = commerce_price_table_sku_contains_partial_sku($order, $sku);

        // Fetch the table based price for the current product quantity.
        $table_price = commerce_price_table_get_amount_qty($product, $quantity, $price_table);

        // If we got a valid table price...
        if (!empty($table_price)) {
            // If the currency is different from the current currency, convert it.
            if ($unit_price['currency_code'] != $table_price['currency_code']) {
                $line_item_wrapper->commerce_unit_price->amount = commerce_currency_convert($unit_price['amount'], $unit_price['currency_code'], $table_price['currency_code']);
                $line_item_wrapper->commerce_unit_price->currency_code = $table_price['currency_code'];

                // Convert the currency code of the price's components.
                if (!empty($unit_price['data']['components'])) {
                    foreach ($unit_price['data']['components'] as $key => &$component) {
                        $component['price']['amount'] = commerce_currency_convert($component['price']['amount'], $component['price']['currency_code'], $table_price['currency_code']);
                        $component['price']['currency_code'] = $table_price['currency_code'];
                    }

                    $wrapper->commerce_unit_price->data = $unit_price['data'];
                }
            }

            // Calculate the difference between the current unit price amount and the
            // table price and create a price array representing the difference.
            $current_amount = $unit_price['amount'];
            $updated_amount = $table_price['amount'];

            $difference = array(
                'amount' => $updated_amount - $current_amount,
                'currency_code' => $table_price['currency_code'],
                'data' => array(),
            );

            // Set the amount of the unit price and add the difference as a component.
            $line_item_wrapper->commerce_unit_price->amount = $updated_amount;

            $line_item_wrapper->commerce_unit_price->data = commerce_price_component_add(
                $line_item_wrapper->commerce_unit_price->value(),
                $component_name,
                $difference,
                TRUE
            );
        }
    }
}


/**
 * Checks to see if a particular products exists on an order
 * and calculates the total quantity, based on a sku fragment.
 */
function commerce_price_table_sku_contains_partial_sku($order, $skus) {
    // Initialize
    $total_quantity = 0;

    // Don't do anything without 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 SKU and quantity from line item.
                $line_item_sku = $line_item_wrapper->commerce_product->sku->value();
                $quantity = $line_item_wrapper->quantity->value();

                // Does the SKU exist partially in the order SKU?
                // multiple skus on newlines
                $sku = strtok($skus, PHP_EOL);
                while ($sku !== false) {
                        if (strpos($line_item_sku, $sku) !== FALSE) {
                            // Update quantity.
                            $total_quantity += $quantity;
                        }
                        $sku = strtok( PHP_EOL );
                }
            }
        }
    }

    return $total_quantity;
}

Thx.

  • The code still needs work : ) – Dadaisme Jun 28 '16 at 18:43
  • Other problems : 1) The rule name lenght may be too long for direct cloning (a rules module problem), just change the name of the clone. 2) There seems to be some conflict as in drupal.org/node/2168027 Any idea how to resolve #2? How can a rule to set price in cart change the table prices in the product display? – Dadaisme Jun 28 '16 at 20:05
  • Here : drupal.org/node/2756847#comment-11352641 – Dadaisme Jun 30 '16 at 4:02

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.