You are here

class OrderRefresh in Commerce Core 8.2

Default implementation for order refresh.

Hierarchy

Expanded class hierarchy of OrderRefresh

1 file declares its use of OrderRefresh
OrderRefreshTest.php in modules/order/tests/src/Kernel/OrderRefreshTest.php
1 string reference to 'OrderRefresh'
commerce_order.services.yml in modules/order/commerce_order.services.yml
modules/order/commerce_order.services.yml
1 service uses OrderRefresh
commerce_order.order_refresh in modules/order/commerce_order.services.yml
Drupal\commerce_order\OrderRefresh

File

modules/order/src/OrderRefresh.php, line 17

Namespace

Drupal\commerce_order
View source
class OrderRefresh implements OrderRefreshInterface {

  /**
   * The order type storage.
   *
   * @var \Drupal\Core\Entity\EntityStorageInterface
   */
  protected $orderTypeStorage;

  /**
   * The chain price resolver.
   *
   * @var \Drupal\commerce_price\Resolver\ChainPriceResolverInterface
   */
  protected $chainPriceResolver;

  /**
   * The current user.
   *
   * @var \Drupal\Core\Session\AccountProxyInterface
   */
  protected $currentUser;

  /**
   * The time.
   *
   * @var \Drupal\Component\Datetime\TimeInterface
   */
  protected $time;

  /**
   * The order preprocessors.
   *
   * @var \Drupal\commerce_order\OrderPreProcessorInterface[]
   */
  protected $preprocessors = [];

  /**
   * The order processors.
   *
   * @var \Drupal\commerce_order\OrderProcessorInterface[]
   */
  protected $processors = [];

  /**
   * Constructs a new OrderRefresh object.
   *
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   The entity type manager.
   * @param \Drupal\commerce_price\Resolver\ChainPriceResolverInterface $chain_price_resolver
   *   The chain price resolver.
   * @param \Drupal\Core\Session\AccountInterface $current_user
   *   The current user.
   * @param \Drupal\Component\Datetime\TimeInterface $time
   *   The time.
   */
  public function __construct(EntityTypeManagerInterface $entity_type_manager, ChainPriceResolverInterface $chain_price_resolver, AccountInterface $current_user, TimeInterface $time) {
    $this->orderTypeStorage = $entity_type_manager
      ->getStorage('commerce_order_type');
    $this->chainPriceResolver = $chain_price_resolver;
    $this->currentUser = $current_user;
    $this->time = $time;
  }

  /**
   * {@inheritdoc}
   */
  public function addPreprocessor(OrderPreprocessorInterface $processor) {
    $this->preprocessors[] = $processor;
  }

  /**
   * {@inheritdoc}
   */
  public function addProcessor(OrderProcessorInterface $processor) {
    $this->processors[] = $processor;
  }

  /**
   * {@inheritdoc}
   */
  public function shouldRefresh(OrderInterface $order) {
    if (!$this
      ->needsRefresh($order)) {
      return FALSE;
    }

    /** @var \Drupal\commerce_order\Entity\OrderTypeInterface $order_type */
    $order_type = $this->orderTypeStorage
      ->load($order
      ->bundle());

    // Ensure the order is only refreshed for its customer, when configured so.
    if ($order_type
      ->getRefreshMode() == OrderType::REFRESH_CUSTOMER) {
      if ($order
        ->getCustomerId() != $this->currentUser
        ->id()) {
        return FALSE;
      }
    }
    return TRUE;
  }

