You are here

function uc_ups_quote in Ubercart 5

Same name and namespace in other branches
  1. 8.4 shipping/uc_ups/uc_ups.module \uc_ups_quote()
  2. 6.2 shipping/uc_ups/uc_ups.module \uc_ups_quote()
  3. 7.3 shipping/uc_ups/uc_ups.module \uc_ups_quote()

Callback for retrieving a UPS shipping quote.

Request a quote for each enabled UPS Service. Therefore, the quote will take longer to display to the user for each option the customer has available.

Parameters

$products: Array of cart contents.

$details: Order details other than product information.

Return value

JSON object containing rate, error, and debugging information.

2 string references to 'uc_ups_quote'
hook_shipping_method in docs/hooks.php
Define callbacks and service options for shipping methods.
uc_ups_shipping_method in shipping/uc_ups/uc_ups.module
Implementation of Übercart's hook_shipping_method().

File

shipping/uc_ups/uc_ups.module, line 805
Shipping quote module that interfaces with www.ups.com to get rates for small package shipments.

Code

function uc_ups_quote($products, $details) {
  include_once drupal_get_path('module', 'uc_store') . '/includes/simplexml.php';
  $quotes = array();
  $method = uc_ups_shipping_method();
  $addresses = array(
    (array) variable_get('uc_quote_store_default_address', new stdClass()),
  );
  $key = 0;
  $last_key = 0;
  $packages = array();
  if (variable_get('uc_ups_all_in_one', true) && count($products) > 1) {
    foreach ($products as $product) {
      if ($product->nid) {

        // Packages are grouped by the address from which they will be
        // shipped. We will keep track of the different addresses in an array
        // and use their keys for the array of packages.
        $address = (array) uc_quote_get_default_shipping_address($product->nid);
        $key = array_search($address, $addresses);
        if ($key === false) {

          // This is a new address. Increment the address counter $last_key
          // instead of using [] so that it can be used in $packages and
          // $addresses.
          $addresses[++$last_key] = $address;
          $key = $last_key;
        }
      }

      // Add this product to the last package from the found address or start
      // a new package.
      if (isset($packages[$key]) && count($packages[$key])) {
        $package = array_pop($packages[$key]);
      }
      else {
        $package = _uc_ups_new_package();
      }
      $weight = $product->weight * $product->qty * uc_weight_conversion($product->weight_units, 'lb');
      $package->weight += $weight;
      $package->price += $product->price * $product->qty;
      $conversion = uc_weight_conversion($product->length_units, 'in');
      $package->length = max($product->length * $conversion, $package->length);
      $package->width = max($product->width * $conversion, $package->width);
      $package->height = max($product->height * $conversion, $package->height);
      $packages[$key][] = $package;
    }
    foreach ($packages as $addr_key => $shipment) {
      foreach ($shipment as $key => $package) {
        if (!$package->weight) {
          unset($packages[$addr_key][$key]);
          continue;
        }
        elseif ($package->weight > 150) {

          // UPS has a weight limit on packages of 150 lbs. Pretend the
          // products can be divided into enough packages.
          $qty = floor($package->weight / 150) + 1;
          $package->qty = $qty;
          $package->weight /= $qty;
          $package->price /= $qty;
        }
      }
    }
  }
  else {
    foreach ($products as $product) {
      $key = 0;
      if ($product->nid) {
        $address = (array) uc_quote_get_default_shipping_address($product->nid);
        $key = array_search($address, $addresses);
        if ($key === false) {
          $addresses[++$last_key] = $address;
          $key = $last_key;
        }
      }
      if (!$product->pkg_qty) {
        $product->pkg_qty = 1;
      }
      $num_of_pkgs = (int) ($product->qty / $product->pkg_qty);
      if ($num_of_pkgs) {
        $package = drupal_clone($product);
        $package->description = $product->model;
        $package->weight = $product->weight * $product->pkg_qty;
        $package->price = $product->price * $product->pkg_qty;
        $package->qty = $num_of_pkgs;
        $package->pkg_type = $product->ups ? $product->ups['pkg_type'] : '02';
        if ($package->weight) {
          $packages[$key][] = $package;
        }
      }
      $remaining_qty = $product->qty % $product->pkg_qty;
      if ($remaining_qty) {
        $package = drupal_clone($product);
        $package->description = $product->model;
        $package->weight = $product->weight * $remaining_qty;
        $package->price = $product->price * $remaining_qty;
        $package->qty = 1;
        $package->pkg_type = $product->ups ? $product->ups['pkg_type'] : '02';
        if ($package->weight) {
          $packages[$key][] = $package;
        }
      }
    }
  }
  if (!count($packages)) {
    return array();
  }
  $dest = (object) $details;
  foreach ($packages as $key => $ship_packages) {
    $orig = (object) $addresses[$key];
    $orig->email = variable_get('uc_store_email', '');
    foreach (array_keys(array_filter(variable_get('uc_ups_services', array()))) as $ups_service) {
      $request = uc_ups_shipping_quote($ship_packages, $orig, $dest, $ups_service);
      $resp = drupal_http_request(variable_get('uc_ups_connection_address', 'https://wwwcie.ups.com/ups.app/xml/') . 'Rate', array(), 'POST', $request);
      if (user_access('configure quotes') && variable_get('uc_quote_display_debug', false)) {
        $quotes[$ups_service]['debug'] .= htmlentities($request) . ' <br /><br /> ' . htmlentities($resp->data);
      }
      $response = new JSimpleXML();
      $response
        ->loadString($resp->data);
      if (isset($response->document->response[0]->error)) {
        foreach ($response->document->response[0]->error as $error) {
          if (user_access('configure quotes') && variable_get('uc_quote_display_debug', false)) {
            $quotes[$ups_service]['error'][] = $error->errorseverity[0]
              ->data() . ' ' . $error->errorcode[0]
              ->data() . ': ' . $error->errordescription[0]
              ->data();
          }
          if ($error->errorseverity[0]
            ->data() == 'HardError') {

            // All or nothing quote. If some products can't be shipped by
            // a certain service, no quote is given for that service. If
            // that means no quotes are given at all, they'd better call in.
            unset($quotes[$ups_service]['rate']);
          }
        }
      }

      // if NegotiatedRates exist, quote based on those, otherwise, use TotalCharges
      if (isset($response->document->ratedshipment)) {
        $charge = $response->document->ratedshipment[0]->totalcharges[0];
        if (isset($response->document->ratedshipment[0]->negotiatedrates)) {
          $charge = $response->document->ratedshipment[0]->negotiatedrates[0]->netsummarycharges[0]->grandtotal[0];
        }
        if (!isset($charge->currencycode) || $charge->currencycode[0]
          ->data() == variable_get('uc_currency_code', "USD")) {
          $rate = uc_ups_markup($charge->monetaryvalue[0]
            ->data());
          $quotes[$ups_service]['rate'] += $rate;
        }
      }
    }
  }
  uasort($quotes, 'uc_quote_price_sort');
  foreach ($quotes as $key => $quote) {
    if (isset($quote['rate'])) {
      $quotes[$key]['format'] = uc_currency_format($quote['rate']);
      $quotes[$key]['option_label'] = '<img class="ups_logo" src="' . base_path() . drupal_get_path('module', 'uc_ups') . '/uc_ups_logo.gif" /> ' . $method['ups']['quote']['accessorials'][$key] . t(' Rate');
    }
  }

  /**
   * Ugly hack to work around PHP bug, details here:
   *   http://bugs.php.net/bug.php?id=23220
   * We strip out errors that look something like:
   *  warning: fread() [function.fread]: SSL fatal protocol error in...
   * Copied from http://drupal.org/node/70915 and then improved by Lyle.
   */
  $messages = drupal_set_message();
  $errors = $messages['error'];
  $total = count($errors);
  for ($i = 0; $i <= $total; $i++) {
    if (strpos($errors[$i], 'SSL: fatal protocol error in')) {
      unset($_SESSION['messages']['error'][$i]);
    }
  }
  if (empty($_SESSION['messages']['error'])) {
    unset($_SESSION['messages']['error']);
  }
  db_query("DELETE FROM {watchdog} WHERE type = 'php' AND message LIKE '%%SSL: fatal protocol error%%'");

  // End of ugly hack.
  return $quotes;
}