When using Flat Rate shipping to determine shipping cost based on Order Total, non-shippable products are not removed from the order-total calculation.

IE:
Item #1 - $5 Shippable
Item #2 - $25 Non-Shippable

Flate Rate Shipping:
Total<$10 = Ship cost: $2.50
Total>$10 = Ship cost: $5.00

Basically if a user orders both items #1 & #2, the will be charged $5 shipping even though #2 is non shippable.

I have played with the flags and filters and can't find a way to exclude non-shippable items from my Fixed Shipping calculations. Can someone please help?

Comments

tr’s picture

Flat Rate, like all other shipping methods, never sees any non-shippable products - those are removed by uc_quote before the shipping method is called. Your problem is that you have the flat rates set up as a base rate only, with no per-product rate. So the shipping cost calculated by your flat rate methods will *always* return the base rate, under all conditions.

The Conditional Actions you set up control whether the shipping method is run or not, based on the total dollar value of the order. They don't calculate shipping cost, nor do they affect the shipping cost. The order total condition tests the order total - the order total doesn't and shouldn't change based on whether some products are shippable or not.

When you set up a rule that allowed Flat Rate to charge a base rate if the order total is >$10, that's what you got, and it worked as designed. If your Flat Rate charged a per-product rate when the order total was >$10, you would see a shipping cost = #shippable products * per-product rate.

So it's your logic that's at fault here, not the flat rate module. If you want to calculate the total price of all shippable products then you're going to have to figure out a combination of conditions or code a PHP condition to compute the value you're interested in.

michaeljw’s picture

Hi,

I'm struggling to get to grips with setting a maximum shipping cost.

The desired shipping costs are:

20% upto a maximum of £12 shipping.

I have used a combination of flat rate and the "Ubercart Percent Rate with Minimum Rate" module to do this as follows:

IF (order total < £60), per product shipping cost is 20% of total;

IF (order total >= £60), base rate shipping = £12, per product shipping = £0.

The problem is that some items are not shippable, meaning that the routine breaks down if the order total is more than £60 and includes a non-shippable product.

I'd be grateful if you could help to solve this?

The best way forward would be if I could somehow set a maximum shipping cost in the Ubercart Percent Rate with Minimum Rate module. Is this achievable? I can do php coding to an intermediate level.

Your ideas would be a great help!

Thanks,
Michael

longwave’s picture

Status: Active » Closed (fixed)

The original issue was solved in #1, and #2 should be a feature request for the module in question.

kristen pol’s picture

You can create your own condition similar to the one in the uc_order module to make this happen as follows:

1) add in hook_ca_condition like:

/**
 * Implementation of hook_ca_condition().
 */
function mymodule_ca_condition() {
  $order_arg = array(
    '#entity' => 'uc_order',
  );
  $conditions['mymodule_condition_shippable_total'] = array(
    '#title' => t('Check the order total for shippable products'),
    '#description' => t('Returns TRUE if the current order total of shippable products is within the parameters below.'),
    '#category' => t('Order'),
    '#callback' => 'mymodule_condition_shippable_total',
    '#arguments' => array(
      'order' => $order_arg,
    ),
  );
  return $conditions;
}

2) Add in the mymodule_condition_shippable_total function like:

/**
 * Check the current order balance for shippable products.
 */
function mymodule_condition_shippable_total($order, $settings) {
  $total = mymodule_get_shippable_total($order);
  switch ($settings['order_total_comparison']) {
    case 'less':
      return $total < $settings['order_total_value'];
    case 'less_equal':
      return $total <= $settings['order_total_value'];
    case 'equal':
      return $total == $settings['order_total_value'];
    case 'greater_equal':
      return $total >= $settings['order_total_value'];
    case 'greater':
      return $total > $settings['order_total_value'];
  }
}

3) Add in helper function mymodule_get_shippable_total like:

/**
 * Calculate up an order's total for shippable products.
 */
function mymodule_get_shippable_total($order) {
  $total = 0;

  if ($order === FALSE) {
    return $total;
  }

  if (is_array($order->products)) {
    $context = array(
      'revision' => 'altered',
      'type' => 'order_product',
    );

    foreach ($order->products as $product) {
      if (uc_cart_product_is_shippable($product)) {
        $price_info = array(
          'price' => $product->price,
          'qty' => ($product->qty) ? $product->qty : 1,
        );
        $context['subject'] = array(
          'order' => $order,
          'product' => $product,
          'node' => node_load($product->nid),
        );
        $total += uc_price($price_info, $context);
      }
    }
  }

  return $total;
}