  /**
   * {@inheritdoc}
   */
  public function needsRefresh(OrderInterface $order) {

    // Only draft orders should be automatically refreshed.
    if ($order
      ->getState()
      ->getId() != 'draft') {
      return FALSE;
    }

    // Only unlocked orders should be automatically refreshed.
    if ($order
      ->isLocked()) {
      return FALSE;
    }

    // Accommodate long-running processes by always using the current time.
    $current_time = $this->time
      ->getCurrentTime();
    $order_time = $order
      ->getChangedTime();
    if (date('Y-m-d', $current_time) != date('Y-m-d', $order_time)) {

      // Refresh on a date change regardless of the refresh frequency.
      // Date changes can impact tax rate amounts, availability of promotions.
      return TRUE;
    }

    /** @var \Drupal\commerce_order\Entity\OrderTypeInterface $order_type */
    $order_type = $this->orderTypeStorage
      ->load($order
      ->bundle());
    $refreshed_ago = $current_time - $order_time;
    if ($refreshed_ago >= $order_type
      ->getRefreshFrequency()) {
      return TRUE;
    }
    return FALSE;
  }

  /**
   * {@inheritdoc}
   */
  public function refresh(OrderInterface $order) {

    // First invoke order preprocessors if any.
    foreach ($this->preprocessors as $processor) {
      $processor
        ->preprocess($order);
    }
    $current_time = $this->time
      ->getCurrentTime();
    $order
      ->setChangedTime($current_time);
    $order
      ->clearAdjustments();

    // Nothing else can be done while the order is empty.
    if (!$order
      ->hasItems()) {
      return;
    }
    $time = $order
      ->getCalculationDate()
      ->format('U');
    $context = new Context($order
      ->getCustomer(), $order
      ->getStore(), $time);
    foreach ($order
      ->getItems() as $order_item) {
      $purchased_entity = $order_item
        ->getPurchasedEntity();
      if ($purchased_entity) {
        $order_item
          ->setTitle($purchased_entity
          ->getOrderItemTitle());
        if (!$order_item
          ->isUnitPriceOverridden()) {
          $unit_price = $this->chainPriceResolver
            ->resolve($purchased_entity, $order_item
            ->getQuantity(), $context);
          $order_item
            ->setUnitPrice($unit_price);
        }
      }

      // If the order refresh is running during order preSave(),
      // $order_item->getOrder() will point to the original order (or
      // NULL, in case the order item is new).
      $order_item->order_id->entity = $order;
    }

    // Allow the processors to modify the order and its items.
    foreach ($this->processors as $processor) {
      $processor
        ->process($order);
      if (!$order
        ->hasItems()) {
        return;
      }
    }
    foreach ($order
      ->getItems() as $order_item) {
      if ($order_item
        ->hasTranslationChanges()) {

        // Remove order items which had their quantities set to 0.
        if (Calculator::compare($order_item
          ->getQuantity(), '0') === 0) {
          $order
            ->removeItem($order_item);
          $order_item
            ->delete();
        }
        else {

          // Remove the order that was set above, to avoid
          // crashes during the entity save process.
          $order_item->order_id->entity = NULL;
          $order_item
            ->setChangedTime($current_time);
          $order_item
            ->save();
        }
      }
    }
  }

}

Members

Namesort descending Modifiers Type Description Overrides
OrderRefresh::$chainPriceResolver protected property The chain price resolver.
OrderRefresh::$currentUser protected property The current user.
OrderRefresh::$orderTypeStorage protected property The order type storage.
OrderRefresh::$preprocessors protected property The order preprocessors.
OrderRefresh::$processors protected property The order processors.
OrderRefresh::$time protected property The time.
OrderRefresh::addPreprocessor public function Adds an order preprocessor. Overrides OrderRefreshInterface::addPreprocessor
OrderRefresh::addProcessor public function Adds an order processor. Overrides OrderRefreshInterface::addProcessor
OrderRefresh::needsRefresh public function Checks whether the given order needs to be refreshed. Overrides OrderRefreshInterface::needsRefresh
OrderRefresh::refresh public function Refreshes the given order. Overrides OrderRefreshInterface::refresh
OrderRefresh::shouldRefresh public function Checks whether the order should be refreshed. Overrides OrderRefreshInterface::shouldRefresh
OrderRefresh::__construct public function Constructs a new OrderRefresh object.