You are here

protected function UPSRateBase::packageProducts in Ubercart 8.4

Organizes products into packages for shipment.

Parameters

OrderProduct[] $products: An array of product objects as they are represented in the cart or order.

Address[] $addresses: Reference to an array of addresses which are the pickup locations of each package. They are determined from the shipping addresses of their component products.

Return value

array Packaged products. Packages are separated by shipping address and weight or quantity limits imposed by the shipping method or the products.

File

shipping/uc_ups/src/Plugin/Ubercart/ShippingQuote/UPSRateBase.php, line 140

Class

UPSRateBase
Common functionality for UPS shipping quotes plugins.

Namespace

Drupal\uc_ups\Plugin\Ubercart\ShippingQuote

Code

protected function packageProducts(array $products, array $addresses) {
  $last_key = 0;
  $packages = [];
  $ups_config = \Drupal::config('uc_ups.settings');
  if ($ups_config
    ->get('all_in_one') && count($products) > 1) {

    // "All in one" packaging strategy.
    // Only need to do this if more than one product line item in order.
    $packages[$last_key] = [
      0 => $this
        ->newPackage(),
    ];
    foreach ($products as $product) {
      if ($product->nid->value) {

        // 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.
        $key = NULL;
        $address = uc_quote_get_default_shipping_address($product->nid->value);
        foreach ($addresses as $index => $value) {
          if ($address
            ->isSamePhysicalLocation($value)) {

            // This is an existing address.
            $key = $index;
            break;
          }
        }
        if (!isset($key)) {

          // 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;
          $packages[$key] = [
            0 => $this
              ->newPackage(),
          ];
        }
      }

      // Grab some product properties directly from the (cached) product
      // data. They are not normally available here because the $product
      // object is being read out of the $order object rather than from
      // the database, and the $order object only carries around a limited
      // number of product properties.
      $temp = Node::load($product->nid->value);
      $product->length = $temp->length->value;
      $product->width = $temp->width->value;
      $product->height = $temp->height->value;
      $product->length_units = $temp->length_units;
      $product->ups['container'] = isset($temp->ups['container']) ? $temp->ups['container'] : 'VARIABLE';
      $packages[$key][0]->price += $product->price * $product->qty;
      $packages[$key][0]->weight += $product->weight * $product->qty * uc_weight_conversion($product->weight_units, 'lb');
    }
    foreach ($packages as $key => $package) {
      $packages[$key][0]->pounds = floor($package[0]->weight);
      $packages[$key][0]->ounces = LB_TO_OZ * ($package[0]->weight - $packages[$key][0]->pounds);
      $packages[$key][0]->container = 'VARIABLE';
      $packages[$key][0]->size = 'REGULAR';

      // Packages are "machinable" if heavier than 6oz. and less than 35lbs.
      $packages[$key][0]->machinable = ($packages[$key][0]->pounds == 0 ? $packages[$key][0]->ounces >= 6 : TRUE) && $packages[$key][0]->pounds <= 35 && ($packages[$key][0]->pounds == 35 ? $packages[$key][0]->ounces == 0 : TRUE);
      $packages[$key][0]->qty = 1;
    }
  }
  else {

    // !$ups_config->get('all_in_one') || count($products) = 1
    // This is the "Each in own" packaging strategy, or
    // there is only one product line item in order.
    foreach ($products as $product) {
      if ($product->nid) {
        $address = uc_quote_get_default_shipping_address($product->nid);
        if (in_array($address, $addresses)) {

          // This is an existing address.
          foreach ($addresses as $index => $value) {
            if ($address == $value) {
              $key = $index;
              break;
            }
          }
        }
        else {

          // This is a new address.
          $addresses[++$last_key] = $address;
          $key = $last_key;
        }
      }
      if (!isset($product->pkg_qty) || !$product->pkg_qty) {
        $product->pkg_qty = 1;
      }
      $num_of_pkgs = (int) ($product->qty / $product->pkg_qty);
      if ($num_of_pkgs) {
        $package = clone $product;
        $package->description = $product->model;
        $weight = $product->weight * $product->pkg_qty;
        switch ($product->weight_units) {
          case 'g':

            // Convert to kg and fall through.
            $weight = $weight * G_TO_KG;
          case 'kg':

            // Convert to lb and fall through.
            $weight = $weight * KG_TO_LB;
          case 'lb':
            $package->pounds = floor($weight);
            $package->ounces = LB_TO_OZ * ($weight - $package->pounds);
            break;
          case 'oz':
            $package->pounds = floor($weight * OZ_TO_LB);
            $package->ounces = $weight - $package->pounds * LB_TO_OZ;
            break;
        }

        // Grab some product properties directly from the (cached) product
        // data. They are not normally available here because the $product
        // object is being read out of the $order object rather than from
        // the database, and the $order object only carries around a limited
        // number of product properties.
        $temp = Node::load($product->nid);
        $product->length = $temp->length;
        $product->width = $temp->width;
        $product->height = $temp->height;
        $product->length_units = $temp->length_units;
        $product->ups['container'] = isset($temp->ups['container']) ? $temp->ups['container'] : 'VARIABLE';
        $package->container = $product->ups['container'];
        $length_conversion = uc_length_conversion($product->length_units, 'in');
        $package->length = max($product->length, $product->width) * $length_conversion;
        $package->width = min($product->length, $product->width) * $length_conversion;
        $package->height = $product->height * $length_conversion;
        if ($package->length < $package->height) {
          list($package->length, $package->height) = [
            $package->height,
            $package->length,
          ];
        }
        $package->girth = 2 * $package->width + 2 * $package->height;
        $package->size = $package->length <= 12 ? 'REGULAR' : 'LARGE';
        $package->machinable = $package->length >= 6 && $package->length <= 34 && $package->width >= 0.25 && $package->width <= 17 && $package->height >= 3.5 && $package->height <= 17 && ($package->pounds == 0 ? $package->ounces >= 6 : TRUE) && $package->pounds <= 35 && ($package->pounds == 35 ? $package->ounces == 0 : TRUE);
        $package->price = $product->price * $product->pkg_qty;
        $package->qty = $num_of_pkgs;
        $packages[$key][] = $package;
      }
      $remaining_qty = $product->qty % $product->pkg_qty;
      if ($remaining_qty) {
        $package = clone $product;
        $package->description = $product->model;
        $weight = $product->weight * $remaining_qty;
        switch ($product->weight_units) {
          case 'g':

            // Convert to kg and fall through.
            $weight = $weight * G_TO_KG;
          case 'kg':

            // Convert to lb and fall through.
            $weight = $weight * KG_TO_LB;
          case 'lb':
            $package->pounds = floor($weight);
            $package->ounces = LB_TO_OZ * ($weight - $package->pounds);
            break;
          case 'oz':
            $package->pounds = floor($weight * OZ_TO_LB);
            $package->ounces = $weight - $package->pounds * LB_TO_OZ;
            break;
        }
        $package->container = $product->ups['container'];
        $length_conversion = uc_length_conversion($product->length_units, 'in');
        $package->length = max($product->length, $product->width) * $length_conversion;
        $package->width = min($product->length, $product->width) * $length_conversion;
        $package->height = $product->height * $length_conversion;
        if ($package->length < $package->height) {
          list($package->length, $package->height) = [
            $package->height,
            $package->length,
          ];
        }
        $package->girth = 2 * $package->width + 2 * $package->height;
        $package->size = $package->length <= 12 ? 'REGULAR' : 'LARGE';
        $package->machinable = $package->length >= 6 && $package->length <= 34 && $package->width >= 0.25 && $package->width <= 17 && $package->height >= 3.5 && $package->height <= 17 && ($package->pounds == 0 ? $package->ounces >= 6 : TRUE) && $package->pounds <= 35 && ($package->pounds == 35 ? $package->ounces == 0 : TRUE);
        $package->price = $product->price * $remaining_qty;
        $package->qty = 1;
        $packages[$key][] = $package;
      }
    }
  }
  return $packages;
}