The select query in uc_shipping_package_load() does not take into account the possibility of product revisions, so it loads the same product multiple times. This causes an issue with the calculated weight of the package.

Here is the relevant code:

<?php
/**
 * Load a package and its products.
 */
function uc_shipping_package_load($package_id) {
  static $packages = array();

  if (!isset($packages[$package_id])) {
    $result = db_query("SELECT * FROM {uc_packages} WHERE package_id = %d", $package_id);
    if ($package = db_fetch_object($result)) {
      $products = array();
      $description = '';
      $weight = 0;
      $units = variable_get('uc_weight_unit', 'lb');
      $addresses = array();
      $result = db_query("SELECT op.order_product_id, pp.qty, pp.qty * op.weight AS weight, p.weight_units, op.nid, op.title, op.model, op.price, op.data FROM {uc_packaged_products} AS pp LEFT JOIN {uc_order_products} AS op ON op.order_product_id = pp.order_product_id LEFT JOIN {uc_products} AS p ON op.nid = p.nid WHERE pp.package_id = %d ORDER BY op.order_product_id", $package_id);
      while ($product = db_fetch_object($result)) {
        $address = uc_quote_get_default_shipping_address($product->nid);
        // TODO: Lodge complaint that array_unique() compares as strings.
        if (!in_array($address, $addresses)) {
          $addresses[] = $address;
        }
        $description .= ', '. $product->qty .' x '. $product->model;
        // Normalize all weights to default units.
        $weight += $product->weight * uc_weight_conversion($product->weight_units, $units);
        $product->data = unserialize($product->data);
        $products[$product->order_product_id] = $product;
      }
...
?>

If there is more than one revision of a product, the while() loop will iterate once for each of them. As you can see, this affects a few things... but the one that has a lasting effect is the weight. It is essentially multiplied by the number of product revisions.

The solution is fairly simple: add a "GROUP BY order_product_id" to the query.

Patch on the way!

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

m.stenta’s picture

Status: Active » Needs review
FileSize
1.36 KB

Patch attached. Simply adds the following to the query:

GROUP BY op.order_product_id

Note that this addition sits nicely alongside the existing ORDER BY op.order_product_id. :-)

m.stenta’s picture

Status: Needs review » Needs work

I discovered another issue with this, which my patch does not solve (changing this to Needs Work).

Simply adding the GROUP BY does not necessarily load all the information from the correct revision. For example, I'm finding that the "p.weight_units" is not returning the most recent revision's units.

m.stenta’s picture

Status: Needs work » Needs review
FileSize
1.39 KB

Attached is a new patch which ensures that only the latest revision of the product is loaded in the query. It does this by adding an additional join to the {node} table and a WHERE condition that checks to make sure {node}.vid = {uc_product}.vid.

Now, the query looks like this (additions in bold):

SELECT
op.order_product_id,
pp.qty,
pp.qty * op.weight AS weight,
p.weight_units,
op.nid,
op.title,
op.model,
op.price,
op.data
FROM {uc_packaged_products} AS pp
LEFT JOIN {uc_order_products} AS op ON op.order_product_id = pp.order_product_id
LEFT JOIN {uc_products} AS p ON op.nid = p.nid
LEFT JOIN {node} n ON n.nid = p.nid
WHERE
n.vid = p.vid
AND
pp.package_id = %d
ORDER BY op.order_product_id

TR’s picture

Version: 6.x-2.x-dev » 8.x-4.x-dev
Component: Shipping » Fulfillment