3 replies [Last post]
hellomobe's picture
Offline
Joined: 02/15/2011
Juice: 16
Was this information Helpful?

I'm using UPS and FedEx quotes. I would like to offer free shipping for "qualified" products when the total for "qualified products" in the cart exceeds $250. Non-qualified products may also be in the cart and shipping will still apply to those items. (same way Amazon does it)

1. I have a cck field called field_shipping_extra to indicate if the product is "qualified" for free shipping
Value of the field: Null = qualifies, 1=oversized (doesn't qualify)

2. Flat Rate won't work, because if I have a non-qualified product in the cart (regardless of cart total), I need to calculate and charge for its shipping.

3. Examples:

* = qualified
A* = $100
B = $300

Cart 1 = A* x 1 + B x 1 = $400 (cart total doesn't really matter), BUT only $100 for the total of qualified products -> Free shipping doesn't apply

Cart 2 = A* x 3 + B x 1 = $600, WITH $300 total of qualified products -> Shipping is only calculated on B, and A* ships for free.

4. I created a custom module with the following code, piecemeal from several forum topics. I am not a programmer so I have no idea if it's set up correctly, but hopefully at the very least it gives a train of logic on perhaps how to address this case. It currently doesn't return any value - the shipping status bar just runs. Any help, advice, or alternative approach is greatly appreciated!

<?php
function mymodule_uc_nonshippable_cart_item($op, &$item) {
    switch (
$op) {
    case
'load':
       
$items = uc_cart_get_contents();
          if (!empty(
$items)) {
                foreach (
$items as $product) {
                 
//Get qualified products and calculate total of qualified products
                
if ($product->field_shipping_extra[0]['value']!= 1){
                 
$total += ($product->price) * $product->qty;
                }
            }
        }
       
//Check if qualified total is over 250
       
if ($total > 250.00) {
             foreach (
$items as $product) {
                 if (
$product->field_shipping_extra[0]['value']!= 1){
                  
//if a qualified product, set shipping to 0 so it won't calculate           
               
$item->data['shippable'] = "0";                       
                } else {
               
//if a non-qualified product, still calculate shipping
               
$item->data['shippable'] = "1";
                }
             }
          }
      
     break;
    }
}
?>
hellomobe's picture
Offline
Joined: 02/15/2011
Juice: 16
Re: Free Shipping for "qualified" products in cart totaling over

I'm realizing the above doesn't work because it's already set the shippable value before the total cart price - no? If I had an ego, it's completely humbled.

I've taken a different approach and hacked the uc.ups.module. I know...I'm going down in flames. Tell me how to get it into a template or my custom module and I'll do it.

Here is my code (the function starts around 693, I've included the code above and below my addition so you can find the placement). *It doesn't work. It's not reading my cck field - $product->field_shipping_extra[0]['value'] ==1. (maybe the above does work and my query of the cck field is incorrect...) Any help is again appreciated.

Maybe we narrow this down to how do I structure $product->field_shipping_extra[0]['value'] ==1 (do I have to do a node_load; and if so, how)?

<?php
function uc_ups_quote($products, $details) {
 
$quotes = array();

 

$method = uc_ups_shipping_method();

 

$addresses = array((array)variable_get('uc_quote_store_default_address', new stdClass()));
 
$key = 0;CD
  $last_key
= 0;
 
$packages = array();

  

//kh modified
  //get cart total for qualified items
     
if (!empty($products)) {
           foreach (
$products as $product) {
        if(
$product->field_shipping_extra[0]['value'] !=1){
     
//Calculate total of qualified products
     
$total += ($product->price) * $product->qty;
                }
            }
        }
       
   
//if total qualifies for free shipping, then filter $products array to be only
    //those products that aren't qualified for free shipping
     
if ($total >= 250){
        foreach (
$products as $product){
        if(
$product->field_shipping_extra[0]['value'] ==1){
        return
$products;
            }
        }
    }
   
//I assume that if that if the condition isn't true, the $products array
    //will stay as all products in the cart without an else statement. kh end
 
 
if (variable_get('uc_ups_all_in_one', TRUE) && count($products) > 1) {
    foreach (
$products as $product) {....
?>
hellomobe's picture
Offline
Joined: 02/15/2011
Juice: 16
Solution!

Here is the solution I ended up with. A few notes: the code in the original question didn't work because I needed to check the cart's total - but to do this I was calling uc_cart_get_contents() inside the function uc_cart_get_contents() ...essentially a loop. Otherwise the code based off ryanschmidt's code works for changing the data. Without the cart total check it would look like this:

<?php
function customsite_cart_item($op, &$item) {
    switch (
$op) {
    case
'load':
           
$nid=$item->nid;
           
$node=node_load($nid);
            if(
$node->field_shipping_extra[0]['value'] != '1'){
                  
//if a qualified product, set shipping to 0 so it won't calculate           
               
$item->data['shippable']= "0";               
               
uc_cart_update_item($item);
                }
                               
     break;
    }
}
?>

To get to my needed solution, it was a little more involved - if any one has a more direct route I'm all ears. I have not tested this for the admin to prepare shipping (where the admin is paying the full shipping rate versus the customers discounted/free shipping rate).

1. In template.php put the following

<?php

//called in uc_ups.module in the function uc_ups_quote after $last_key = 0; line
//this checks the total non-oversized prodcuts, if over 250, redefines $products to only
//oversized products that will still be charged shipping
function cps_ship_discount($products){
   
//$products = uc_cart_get_contents();
   
if (!empty($products)) {
           foreach (
$products as $product) {
       
$nid=$product->nid;
       
$node=node_load($nid);   
        if(
$node->field_shipping_extra[0]['value'] != 1){
     
//Calculate total of qualified products
     
$total += ($product->price) * $product->qty;
        }
        }
    if (
$total >= 250.0){
       
$oversize_products = cps_filter_by_value($products);
        return
$oversize_products;
        } else{
      return
$products;   
        }
   
}
}
//used by cps_ship_discount to filter products to only those that are oversized
function cps_filter_by_value($products){
       foreach (
$products as $i=>$product) {
           
$nid=$product->nid;
           
$node=node_load($nid);
           
//print $nid;
           
if ($node->field_shipping_extra[0]['value'] == '1'){
              
$newarray[] = $products[$i];
             }
         }
         return
$newarray;
    }

//used in customsite.module for function customsite_condition_standard_total
function cart_standard_total($products){
   
//$products = uc_cart_get_contents();
   
if (!empty($products)) {
           foreach (
$products as $product) {
       
$nid=$product->nid;
       
$node=node_load($nid);   
        if(
$node->field_shipping_extra[0]['value'] != 1){
     
//Calculate total of qualified products
     
$total += ($product->price) * $product->qty;
        }
        }
     return
$total;
}
}
?>

2. For UPS to only calculate oversized products if non-oversized product subtotal > 250. In uc_ups.module at function uc_ups_quote after $last_key = 0;

<?php

//kh added to filter products if cart is over $250 and products is oversized
 
$products = cps_ship_discount($products);
?>

and change around line 763

<?php
else{
?>

to

<?php
elseif (variable_get('uc_ups_all_in_one', TRUE) && count($products) == 1){
?>

otherwise you'll get an error if there is only one oversized product (NOTE: Oversized can be any non-qualified product)

3. In a custom module - mine is named customsite.module put the following hook_ca_conditions

<?php
/**
* Implementation of hook_ca_condition().
*/
function customsite_ca_condition() {
  return array(
   
'customsite_condition_standard_total' => array(
       
'#title' => t('Check the non-oversize order subtotal'),
       
'#description' => t('Returns TRUE if the current non-oversize order total is within the parameters below.'),
       
'#category' => t('Order'),
       
'#callback' => 'customsite_condition_standard_total',
       
'#arguments' => array(
             
'order' => array('#entity' => 'uc_order', '#title' => t('Order')),
      ),
    ),
   
'customsite_condition_product_oversize' => array(
     
'#title' => t("Order has a product that is oversized"),
     
'#category' => t('Order: Product'),
     
'#callback' => 'customsite_condition_product_oversize',
     
'#arguments' => array(
          
'order' => array('#entity' => 'uc_order', '#title' => t('Order')),
      ),
    ),
  );
}

/**
* Check the current order balance.
*
* @see customsite_condition_standard_total_form()
* see template.php for cart_standard_total function
*/
function customsite_condition_standard_total($order, $settings) {
 
$total = cart_standard_total($order->products);
  switch (
$settings['order_std_total_comparison']) {
    case
'less':
      return
$total < $settings['order_total_std_value'];
    case
'less_equal':
      return
$total <= $settings['order_total_std_value'];
    case
'equal':
      return
$total == $settings['order_total_std_value'];
    case
'greater_equal':
      return
$total >= $settings['order_total_std_value'];
    case
'greater':
      return
$total > $settings['order_total_std_value'];
  }
}

/**
* @see customsite_condition_standard_total()
*/
function customsite_condition_standard_total_form($form_state, $settings = array()) {
 
$form['order_total_std_value'] = array(
   
'#type' => 'textfield',
   
'#title' => t('Order Non-oversized Total value'),
   
'#description' => t('Specify a value to compare the order total against.'),
   
'#default_value' => $settings['order_total_std_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_std_total_comparison'] = array(
   
'#type' => 'radios',
   
'#title' => t('Order Non-oversized total comparison type'),
   
'#options' => $options,
   
'#default_value' => isset($settings['order_std_total_comparison']) ? $settings['order_std_total_comparison'] : 'greater_equal',
  );

  return

$form;
}

/**
* Returns true if the order has a product that is oversized.
*
* @see customsite_condition_product_oversize_form()
*/
function customsite_condition_product_oversize($order, $settings) {
 
$result = FALSE;
  foreach (
$order->products as $product) {
   
$nid=$product->nid;
   
$node=node_load($nid);   
    if(
$node->field_shipping_extra[0]['value'] == $settings['type']){
   
//if ($product->nid && uc_product_get_shipping_type($product) == $settings['type']) {
     
$result = TRUE;
      break;
    }
  }
  return
$result;
}

/**
* Settings form for customsite_condition_product_oversize().
*
*/
function customsite_condition_product_oversize_form($form_state, $settings) {
 
$form = array();

 

$options = array(
   
'NULL' => t('Product is standard.'),
   
'1' => t('Product is oversized.'),
   
'2' => t('Product is freight.'),
  );
 
$form['type'] = array(
   
'#type' => 'radios',
   
'#title' => t('Weight type'),
   
'#options' => $options,
   
'#default_value' => $settings['type'],
     );

  return

$form;
}
?>

4. Lastly, add the free shipping option - Flat Rate: Free Shipping. Add the new conditions we create in #3 - Check orders non-oversized subtotal (configure as needed) AND Order has a product that is oversized (select oversize/nonqualified, and negate since free shipping won't apply if there IS an oversized product in the order).

You could also add the conditions to UPS to not show a quote when the shipping is free, but I like that it shows the customer what they saved. I'm confident that my customers will select free shipping if it's available =0)

I'm not too excited about the node_loads() so if someone can tell me an alternative/better way, input is welcome.

ryanschmidt's picture
Offline
Joined: 11/17/2008
Juice: 235
Re: Solution!

Looks like a good solution to me! Glad you figured it out and happy to be of any assistance. And yes, if someone has any suggestions on how we could avoid using node_load I'd be all ears too.