-
Notifications
You must be signed in to change notification settings - Fork 64
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #82 from AmpersandHQ/8-creditmemo-back-to-stock-am…
…persandcopy Back in stock, when credit memo creation occours
- Loading branch information
Showing
5 changed files
with
358 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
255 changes: 255 additions & 0 deletions
255
src/Observer/RestoreSourceItemQuantityOnRefundObserver.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,255 @@ | ||
<?php | ||
|
||
namespace Ampersand\DisableStockReservation\Observer; | ||
|
||
use Ampersand\DisableStockReservation\ReturnProcessor\GetSalesChannelForOrder; | ||
use Ampersand\DisableStockReservation\ReturnProcessor\GetSalesChannelForOrderFactory; | ||
use Magento\Framework\Event\Observer; | ||
use Magento\Framework\Event\ObserverInterface; | ||
use Magento\Framework\Exception\LocalizedException; | ||
use Magento\InventoryApi\Api\GetSourceItemsBySkuInterface; | ||
use Magento\InventoryApi\Api\GetSourcesAssignedToStockOrderedByPriorityInterface; | ||
use Magento\InventoryCatalogApi\Api\DefaultSourceProviderInterface; | ||
use Magento\InventoryCatalogApi\Model\GetProductTypesBySkusInterface; | ||
use Magento\InventoryConfigurationApi\Model\IsSourceItemManagementAllowedForProductTypeInterface; | ||
use Magento\InventorySalesApi\Api\Data\SalesEventExtensionFactory; | ||
use Magento\InventorySalesApi\Api\Data\SalesEventExtensionInterface; | ||
use Magento\InventorySalesApi\Api\Data\SalesEventInterface; | ||
use Magento\InventorySalesApi\Api\Data\SalesEventInterfaceFactory; | ||
use Magento\InventorySalesApi\Model\GetSkuFromOrderItemInterface; | ||
use Magento\InventorySalesApi\Model\StockByWebsiteIdResolverInterface; | ||
use Magento\InventorySourceDeductionApi\Model\ItemToDeductFactory; | ||
use Magento\InventorySourceDeductionApi\Model\SourceDeductionRequestFactory; | ||
use Magento\InventorySourceDeductionApi\Model\SourceDeductionService; | ||
use Magento\Sales\Model\OrderRepository; | ||
|
||
class RestoreSourceItemQuantityOnRefundObserver implements ObserverInterface | ||
{ | ||
/** | ||
* @var GetSkuFromOrderItemInterface | ||
*/ | ||
private $getSkuFromOrderItem; | ||
|
||
/** | ||
* @var IsSourceItemManagementAllowedForProductTypeInterface | ||
*/ | ||
private $isSourceItemManagementAllowedForProductType; | ||
|
||
/** | ||
* @var GetProductTypesBySkusInterface | ||
*/ | ||
private $getProductTypesBySkus; | ||
|
||
/** | ||
* @var OrderRepository | ||
*/ | ||
private $orderRepository; | ||
|
||
/** | ||
* @var DefaultSourceProviderInterface | ||
*/ | ||
private $defaultSourceProvider; | ||
|
||
|
||
/** | ||
* @var GetSourcesAssignedToStockOrderedByPriorityInterface | ||
*/ | ||
private $getSourcesAssignedToStockOrderedByPriority; | ||
|
||
/** | ||
* @var StockByWebsiteIdResolverInterface | ||
*/ | ||
private $stockByWebsiteIdResolver; | ||
|
||
|
||
/** | ||
* @var SourceDeductionRequestFactory | ||
*/ | ||
private $sourceDeductionRequestFactory; | ||
|
||
/** | ||
* @var SalesEventExtensionFactory; | ||
*/ | ||
private $salesEventExtensionFactory; | ||
|
||
/** | ||
* @var GetSalesChannelForOrder|\Magento\InventorySales\Model\ReturnProcessor\GetSalesChannelForOrder | ||
*/ | ||
private $getSalesChannelForOrder; | ||
|
||
/** | ||
* @var SourceDeductionService | ||
*/ | ||
private $sourceDeductionService; | ||
|
||
|
||
/** | ||
* @var GetSourceItemsBySkuInterface | ||
*/ | ||
private $getSourceItemsBySku; | ||
|
||
/** | ||
* @var SalesEventInterfaceFactory | ||
*/ | ||
private $salesEventFactory; | ||
|
||
/** | ||
* @var ItemToDeductFactory | ||
*/ | ||
private $itemToDeductFactory; | ||
|
||
/** | ||
* RestoreSourceItemQuantityOnRefundObserver constructor. | ||
* | ||
* @param GetSkuFromOrderItemInterface $getSkuFromOrderItem | ||
* @param IsSourceItemManagementAllowedForProductTypeInterface $isSourceItemManagementAllowedForProductType | ||
* @param GetProductTypesBySkusInterface $getProductTypesBySkus | ||
* @param OrderRepository $orderRepository | ||
* @param DefaultSourceProviderInterface $defaultSourceProvider | ||
* @param GetSourcesAssignedToStockOrderedByPriorityInterface $getSourcesAssignedToStockOrderedByPriority | ||
* @param StockByWebsiteIdResolverInterface $stockByWebsiteIdResolver | ||
* @param SourceDeductionRequestFactory $sourceDeductionRequestFactory | ||
* @param SalesEventExtensionFactory $salesEventExtensionFactory | ||
* @param GetSalesChannelForOrderFactory $getSalesChannelForOrderFactory | ||
* @param SourceDeductionService $sourceDeductionService | ||
* @param GetSourceItemsBySkuInterface $getSourceItemsBySku | ||
* @param SalesEventInterfaceFactory $salesEventFactory | ||
* @param ItemToDeductFactory $itemToDeductFactory | ||
*/ | ||
public function __construct( | ||
GetSkuFromOrderItemInterface $getSkuFromOrderItem, | ||
IsSourceItemManagementAllowedForProductTypeInterface $isSourceItemManagementAllowedForProductType, | ||
GetProductTypesBySkusInterface $getProductTypesBySkus, | ||
OrderRepository $orderRepository, | ||
DefaultSourceProviderInterface $defaultSourceProvider, | ||
GetSourcesAssignedToStockOrderedByPriorityInterface $getSourcesAssignedToStockOrderedByPriority, | ||
StockByWebsiteIdResolverInterface $stockByWebsiteIdResolver, | ||
SourceDeductionRequestFactory $sourceDeductionRequestFactory, | ||
SalesEventExtensionFactory $salesEventExtensionFactory, | ||
GetSalesChannelForOrderFactory $getSalesChannelForOrderFactory, | ||
SourceDeductionService $sourceDeductionService, | ||
GetSourceItemsBySkuInterface $getSourceItemsBySku, | ||
SalesEventInterfaceFactory $salesEventFactory, | ||
ItemToDeductFactory $itemToDeductFactory | ||
) { | ||
$this->getSkuFromOrderItem = $getSkuFromOrderItem; | ||
|
||
|
||
$this->isSourceItemManagementAllowedForProductType = $isSourceItemManagementAllowedForProductType; | ||
$this->getProductTypesBySkus = $getProductTypesBySkus; | ||
$this->orderRepository = $orderRepository; | ||
$this->defaultSourceProvider = $defaultSourceProvider; | ||
$this->getSourcesAssignedToStockOrderedByPriority = $getSourcesAssignedToStockOrderedByPriority; | ||
$this->stockByWebsiteIdResolver = $stockByWebsiteIdResolver; | ||
|
||
$this->sourceDeductionRequestFactory = $sourceDeductionRequestFactory; | ||
$this->salesEventExtensionFactory = $salesEventExtensionFactory; | ||
$this->getSalesChannelForOrder = $getSalesChannelForOrderFactory->create(); | ||
$this->sourceDeductionService = $sourceDeductionService; | ||
$this->getSourceItemsBySku = $getSourceItemsBySku; | ||
$this->salesEventFactory = $salesEventFactory; | ||
$this->itemToDeductFactory = $itemToDeductFactory; | ||
} | ||
|
||
|
||
public function execute(Observer $observer) | ||
{ | ||
/* @var $creditMemo \Magento\Sales\Model\Order\Creditmemo */ | ||
$creditMemo = $observer->getEvent()->getCreditmemo(); | ||
$order = $this->orderRepository->get($creditMemo->getOrderId()); | ||
$websiteId = (int)$order->getStore()->getWebsiteId(); | ||
$salesChannel = $this->getSalesChannelForOrder->execute($order); | ||
|
||
$items = $returnToStockItems = []; | ||
foreach ($creditMemo->getItems() as $item) { | ||
$orderItem = $item->getOrderItem(); | ||
$itemSku = $this->getSkuFromOrderItem->execute($orderItem); | ||
|
||
if ($this->isValidItem($itemSku, $orderItem->getProductType()) && $item->getBackToStock()) { | ||
$returnToStockItems[] = $item->getOrderItemId(); | ||
$qty = $item->getQty(); | ||
$stockId = (int)$this->stockByWebsiteIdResolver->execute($websiteId)->getStockId(); | ||
$sourceCode = $this->getSourceCodeWithHighestPriorityBySku((string)$itemSku, $stockId); | ||
$items[$sourceCode][] = $this->itemToDeductFactory->create([ | ||
'sku' => $itemSku, | ||
'qty' => -$qty | ||
]); | ||
} | ||
} | ||
|
||
/** @var SalesEventExtensionInterface */ | ||
$salesEventExtension = $this->salesEventExtensionFactory->create([ | ||
'data' => ['objectIncrementId' => (string)$order->getIncrementId()] | ||
]); | ||
/** @var SalesEventInterface $salesEvent */ | ||
$salesEvent = $this->salesEventFactory->create([ | ||
'type' => SalesEventInterface::EVENT_CREDITMEMO_CREATED, | ||
'objectType' => SalesEventInterface::OBJECT_TYPE_ORDER, | ||
'objectId' => (string)$order->getEntityId() | ||
]); | ||
$salesEvent->setExtensionAttributes($salesEventExtension); | ||
|
||
foreach ($items as $sourceCode => $items) { | ||
$sourceDeductionRequest = $this->sourceDeductionRequestFactory->create([ | ||
'sourceCode' => $sourceCode, | ||
'items' => $items, | ||
'salesChannel' => $salesChannel, | ||
'salesEvent' => $salesEvent | ||
]); | ||
$this->sourceDeductionService->execute($sourceDeductionRequest); | ||
} | ||
} | ||
|
||
/** | ||
* Verify is item valid for return qty to stock. | ||
* | ||
* @param string $sku | ||
* @param string|null $typeId | ||
* | ||
* @return bool | ||
*/ | ||
private function isValidItem(string $sku, ?string $typeId): bool | ||
{ | ||
// https://github.com/magento-engcom/msi/issues/1761 | ||
// If product type located in table sales_order_item is "grouped" replace it with "simple" | ||
if ($typeId === 'grouped') { | ||
$typeId = 'simple'; | ||
} | ||
|
||
$productType = $typeId ?: $this->getProductTypesBySkus->execute( | ||
[$sku] | ||
)[$sku]; | ||
|
||
return $this->isSourceItemManagementAllowedForProductType->execute($productType); | ||
} | ||
|
||
/** | ||
* Returns source code with highest priority by sku | ||
* | ||
* @param string $sku | ||
* @param int $stockId | ||
* | ||
* @return string | ||
*/ | ||
private function getSourceCodeWithHighestPriorityBySku(string $sku, int $stockId): string | ||
{ | ||
$sourceCode = $this->defaultSourceProvider->getCode(); | ||
try { | ||
$availableSourcesForProduct = $this->getSourceItemsBySku->execute($sku); | ||
$assignedSourcesToStock = $this->getSourcesAssignedToStockOrderedByPriority->execute($stockId); | ||
foreach ($assignedSourcesToStock as $assignedSource) { | ||
foreach ($availableSourcesForProduct as $availableSource) { | ||
if ($assignedSource->getSourceCode() == $availableSource->getSourceCode()) { | ||
$sourceCode = $assignedSource->getSourceCode(); | ||
break 2; | ||
} | ||
} | ||
} | ||
} catch (LocalizedException $e) { | ||
//Use Default Source if the source can't be resolved | ||
return $sourceCode; | ||
} | ||
|
||
return $sourceCode; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
<?php | ||
declare(strict_types=1); | ||
namespace Ampersand\DisableStockReservation\ReturnProcessor; | ||
|
||
use Magento\InventorySalesApi\Api\Data\SalesChannelInterface; | ||
use Magento\InventorySalesApi\Api\Data\SalesChannelInterfaceFactory; | ||
use Magento\Sales\Api\Data\OrderInterface; | ||
use Magento\Store\Api\WebsiteRepositoryInterface; | ||
|
||
class GetSalesChannelForOrder | ||
{ | ||
/** | ||
* @var SalesChannelInterfaceFactory | ||
*/ | ||
private $salesChannelFactory; | ||
|
||
/** | ||
* @var WebsiteRepositoryInterface | ||
*/ | ||
private $websiteRepository; | ||
|
||
/** | ||
* @param WebsiteRepositoryInterface $websiteRepository | ||
* @param SalesChannelInterfaceFactory $salesChannelFactory | ||
*/ | ||
public function __construct( | ||
WebsiteRepositoryInterface $websiteRepository, | ||
SalesChannelInterfaceFactory $salesChannelFactory | ||
) { | ||
$this->websiteRepository = $websiteRepository; | ||
$this->salesChannelFactory = $salesChannelFactory; | ||
} | ||
|
||
/** | ||
* Return sales channel for order | ||
* | ||
* @param OrderInterface $order | ||
* @return SalesChannelInterface | ||
*/ | ||
public function execute(OrderInterface $order): SalesChannelInterface | ||
{ | ||
$websiteId = (int)$order->getStore()->getWebsiteId(); | ||
$websiteCode = $this->websiteRepository->getById($websiteId)->getCode(); | ||
|
||
return $this->salesChannelFactory->create([ | ||
'data' => [ | ||
'type' => SalesChannelInterface::TYPE_WEBSITE, | ||
'code' => $websiteCode | ||
] | ||
]); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
<?php | ||
declare(strict_types=1); | ||
namespace Ampersand\DisableStockReservation\ReturnProcessor; | ||
|
||
use Magento\Framework\ObjectManagerInterface; | ||
use Magento\InventorySales\Model\ReturnProcessor\GetSalesChannelForOrder as GetSalesChannelForOrder24; | ||
|
||
class GetSalesChannelForOrderFactory | ||
{ | ||
/** | ||
* @var ObjectManagerInterface | ||
*/ | ||
private $objectManager; | ||
|
||
/** | ||
* constructor. | ||
*/ | ||
public function __construct( | ||
ObjectManagerInterface $objectManager | ||
) { | ||
$this->objectManager = $objectManager; | ||
} | ||
|
||
/** | ||
* For magento 2.4 return the core provided class | ||
* For magento 2.3 return the workaround copy of that class | ||
* | ||
* @return GetSalesChannelForOrder|GetSalesChannelForOrder24|mixed | ||
*/ | ||
public function create() | ||
{ | ||
if (\class_exists(GetSalesChannelForOrder24::class)) { | ||
return $this->objectManager->create(GetSalesChannelForOrder24::class); | ||
} | ||
return $this->objectManager->create( | ||
GetSalesChannelForOrder::class | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters