You are here

function uc_product_kit_node_update in Ubercart 8.4

Same name and namespace in other branches
  1. 7.3 uc_product_kit/uc_product_kit.module \uc_product_kit_node_update()

Implements hook_node_update().

Updates information in {uc_products} as well as {uc_product_kits}. Because component products are known when the form is loaded, discount information can be input and saved.

Also ensures product kit discounts are updated if their component nodes are updated or deleted.

See also

uc_product_node_update()

File

uc_product_kit/uc_product_kit.module, line 243
The product kit module for Ubercart.

Code

function uc_product_kit_node_update($node) {
  $connection = \Drupal::database();

  // Recalculate kit discounts that include the updated node.
  if (uc_product_is_product($node)) {
    $result = $connection
      ->query('SELECT DISTINCT nid FROM {uc_product_kits} WHERE product_id = :nid', [
      ':nid' => $node
        ->id(),
    ]);
    while ($nid = $result
      ->fetchField()) {
      $kit = Node::load($nid);
      uc_product_kit_node_update($kit);
    }
    return;
  }
  if ($node
    ->getType() != 'product_kit') {
    return;
  }
  $obj = new \stdClass();
  $obj->vid = $node
    ->getRevisionId();
  $obj->nid = $node
    ->id();
  $obj->model = '';
  $obj->cost = 0;
  $obj->price = 0;
  $obj->weight = 0;
  $obj->weight_units = \Drupal::config('uc_store.settings')
    ->get('weight.units');
  $obj->default_qty = $node->default_qty->value;
  $obj->shippable = FALSE;
  if (!isset($node->kit_total) && isset($node->synchronized) && isset($node->price)) {
    $override_discounts = !$node->synchronized;
    $node->kit_total = $node->price->value;
  }
  else {
    $override_discounts = isset($node->kit_total) && is_numeric($node->kit_total);
  }

  // Get the price of all the products without any discounts. This number is
  // used if a total kit price was specified to calculate the individual
  // product discounts.
  if ($override_discounts) {
    $base_price = 0;
    foreach ($node->products as $nid) {

      // Usually, $node is $form_state->getValues() cast as an object.
      // However, there could be times where node_save() is called with an
      // actual product kit node. $node->products is an array of objects and
      // $node->items doesn't exist then.
      if (is_numeric($nid)) {
        $product = Node::load($nid);
        if (!isset($node->items[$nid]['qty']) || $node->items[$nid]['qty'] === '') {
          $node->items[$nid]['qty'] = 1;
        }
      }
      else {
        $product = $nid;
        $nid = $product
          ->id();
        $node->items[$nid]['qty'] = $product->qty;
        $node->items[$nid]['discount'] = $product->discount;
        $node->items[$nid]['ordering'] = $product->ordering;
      }
      $base_price += $product->price->value * $node->items[$nid]['qty'];
    }
  }
  if (!$node
    ->isNewRevision()) {
    $connection
      ->delete('uc_product_kits')
      ->condition('vid', $node
      ->getRevisionId())
      ->execute();
  }
  foreach ($node->products as $nid) {
    if (is_numeric($nid)) {
      $product = Node::load($nid);
    }
    else {
      $product = $nid;
      $nid = $product
        ->id();
    }

    // When a total kit price is specified, calculate the individual product
    // discounts needed to reach it, taking into account the product quantities
    // and their relative prices. More expensive products should be given a
    // proportionally higher discount.
    if ($override_discounts) {

      // After all the algebra that went into finding this formula, it's
      // surprising how simple it is.
      $discount = ($node->kit_total - $base_price) * $product->price->value / $base_price;
    }
    elseif (isset($node->items[$nid]['discount'])) {
      $discount = (double) $node->items[$nid]['discount'];
    }
    elseif (isset($node->products[$nid]->discount)) {
      $discount = $node->products[$nid]->discount;
    }
    else {
      $discount = 0;
    }
    if (isset($node->items)) {
      if (!isset($node->items[$nid]['qty']) || $node->items[$nid]['qty'] === '') {
        $node->items[$nid]['qty'] = 1;
      }
      $product->qty = $node->items[$nid]['qty'];
      $product->ordering = isset($node->items[$nid]['ordering']) ? $node->items[$nid]['ordering'] : 0;
    }
    else {
      $product->qty = $node->products[$nid]->qty;
      $product->ordering = $node->products[$nid]->ordering;
    }

    // Discounts are always saved, but they are only applied if the kit can't
    // be changed by the customer.
    if ($node->mutable != UC_PRODUCT_KIT_MUTABLE) {
      $product->price->value += $discount;
    }
    $obj->model .= $product->model->value . ' / ';
    $obj->cost += $product->cost->value * $product->qty;
    $obj->price += $product->price->value * $product->qty;
    $obj->weight += $product->weight->value * $product->qty * uc_weight_conversion($product->weight->units, $obj->weight_units);
    if ($product->shippable->value) {
      $obj->shippable = TRUE;
    }
    $connection
      ->insert('uc_product_kits')
      ->fields([
      'vid' => $node
        ->getRevisionId(),
      'nid' => $node
        ->id(),
      'product_id' => $nid,
      'mutable' => $node->mutable,
      'qty' => $product->qty,
      'discount' => $discount,
      'ordering' => $product->ordering,
      'synchronized' => $override_discounts ? 0 : 1,
    ])
      ->execute();
  }
  $obj->model = rtrim($obj->model, ' / ');
  if ($node->mutable == UC_PRODUCT_KIT_MUTABLE && !empty($discount)) {
    \Drupal::messenger()
      ->addMessage(t('Product kit discounts are not applied because the customer can remove components from their cart.'));
  }
  $connection
    ->merge('uc_products')
    ->key([
    'vid' => $obj->vid,
  ])
    ->fields([
    'model' => $obj->model,
    'cost' => $obj->cost,
    'price' => $obj->price,
    'weight' => $obj->weight,
    'weight_units' => $obj->weight_units,
    'default_qty' => $obj->default_qty,
    'shippable' => $obj->shippable ? 1 : 0,
  ])
    ->execute();

  // When a kit is updated, remove matching kits from the cart, as there is no
  // simple way to handle product addition or removal at this point.
  if (\Drupal::moduleHandler()
    ->moduleExists('uc_cart')) {
    $connection
      ->delete('uc_cart_products')
      ->condition('data', '%' . $connection
      ->escapeLike('s:6:"kit_id";s:' . strlen($node
      ->id()) . ':"' . $node
      ->id() . '";') . '%', 'LIKE')
      ->execute();
  }
}