commerce_inventory-8.x-1.0-alpha6/modules/commerce_inventory_order/src/EventSubscriber/OrderEventSubscriber.php
modules/commerce_inventory_order/src/EventSubscriber/OrderEventSubscriber.php
<?php
namespace Drupal\commerce_inventory_order\EventSubscriber;
use Drupal\commerce_inventory_order\InventoryOrderManager;
use Drupal\commerce_order\Event\OrderEvents;
use Drupal\commerce_order\Event\OrderItemEvent;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\state_machine\Event\WorkflowTransitionEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Listen to Commerce Order events.
*/
class OrderEventSubscriber implements EventSubscriberInterface {
/**
* The Order inventory manager.
*
* @var \Drupal\commerce_inventory_order\InventoryOrderManager
*/
protected $manager;
/**
* The Commerce Log entity storage.
*
* @var \Drupal\commerce_log\LogStorageInterface
*/
protected $logStorage;
/**
* Constructs a new OrderEventSubscriber object.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
* @param \Drupal\commerce_inventory_order\InventoryOrderManager $inventory_order_manager
* The Order Inventory manager.
*/
public function __construct(EntityTypeManagerInterface $entity_type_manager, InventoryOrderManager $inventory_order_manager) {
$this->logStorage = $entity_type_manager->getStorage('commerce_log');
$this->manager = $inventory_order_manager;
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
// Make all workflow transitions point to the catch-all subscriber method.
// @todo load all possible transactions dynamically.
// Dynamic transition loading currently errors on Drush cache clear stating
// that the container isn't set, which is what is needed to load the
// "plugin.manager.workflow" service. Until then, custom modules with
// different Order states will only need to write a custom subscriber that
// mimics ::transitionOrder to apply the relevant adjustment state.
$events['commerce_order.cancel.pre_transition'] = 'preTransitionOrder';
$events['commerce_order.fulfill.pre_transition'] = 'preTransitionOrder';
$events['commerce_order.place.pre_transition'] = 'preTransitionOrder';
$events['commerce_order.validate.pre_transition'] = 'preTransitionOrder';
$events['commerce_order.cancel.post_transition'] = 'postTransitionOrder';
$events['commerce_order.fulfill.post_transition'] = 'postTransitionOrder';
$events['commerce_order.place.post_transition'] = 'postTransitionOrder';
$events['commerce_order.validate.post_transition'] = 'postTransitionOrder';
// Track Order Item Updates.
// $events[OrderEvents::ORDER_ITEM_PREDELETE] = 'preDeleteOrderItem';.
$events[OrderEvents::ORDER_ITEM_PRESAVE] = 'preSaveOrderItem';
$events[OrderEvents::ORDER_ITEM_UPDATE] = 'postSaveOrderItem';
return $events;
}
/**
* Handle inventory adjustments on Order pre-transitions.
*
* @param \Drupal\state_machine\Event\WorkflowTransitionEvent $event
* The workflow transition event.
* @param string $event_name
* The name of the event.
*/
public function preTransitionOrder(WorkflowTransitionEvent $event, $event_name) {
/** @var \Drupal\commerce_order\Entity\OrderInterface $order */
$order = $event->getEntity();
// Get current transition from event name.
$order_transition_id = explode('.', $event_name)[1];
// Get all available order item transitions.
$transitions = $this->manager->getAllBundleInventoryWorkflowTransitions();
// Exit early if this transition isn't tracked under any Order Item bundle.
if (!array_key_exists($order_transition_id, $transitions)) {
return;
}
// Track Order Item fallback state for.
$states = [];
foreach ($transitions[$order_transition_id] as $item_bundle_id => $item_data) {
$states[$item_bundle_id] = $item_data['state'];
}
$order->setData('item_inventory_adjustment_states', $states);
}
/**
* Handle inventory adjustments on Order post-transitions.
*
* @param \Drupal\state_machine\Event\WorkflowTransitionEvent $event
* The workflow transition event.
* @param string $event_name
* The name of the event.
*/
public function postTransitionOrder(WorkflowTransitionEvent $event, $event_name) {
/** @var \Drupal\commerce_order\Entity\OrderInterface $order */
$order = $event->getEntity();
// Get current transition from event name.
$order_transition_id = explode('.', $event_name)[1];
// Get all available order item transitions.
$transitions = $this->manager->getAllBundleInventoryWorkflowTransitions();
// Exit early if this transition isn't tracked under any Order Item bundle.
if (!array_key_exists($order_transition_id, $transitions)) {
return;
}
// Run through each Order Item to adjust the inventory workflow state.
foreach ($order->getItems() as $order_item) {
$order_item_transition = NestedArray::getValue($transitions, [
$order_transition_id,
$order_item->bundle(),
'transition',
]);
// Exit early if the order transition shouldn't update.
if (is_null($order_item_transition)) {
continue;
}
// Transition the Order Item inventory workflow state.
if ($this->manager::transitionAdjustmentState($order_item, $order_item_transition)) {
$order_item->save();
}
}
}
/**
* Handle Order Item pre-deletions.
*
* @param \Drupal\commerce_order\Event\OrderItemEvent $event
* The Order Item event.
*/
public function preDeleteOrderItem(OrderItemEvent $event) {
$order_item = $event->getOrderItem();
// Exit early if Order isn't set.
if (is_null($order_item->getOrder())) {
return;
}
$this->manager->deleteInventory($order_item);
}
/**
* Handle Order Item pre-saves.
*
* @param \Drupal\commerce_order\Event\OrderItemEvent $event
* The Order Item event.
*/
public function preSaveOrderItem(OrderItemEvent $event) {
$order_item = $event->getOrderItem();
// Exit early if Order isn't set.
if (is_null($order_item->getOrderId()) || is_null($order_item->id())) {
return;
}
// Make un-tracked workflow adjustments.
if ($this->manager::checkAdjustmentState($order_item, 'untracked')) {
$this->manager->cancelInventoryHolds($order_item);
}
// Make available workflow adjustments.
if ($this->manager::checkAdjustmentState($order_item, 'available')) {
$this->manager->makeInventoryHolds($order_item);
}
// Make on-hand workflow adjustments.
if ($this->manager::checkAdjustmentState($order_item, 'on_hand')) {
$this->manager->makeInventoryHolds($order_item);
$this->manager->convertInventoryHolds($order_item);
}
}
/**
* Handle Order Item post-saves.
*
* @param \Drupal\commerce_order\Event\OrderItemEvent $event
* The Order Item event.
*/
public function postSaveOrderItem(OrderItemEvent $event) {
$order_item = $event->getOrderItem();
// Exit early if Order isn't set.
if (is_null($order_item->getOrderId()) || is_null($order_item->id())) {
return;
}
if (!$order_item->get('inventory_adjustment_holds')->isEmpty()) {
foreach ($order_item->get('inventory_adjustment_holds') as $hold) {
// Add to logs.
$this->logStorage->generate($hold->entity, 'inventory_item_adjustment_available', [
'order_id' => $order_item->getOrderId(),
'quantity' => $hold->quantity,
])->save();
$this->logStorage->generate($order_item->getOrder(), 'order_item_inventory_adjustment_available', [
'purchasable_entity_label' => $order_item->getPurchasedEntity()->label(),
'quantity' => $hold->quantity,
'inventory_location_label' => $hold->entity->getLocation()->label(),
])->save();
}
}
}
}