4) Add in condition settings form like:

/**
 * Settings form for shipping condition.
 */
function mymodule_condition_shippable_total_form($form_state, $settings = array()) {
  $form['order_total_value'] = array(
    '#type' => 'textfield',
    '#title' => t('Shippable products total value'),
    '#description' => t('Specify a value to compare the shippable products total against.'),
    '#default_value' => $settings['order_total_value'],
    '#size' => 16,
    '#field_prefix' => variable_get('uc_sign_after_amount', FALSE) ? '' : variable_get('uc_currency_sign', '$'),
    '#field_suffix' => variable_get('uc_sign_after_amount', FALSE) ? variable_get('uc_currency_sign', '$') : '',
  );

  $options = array(
    'less' => t('Total is less than specified value.'),
    'less_equal' => t('Total is less than or equal to specified value.'),
    'equal' => t('Total is equal to specified value.'),
    'greater_equal' => t('Total is greater than or equal to specified value.'),
    'greater' => t('Total is greater than specified value.'),
  );
  $form['order_total_comparison'] = array(
    '#type' => 'radios',
    '#title' => t('Shippable products total comparison type'),
    '#options' => $options,
    '#default_value' => isset($settings['order_total_comparison']) ? $settings['order_total_comparison'] : 'greater_equal',
  );

  return $form;
}

Have fun!
Kristen

ssn’s picture

Worked like a magic exactly what we needed for our project... u rock Kirsten pol...
you saved my day. :)

many thanks again..

kristen pol’s picture

Glad to hear it was useful for someone ;)

khan2ims’s picture

Thanks a lot. It helped me to add a condition to count number of shippable products.

Here is my code

<?php 
/**
* Implementation of hook_ca_condition().
*/
function custom_shipping_ca_condition() {
  $order_arg = array(
    '#entity' => 'uc_order',
  );
  $conditions['custom_shipping_condition_shippable_products'] = array(
    '#title' => t('Check the number of shippable products'),
    '#description' => t('Returns TRUE if the total number of shippable products is within the parameters below.'),
    '#category' => t('Order: Product'),
    '#callback' => 'custom_shipping_condition_shippable_products',
    '#arguments' => array(
      'order' => $order_arg,
    ),
  );
  return $conditions;
}


/**
* Check the current number of shippable products.
*/
function custom_shipping_condition_shippable_products($order, $settings) {
  $total = custom_shipping_get_shippable_products($order);
  switch ($settings['product_count_comparison']) {
    case 'less':
      return $total < $settings['product_count_value'];
    case 'less_equal':
      return $total <= $settings['product_count_value'];
    case 'equal':
      return $total == $settings['product_count_value'];
    case 'greater_equal':
      return $total >= $settings['product_count_value'];
    case 'greater':
      return $total > $settings['product_count_value'];
  }
}


/**
* Calculate up total number of shippable products.
*/
function custom_shipping_get_shippable_products($order) {
  $total = 0;
if ($order === FALSE) {
    return $total;
  }

  if (is_array($order->products)) {
    $context = array(
      'revision' => 'altered',
      'type' => 'order_product',
    );

    foreach ($order->products as $product) {
      if (uc_cart_product_is_shippable($product)) {
          $total += $product->qty;
       }
    }
  }
  return $total;
}


/**
* Settings form for shipping condition.
*/
function custom_shipping_condition_shippable_products_form($form_state, $settings = array()) {
  $form['product_count_value'] = array(
    '#type' => 'textfield',
    '#title' => t('Product count value'),
    '#description' => t('Specify a value to compare the product count against.'),
    '#default_value' => $settings['product_count_value'],
    '#size' => 16,
  );

  $options = array(
    'less' => t('Total is less than specified value.'),
    'less_equal' => t('Total is less than or equal to specified value.'),
    'equal' => t('Total is equal to specified value.'),
    'greater_equal' => t('Total is greater than or equal to specified value.'),
    'greater' => t('Total is greater than specified value.'),
  );
  $form['product_count_comparison'] = array(
    '#type' => 'radios',
    '#title' => t('Product count comparison type'),
    '#options' => $options,
    '#default_value' => isset($settings['product_count_comparison']) ? $settings['product_count_comparison'] : 'greater_equal',
  );

  return $form;
}