commerce_shipping-8.x-2.0-rc2/src/EarlyOrderProcessor.php
src/EarlyOrderProcessor.php
<?php namespace Drupal\commerce_shipping; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\commerce_order\Entity\OrderInterface; use Drupal\commerce_order\OrderProcessorInterface; /** * Prepares shipments for the order refresh process. * * Runs before other order processors (promotion, tax, etc). * Packs the shipments, resets their amounts and adjustments. * * Once the other order processors perform their changes, the * LateOrderProcessor transfers the shipment adjustments to the order. * * @see \Drupal\commerce_shipping\LateOrderProcessor */ class EarlyOrderProcessor implements OrderProcessorInterface { /** * The entity type manager. * * @var \Drupal\Core\Entity\EntityTypeManagerInterface */ protected $entityTypeManager; /** * The shipping order manager. * * @var \Drupal\commerce_shipping\ShippingOrderManagerInterface */ protected $shippingOrderManager; /** * The shipment manager. * * @var \Drupal\commerce_shipping\ShipmentManagerInterface */ protected $shipmentManager; /** * Constructs a new EarlyOrderProcessor object. * * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager * The entity type manager. * @param \Drupal\commerce_shipping\ShippingOrderManagerInterface $shipping_order_manager * The shipping order manager. * @param \Drupal\commerce_shipping\ShipmentManagerInterface $shipment_manager * The shipment manager. */ public function __construct(EntityTypeManagerInterface $entity_type_manager, ShippingOrderManagerInterface $shipping_order_manager, ShipmentManagerInterface $shipment_manager) { $this->entityTypeManager = $entity_type_manager; $this->shippingOrderManager = $shipping_order_manager; $this->shipmentManager = $shipment_manager; } /** * {@inheritdoc} */ public function process(OrderInterface $order) { if (!$this->shippingOrderManager->hasShipments($order)) { return; } /** @var \Drupal\commerce_shipping\Entity\ShipmentInterface[] $shipments */ $shipments = $order->get('shipments')->referencedEntities(); if ($shipments && $this->shouldRepack($order, $shipments)) { $shipping_profile = $this->shippingOrderManager->getProfile($order); // If the shipping profile does not exist, delete all shipments. if (!$shipping_profile) { $shipment_storage = $this->entityTypeManager->getStorage('commerce_shipment'); $shipment_storage->delete($shipments); return; } $shipments = $this->shippingOrderManager->pack($order, $shipping_profile); } $should_refresh = $this->shouldRefresh($order); foreach ($shipments as $key => $shipment) { $original_amount = $shipment->getOriginalAmount(); $pre_promotion_amount = $shipment->getData('pre_promotion_amount'); if ($pre_promotion_amount) { if ($original_amount) { $shipment->setAmount($pre_promotion_amount); } else { $shipment->unsetData('pre_promotion_amount'); } } $shipment->clearAdjustments(); if (!$should_refresh) { continue; } $shipment->order_id->entity = $order; $rates = $this->shipmentManager->calculateRates($shipment); // There is no rates for shipping. "clear" the rate... // Note that we don't remove the shipment to prevent data loss (we're // mainly interested in preserving the shipping profile). if (empty($rates)) { $shipment->clearRate(); continue; } $rate = $this->shipmentManager->selectDefaultRate($shipment, $rates); $this->shipmentManager->applyRate($shipment, $rate); } // Unset flag before returning updated shipments. if ($should_refresh) { $order->unsetData(ShippingOrderManagerInterface::FORCE_REFRESH); } $order->set('shipments', $shipments); } /** * Determines whether the given order's shipments should be repacked. * * @param \Drupal\commerce_order\Entity\OrderInterface $order * The order. * @param \Drupal\commerce_shipping\Entity\ShipmentInterface[] $shipments * The shipments. * * @return bool * TRUE if the order should be repacked, FALSE otherwise. */ protected function shouldRepack(OrderInterface $order, array $shipments) { // Skip repacking if there's at least one shipment that was created outside // of the packing process (via the admin UI, for example). foreach ($shipments as $shipment) { if (!$shipment->getData('owned_by_packer')) { return FALSE; } } // Flag used for force repacking shipments and possible recalculation // of rates. if ($this->shouldRefresh($order)) { return TRUE; } // Ideally repacking would happen only if the order items changed. // However, it is not possible to detect order item quantity changes, // because the order items are saved before the order itself. // Therefore, repacking runs on every refresh, but as a minimal // optimization, this processor ignores refreshes caused by moving // through checkout, unless an order item was added/removed along the way. if (isset($order->original) && $order->hasField('checkout_step')) { $previous_step = $order->original->get('checkout_step')->value; $current_step = $order->get('checkout_step')->value; $previous_order_item_ids = array_map(function ($value) { return $value['target_id']; }, $order->original->get('order_items')->getValue()); $current_order_item_ids = array_map(function ($value) { return $value['target_id']; }, $order->get('order_items')->getValue()); if ($previous_step != $current_step && $previous_order_item_ids == $current_order_item_ids) { return FALSE; } } return TRUE; } /** * Determines whether the order needs to be repacked and/or whether the * shipping rates should be recalculated. * * @param \Drupal\commerce_order\Entity\OrderInterface $order * The order. * * @return bool * TRUE if it should refresh, FALSE otherwise. */ protected function shouldRefresh(OrderInterface $order) { return (bool) $order->getData(ShippingOrderManagerInterface::FORCE_REFRESH, FALSE); } }