View source  
  <?php
namespace Drupal\xquantity_stock\EventSubscriber;
use Drupal\commerce\Context;
use Drupal\commerce_order\Event\OrderEvent;
use Drupal\commerce_order\Event\OrderEvents;
use Drupal\commerce_order\Event\OrderItemEvent;
use Drupal\state_machine\Event\WorkflowTransitionEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Session\AccountProxyInterface;
use Drupal\commerce_order\AvailabilityManagerInterface;
use Drupal\commerce_order\AvailabilityCheckerInterface;
use Drupal\commerce_order\Entity\Order;
use Drupal\commerce_order\Entity\OrderItem;
class XquantityStockOrderEventSubscriber implements EventSubscriberInterface {
  
  protected $routeMatch;
  
  protected $currentUser;
  
  public $availabilityManager;
  
  public function __construct(RouteMatchInterface $route_match, AccountProxyInterface $user, AvailabilityCheckerInterface $availability_manager) {
    $this->routeMatch = $route_match;
    $this->currentUser = $user;
    $this->availabilityManager = $availability_manager;
  }
  
  public static function getSubscribedEvents() {
    $events = [
      'commerce_order.cancel.post_transition' => [
        'onOrderCancel',
        -100,
      ],
      OrderEvents::ORDER_PREDELETE => [
        'onOrderDelete',
        -100,
      ],
      OrderEvents::ORDER_ITEM_UPDATE => [
        'onOrderItemUpdate',
        -100,
      ],
      OrderEvents::ORDER_ITEM_DELETE => [
        'onOrderItemDelete',
        -100,
      ],
    ];
    return $events;
  }
  
  public function onOrderCancel(WorkflowTransitionEvent $event) {
    $order = $event
      ->getEntity();
    foreach ($order
      ->getItems() as $order_item) {
      $this
        ->updateStock($order, $order_item, TRUE);
    }
  }
  
  public function onOrderDelete(OrderEvent $event) {
    $order = $event
      ->getOrder();
    foreach ($order
      ->getItems() as $order_item) {
      $this
        ->updateStock($order, $order_item, TRUE);
    }
  }
  
  public function onOrderItemUpdate(OrderItemEvent $event) {
    
    if ($this->routeMatch
      ->getParameter('commerce_order')) {
      if (($order_item = $event
        ->getOrderItem()) && ($order = $order_item
        ->getOrder())) {
        $this
          ->updateStock($order, $order_item);
      }
    }
  }
  
  public function onOrderItemDelete(OrderItemEvent $event) {
    $order_item = $event
      ->getOrderItem();
    
    if ($order = $order_item
      ->getOrder()) {
      $this
        ->updateStock($order, $order_item, TRUE);
    }
  }
  
  protected function updateStock(Order $order, OrderItem $order_item, $delete = FALSE) {
    $state = $order
      ->getState()->value;
    if ($state != 'canceled' && $state != 'completed') {
      $quantity = $delete ? '0' : $order_item
        ->getQuantity();
      $old = $delete ? $order_item
        ->getQuantity() : $order_item->original
        ->getQuantity();
      $context = new Context($this->currentUser, $order
        ->getStore(), time(), [
        'xquantity' => $delete ? 'delete' : 'update',
        'old' => $old,
      ]);
      $result = $this->availabilityManager
        ->check($order_item, $context, $quantity);
      if ($result
        ->isUnavailable()) {
        throw new \InvalidArgumentException("The quantity {$quantity} to update on the {$order_item->getTitle()} order item is not available on the stock.");
      }
    }
  }
}