diff --git a/vendor/magento/module-inventory-sales/Observer/SalesInventory/DeductSourceItemQuantityOnRefundObserver.php b/vendor/magento/module-inventory-sales/Observer/SalesInventory/DeductSourceItemQuantityOnRefundObserver.php
deleted file mode 100644
index f080682019b..00000000000
--- a/vendor/magento/module-inventory-sales/Observer/SalesInventory/DeductSourceItemQuantityOnRefundObserver.php
+++ /dev/null
@@ -1,144 +0,0 @@
-getSkuFromOrderItem = $getSkuFromOrderItem;
- $this->itemsToRefundFactory = $itemsToRefundFactory;
- $this->isSourceItemManagementAllowedForProductType = $isSourceItemManagementAllowedForProductType;
- $this->getProductTypesBySkus = $getProductTypesBySkus;
- $this->deductSourceItemQuantityOnRefund = $deductSourceItemQuantityOnRefund;
- $this->orderRepository = $orderRepository;
- }
-
- /**
- * @param Observer $observer
- * @return void
- */
- public function execute(Observer $observer)
- {
- /* @var $creditmemo \Magento\Sales\Model\Order\Creditmemo */
- $creditmemo = $observer->getEvent()->getCreditmemo();
- $order = $this->orderRepository->get($creditmemo->getOrderId());
- $itemsToRefund = $refundedOrderItemIds = [];
- /** @var CreditmemoItem $item */
- foreach ($creditmemo->getItems() as $item) {
- /** @var OrderItemInterface $orderItem */
- $orderItem = $item->getOrderItem();
- $sku = $this->getSkuFromOrderItem->execute($orderItem);
-
- if ($this->isValidItem($sku, $item)) {
- $refundedOrderItemIds[] = $item->getOrderItemId();
- $qty = (float)$item->getQty();
- $processedQty = $orderItem->getQtyInvoiced() - $orderItem->getQtyRefunded() + $qty;
- $itemsToRefund[$sku] = [
- 'qty' => ($itemsToRefund[$sku]['qty'] ?? 0) + $qty,
- 'processedQty' => ($itemsToRefund[$sku]['processedQty'] ?? 0) + (float)$processedQty
- ];
- }
- }
-
- $itemsToDeductFromSource = [];
- foreach ($itemsToRefund as $sku => $data) {
- $itemsToDeductFromSource[] = $this->itemsToRefundFactory->create([
- 'sku' => $sku,
- 'qty' => $data['qty'],
- 'processedQty' => $data['processedQty']
- ]);
- }
-
- if (!empty($itemsToDeductFromSource)) {
- $this->deductSourceItemQuantityOnRefund->execute(
- $order,
- $itemsToDeductFromSource,
- $refundedOrderItemIds
- );
- }
- }
-
- /**
- * @param string $sku
- * @param CreditmemoItem $item
- * @return bool
- */
- private function isValidItem(string $sku, CreditmemoItem $item): bool
- {
- /** @var OrderItemInterface $orderItem */
- $orderItem = $item->getOrderItem();
- // Since simple products which are the part of a grouped product are saved in the database
- // (table sales_order_item) with product type grouped, we manually change the type of
- // product from grouped to simple which support source management.
- $typeId = $orderItem->getProductType() === 'grouped' ? 'simple' : $orderItem->getProductType();
-
- $productType = $typeId ?: $this->getProductTypesBySkus->execute(
- [$sku]
- )[$sku];
-
- return $this->isSourceItemManagementAllowedForProductType->execute($productType)
- && $item->getQty() > 0
- && !$item->getBackToStock();
- }
-}
diff --git a/vendor/magento/module-inventory-sales/etc/di.xml b/vendor/magento/module-inventory-sales/etc/di.xml
index b94a1e61023..19faa0c3719 100644
--- a/vendor/magento/module-inventory-sales/etc/di.xml
+++ b/vendor/magento/module-inventory-sales/etc/di.xml
@@ -169,6 +169,9 @@
+
+
+
diff --git a/vendor/magento/module-inventory-sales/etc/events.xml b/vendor/magento/module-inventory-sales/etc/events.xml
index 63cfbdac442..568e90310f8 100644
--- a/vendor/magento/module-inventory-sales/etc/events.xml
+++ b/vendor/magento/module-inventory-sales/etc/events.xml
@@ -12,10 +12,6 @@
-
-
-
-
diff --git a/vendor/magento/module-inventory-sales/Plugin/Sales/OrderManagement/DeductSourceItemQuantityOnRefundPlugin.php b/vendor/magento/module-inventory-sales/Plugin/Sales/OrderManagement/DeductSourceItemQuantityOnRefundPlugin.php
new file mode 100644
index 00000000000..54e5d95dae5
--- /dev/null
+++ b/vendor/magento/module-inventory-sales/Plugin/Sales/OrderManagement/DeductSourceItemQuantityOnRefundPlugin.php
@@ -0,0 +1,189 @@
+getSkuFromOrderItem = $getSkuFromOrderItem;
+ $this->itemsToRefundFactory = $itemsToRefundFactory;
+ $this->isSourceItemManagementAllowedForProductType = $isSourceItemManagementAllowedForProductType;
+ $this->getProductTypesBySkus = $getProductTypesBySkus;
+ $this->deductSourceItemQuantityOnRefund = $deductSourceItemQuantityOnRefund;
+ $this->orderRepository = $orderRepository;
+ $this->productResource = $productResource;
+ }
+
+ /**
+ * On Credit Memo create, issues the reservation compensation record.
+ *
+ * Before saving the credit memo, validates if the credit memo object was created or updated.
+ * If the credit memo was updates, we should not compensate the reservation.
+ *
+ * @param CreditmemoRepositoryInterface $subject
+ * @param callable $proceed
+ * @param CreditmemoInterface $entity
+ * @return mixed
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ */
+ public function aroundSave(
+ CreditmemoRepositoryInterface $subject,
+ callable $proceed,
+ CreditmemoInterface $entity
+ ) {
+ $isNewCreditMemo = !(bool)$entity->getEntityId();
+ $result = $proceed($entity);
+
+ if ($isNewCreditMemo) {
+ $this->compensateReservation($entity);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Compensate reservation for creditmemo item
+ *
+ * @param CreditmemoInterface $creditMemo
+ * @return void
+ */
+ private function compensateReservation(CreditmemoInterface $creditMemo): void
+ {
+ $order = $this->orderRepository->get($creditMemo->getOrderId());
+ $itemsToRefund = $refundedOrderItemIds = [];
+ $productSkus = [];
+ /** @var CreditmemoItem $item */
+ foreach ($creditMemo->getItems() as $item) {
+ /** @var OrderItemInterface $orderItem */
+ $orderItem = $item->getOrderItem();
+ $sku = $this->getSkuFromOrderItem->execute($orderItem);
+ $productSkus[] = $sku;
+
+ if ($this->isValidItem($sku, $item)) {
+ $refundedOrderItemIds[] = $item->getOrderItemId();
+ $qty = (float)$item->getQty();
+ $processedQty = $orderItem->getQtyInvoiced() - $orderItem->getQtyRefunded() + $qty;
+ $itemsToRefund[$sku] = [
+ 'qty' => ($itemsToRefund[$sku]['qty'] ?? 0) + $qty,
+ 'processedQty' => ($itemsToRefund[$sku]['processedQty'] ?? 0) + (float)$processedQty
+ ];
+ }
+ }
+
+ $existingProductIdsBySkus = $this->productResource->getProductsIdsBySkus($productSkus);
+
+ $itemsToDeductFromSource = [];
+ foreach ($itemsToRefund as $sku => $data) {
+ if (array_key_exists($sku, $existingProductIdsBySkus)) {
+ $itemsToDeductFromSource[] = $this->itemsToRefundFactory->create([
+ 'sku' => $sku,
+ 'qty' => $data['qty'],
+ 'processedQty' => $data['processedQty']
+ ]);
+ }
+ }
+
+ if (!empty($itemsToDeductFromSource)) {
+ $this->deductSourceItemQuantityOnRefund->execute(
+ $order,
+ $itemsToDeductFromSource,
+ $refundedOrderItemIds
+ );
+ }
+ }
+
+ /**
+ * Validate if the compensation should be processed
+ *
+ * @param string $sku
+ * @param CreditmemoItem $item
+ * @return bool
+ */
+ private function isValidItem(string $sku, CreditmemoItem $item): bool
+ {
+ /** @var OrderItemInterface $orderItem */
+ $orderItem = $item->getOrderItem();
+ // Since simple products which are the part of a grouped product are saved in the database
+ // (table sales_order_item) with product type grouped, we manually change the type of
+ // product from grouped to simple which support source management.
+ $typeId = $orderItem->getProductType() === 'grouped' ? 'simple' : $orderItem->getProductType();
+
+ $productType = $typeId ?: $this->getProductTypesBySkus->execute(
+ [$sku]
+ )[$sku];
+
+ return $this->isSourceItemManagementAllowedForProductType->execute($productType)
+ && $item->getQty() > 0
+ && !$item->getBackToStock()
+ && !$orderItem->getIsVirtual();
+ }
+}