Major issue with function uc_quote_request_quotes

Project: 
Ubercart
Category: 
bug report
Priority: 
critical
Status: 
active

After many hours of debugging a the Canada Post weight issue and thinking I had everything fixed I came upon another issue that really baffled me.

Quotes were returning beautifully from CP as expected in the cart/checkout phase.

The problem started when I would attempt to go to the review stage.
I would get kicked back to the checkout phase with an error "Invalid Option. Select a correct shipping quote"

After many more hours of debugging and cursing and sheer terror that this was an unfindable issue I twigged on the fact that an entirely different data structure was being passed to the quote method when going to the review stage.

The data passed is part of the checkout phase, serialized product list, it is hacked apart and then assigned into a simple product structure and passed to the quote method. The weight_units attribute is not part of this structure and the Canada Post module was merrily sending along my 750gram order as a 750kg order.

Canada Post of course returns an error that is ignored as a bad response.

Here is the function in uc_quote.module

<?php
/**
* Callback to return the shipping quote(s) of the appropriate quoting method(s).
*/
function uc_quote_request_quotes(){
 
/* print '<pre>';
  print_r($_POST);
  print '</pre>'; */
 
ob_start();
 
 
$details = $_POST['details'];
  if (!isset(
$details['country'])){
   
$details['country'] = variable_get('uc_store_country', 840);
  }
 
$products = array();
  foreach (
explode('|', urldecode($_POST['products'])) as $item){
   
$props = explode('^', $item);
   
$product = new stdClass();
   
$product->nid = $props[0];
   
$product->title = $props[1];
   
$product->model = $props[2];
   
$product->manufacturer = $props[3];
   
$product->qty = $props[4];
   
$product->cost = $props[5];
   
$product->price = $props[6];
   
$product->weight = $props[7];
   
$product->data = $props[8];
   
$node = (array)node_load($product->nid);
    foreach (
$node as $key => $value){
      if (!isset(
$product->$key)){
       
$product->$key = $value;
      }
    }
   
$products[] = $product;
  }
?>

The issue isn't as simple as adding in the weight unit property here as is doesn't exist in the serialized data that is stored in the form.

Changing uc_cart_get_contents would be my first guess as to where to start. I am assuming the product nodes are recreated to keep the serialized data in the form to a minimum. Given that this is likely to cause issues for other shipping modules or many other things, perhaps we need a better solution?

Something like storing the nid's and sku's of the cart contents and then doing node_load/product_load. All those functions should have static caches of the data anyways, so the hit should be negligible?

For now I have hacked the Canada Post module to default to grams if no weight unit is provided. This causes any product to the be weight in any other unit to show a very small weight instead, greatly skewing the shipping costs.

Re: Major issue with function uc_quote_request_quotes

That's why there is a node_load() in the code you posted, before the second foreach(). The cart and order objects keep track of all the data that can be different from the node itself. Weight units and shipping information aren't changeable in the order-edit form, so all that is loaded in from the node.

The same thing is done in the AJAX call to get the shipping quote form and in the processing phase before the review screen. The products shouldn't look any different to the quote methods at either time.

Weight units are a fairly recent addition, but I can't think of a good reason for it to be set at one point but not the next. Check that your nodes have had their weight_units set, and have your module use the store default with variable_get('uc_weight_unit', 'lb'). That variable can be set in the Store settings under Formats.

Re: Re: Major issue with function uc_quote_request_quotes

I am having a hard time understanding why the products are passed passed to the quote:

1 - In the checkout phase passed from the uc_cart_get_contents() (Actual complete nodes)

2 - In the review phase the data passed is from the serialized data created in the function I posted above.

Why not use a session variable with the $cid and a list of node objects inside of it?

Gord.