AmpersandHQ / magento2-disable-stock-reservation

This module disables the inventory reservation logic introduced as part of MSI in Magento 2.3.3
GNU Lesser General Public License v3.0
215 stars 61 forks source link

Incompatibility between disable-stock-reservation and 2.4.0 In store pickup during Ampersand\DisableStockReservation\Plugin\SourceDeductionProcessor->afterPlace #38

Closed mrobichaud-absolunet closed 3 years ago

mrobichaud-absolunet commented 3 years ago

An exception occurs if you are connected and there are orders not completed on other customer.

At some point in the stack when this function is called a list of all pending pickup orders are loaded and magento send an exception if you try to load an order that isn't associated to the connected customer. When this happen, since it's in the afterPlace, the order is created but the checkout process never complete and the customer is presented with a "No such entity found for entity_id = ###"

The specific line where it all start (95) $sourceSelectionResult = $this->getSourceSelectionResultFromOrder->execute($order);

Here's a stack trace of the exception

#0 /srv/www/vendor/magento/module-sales/Model/ResourceModel/Order/Plugin/Authorization.php(45): Magento\Framework\Exception\NoSuchEntityException::singleField('orderId', '35')
#1 /srv/www/vendor/magento/framework/Interception/Interceptor.php(146): Magento\Sales\Model\ResourceModel\Order\Plugin\Authorization->afterLoad(Object(Magento\Sales\Model\ResourceModel\Order\Interceptor), Object(Magento\Sales\Model\ResourceModel\Order\Interceptor), Object(Magento\Sales\Model\Order\Interceptor), '35', NULL)
#2 /srv/www/vendor/magento/framework/Interception/Interceptor.php(153): Magento\Sales\Model\ResourceModel\Order\Interceptor->Magento\Framework\Interception\{closure}(Object(Magento\Sales\Model\Order\Interceptor), '35', NULL)
#3 /srv/www/generated/code/Magento/Sales/Model/ResourceModel/Order/Interceptor.php(117): Magento\Sales\Model\ResourceModel\Order\Interceptor->___callPlugins('load', Array, Array)
#4 /srv/www/vendor/magento/framework/Model/AbstractModel.php(540): Magento\Sales\Model\ResourceModel\Order\Interceptor->load(Object(Magento\Sales\Model\Order\Interceptor), '35', NULL)
#5 /srv/www/generated/code/Magento/Sales/Model/Order/Interceptor.php(4964): Magento\Framework\Model\AbstractModel->load('35', NULL)
#6 /srv/www/vendor/magento/module-sales/Model/Order/ShippingAssignmentBuilder.php(79): Magento\Sales\Model\Order\Interceptor->load('35')
#7 /srv/www/vendor/magento/module-sales/Model/OrderRepository.php(294): Magento\Sales\Model\Order\ShippingAssignmentBuilder->create()
#8 /srv/www/vendor/magento/module-sales/Model/OrderRepository.php(216): Magento\Sales\Model\OrderRepository->setShippingAssignments(Object(Magento\Sales\Model\Order\Interceptor))
#9 /srv/www/vendor/magento/framework/Interception/Interceptor.php(58): Magento\Sales\Model\OrderRepository->getList(Object(Magento\Framework\Api\SearchCriteria))
#10 /srv/www/vendor/magento/framework/Interception/Interceptor.php(138): Magento\Sales\Model\OrderRepository\Interceptor->___callParent('getList', Array)
#11 /srv/www/vendor/magento/framework/Interception/Interceptor.php(153): Magento\Sales\Model\OrderRepository\Interceptor->Magento\Framework\Interception\{closure}(Object(Magento\Framework\Api\SearchCriteria))
#12 /srv/www/generated/code/Magento/Sales/Model/OrderRepository/Interceptor.php(39): Magento\Sales\Model\OrderRepository\Interceptor->___callPlugins('getList', Array, Array)
#13 /srv/www/vendor/magento/module-inventory-in-store-pickup-sales/Model/SourceSelection/GetActiveStorePickupOrdersBySource.php(65): Magento\Sales\Model\OrderRepository\Interceptor->getList(Object(Magento\Framework\Api\SearchCriteria))
#14 /srv/www/vendor/magento/module-inventory-in-store-pickup-sales/Model/SourceSelection/GetSourceItemQtyAvailableService.php(105): Magento\InventoryInStorePickupSales\Model\SourceSelection\GetActiveStorePickupOrdersBySource->execute('4101')
#15 /srv/www/vendor/magento/module-inventory-in-store-pickup-sales/Model/SourceSelection/GetSourceItemQtyAvailableService.php(76): Magento\InventoryInStorePickupSales\Model\SourceSelection\GetSourceItemQtyAvailableService->getStorePickupOrdersBySourceItem(Object(Magento\Inventory\Model\SourceItem))
#16 /srv/www/vendor/magento/module-inventory-in-store-pickup-sales/Model/SourceSelection/GetSourceItemQtyAvailableService.php(64): Magento\InventoryInStorePickupSales\Model\SourceSelection\GetSourceItemQtyAvailableService->getStorePickupReservedQty(Object(Magento\Inventory\Model\SourceItem))
#17 /srv/www/vendor/magento/module-inventory-source-selection-api/Model/Algorithms/Result/GetDefaultSortedSourcesResult.php(115): Magento\InventoryInStorePickupSales\Model\SourceSelection\GetSourceItemQtyAvailableService->execute(Object(Magento\Inventory\Model\SourceItem))
#18 /srv/www/vendor/magento/module-inventory-source-selection/Model/Algorithms/PriorityBasedAlgorithm.php(73): Magento\InventorySourceSelectionApi\Model\Algorithms\Result\GetDefaultSortedSourcesResult->execute(Object(Magento\InventorySourceSelection\Model\Request\InventoryRequest), Array)
#19 /srv/www/vendor/magento/module-inventory-source-selection-api/Model/SourceSelectionService.php(61): Magento\InventorySourceSelection\Model\Algorithms\PriorityBasedAlgorithm->execute(Object(Magento\InventorySourceSelection\Model\Request\InventoryRequest))
**#20 /srv/www/vendor/ampersand/magento2-disable-stock-reservation/src/Model/GetSourceSelectionResultFromOrder.php(79): Magento\InventorySourceSelectionApi\Model\SourceSelectionService->execute(Object(Magento\InventorySourceSelection\Model\Request\InventoryRequest), 'priority')
#21 /srv/www/vendor/ampersand/magento2-disable-stock-reservation/src/Plugin/SourceDeductionProcessor.php(95): Ampersand\DisableStockReservation\Model\GetSourceSelectionResultFromOrder->execute(Object(Magento\Sales\Model\Order\Interceptor))
#22 /srv/www/vendor/magento/framework/Interception/Interceptor.php(146): Ampersand\DisableStockReservation\Plugin\SourceDeductionProcessor->afterPlace(Object(Magento\Sales\Model\Service\OrderService\Interceptor), Object(Magento\Sales\Model\Order\Interceptor), Object(Magento\Sales\Model\Order\Interceptor))**
#23 /srv/www/vendor/magento/module-inventory-sales/Plugin/Sales/OrderManagement/AppendReservationsAfterOrderPlacementPlugin.php(195): Magento\Sales\Model\Service\OrderService\Interceptor->Magento\Framework\Interception\{closure}(Object(Magento\Sales\Model\Order\Interceptor))
#24 /srv/www/vendor/magento/framework/Interception/Interceptor.php(135): Magento\InventorySales\Plugin\Sales\OrderManagement\AppendReservationsAfterOrderPlacementPlugin->aroundPlace(Object(Magento\Sales\Model\Service\OrderService\Interceptor), Object(Closure), Object(Magento\Sales\Model\Order\Interceptor))
#25 /srv/www/vendor/magento/framework/Interception/Interceptor.php(153): Magento\Sales\Model\Service\OrderService\Interceptor->Magento\Framework\Interception\{closure}(Object(Magento\Sales\Model\Order\Interceptor))
#26 /srv/www/generated/code/Magento/Sales/Model/Service/OrderService/Interceptor.php(117): Magento\Sales\Model\Service\OrderService\Interceptor->___callPlugins('place', Array, Array)
#27 /srv/www/vendor/magento/module-quote/Model/QuoteManagement.php(563): Magento\Sales\Model\Service\OrderService\Interceptor->place(Object(Magento\Sales\Model\Order\Interceptor))
#28 /srv/www/vendor/magento/module-quote/Model/QuoteManagement.php(453): Magento\Quote\Model\QuoteManagement->submitQuote(Object(Magento\Quote\Model\Quote\Interceptor), Array)
#29 /srv/www/vendor/magento/framework/Interception/Interceptor.php(58): Magento\Quote\Model\QuoteManagement->submit(Object(Magento\Quote\Model\Quote\Interceptor), Array)
#30 /srv/www/vendor/magento/framework/Interception/Interceptor.php(138): Magento\Quote\Model\QuoteManagement\Interceptor->___callParent('submit', Array)
#31 /srv/www/vendor/magento/framework/Interception/Interceptor.php(153): Magento\Quote\Model\QuoteManagement\Interceptor->Magento\Framework\Interception\{closure}(Object(Magento\Quote\Model\Quote\Interceptor))
#32 /srv/www/generated/code/Magento/Quote/Model/QuoteManagement/Interceptor.php(91): Magento\Quote\Model\QuoteManagement\Interceptor->___callPlugins('submit', Array, Array)
#33 /srv/www/vendor/magento/module-quote/Model/QuoteManagement.php(411): Magento\Quote\Model\QuoteManagement\Interceptor->submit(Object(Magento\Quote\Model\Quote\Interceptor))
#34 /srv/www/generated/code/Magento/Quote/Model/QuoteManagement/Interceptor.php(63): Magento\Quote\Model\QuoteManagement->placeOrder(94, NULL)
#35 /srv/www/vendor/magento/module-checkout/Model/PaymentInformationManagement.php(86): Magento\Quote\Model\QuoteManagement\Interceptor->placeOrder(94)
#36 /srv/www/vendor/magento/framework/Interception/Interceptor.php(58): Magento\Checkout\Model\PaymentInformationManagement->savePaymentInformationAndPlaceOrder(94, Object(Magento\Quote\Model\Quote\Payment), Object(Magento\Quote\Model\Quote\Address\Interceptor))
#37 /srv/www/vendor/magento/framework/Interception/Interceptor.php(138): Magento\Checkout\Model\PaymentInformationManagement\Interceptor->___callParent('savePaymentInfo...', Array)
#38 /srv/www/vendor/magento/framework/Interception/Interceptor.php(153): Magento\Checkout\Model\PaymentInformationManagement\Interceptor->Magento\Framework\Interception\{closure}(94, Object(Magento\Quote\Model\Quote\Payment), Object(Magento\Quote\Model\Quote\Address\Interceptor))
#39 /srv/www/generated/code/Magento/Checkout/Model/PaymentInformationManagement/Interceptor.php(26): Magento\Checkout\Model\PaymentInformationManagement\Interceptor->___callPlugins('savePaymentInfo...', Array, Array)
#40 [internal function]: Magento\Checkout\Model\PaymentInformationManagement\Interceptor->savePaymentInformationAndPlaceOrder(94, Object(Magento\Quote\Model\Quote\Payment), Object(Magento\Quote\Model\Quote\Address\Interceptor))
#41 /srv/www/vendor/magento/module-webapi/Controller/Rest/SynchronousRequestProcessor.php(95): call_user_func_array(Array, Array)
#42 /srv/www/vendor/magento/module-webapi/Controller/Rest.php(188): Magento\Webapi\Controller\Rest\SynchronousRequestProcessor->process(Object(Magento\Framework\Webapi\Rest\Request\Proxy))
#43 /srv/www/vendor/magento/framework/Interception/Interceptor.php(58): Magento\Webapi\Controller\Rest->dispatch(Object(Magento\Framework\App\Request\Http))
#44 /srv/www/vendor/magento/framework/Interception/Interceptor.php(138): Magento\Webapi\Controller\Rest\Interceptor->___callParent('dispatch', Array)
#45 /srv/www/vendor/fastly/magento2/Model/FrontControllerPlugin.php(131): Magento\Webapi\Controller\Rest\Interceptor->Magento\Framework\Interception\{closure}(Object(Magento\Framework\App\Request\Http))
#46 /srv/www/vendor/magento/framework/Interception/Interceptor.php(135): Fastly\Cdn\Model\FrontControllerPlugin->aroundDispatch(Object(Magento\Webapi\Controller\Rest\Interceptor), Object(Closure), Object(Magento\Framework\App\Request\Http))
#47 /srv/www/vendor/magento/framework/Interception/Interceptor.php(153): Magento\Webapi\Controller\Rest\Interceptor->Magento\Framework\Interception\{closure}(Object(Magento\Framework\App\Request\Http))
#48 /srv/www/generated/code/Magento/Webapi/Controller/Rest/Interceptor.php(26): Magento\Webapi\Controller\Rest\Interceptor->___callPlugins('dispatch', Array, Array)
#49 /srv/www/vendor/magento/framework/App/Http.php(116): Magento\Webapi\Controller\Rest\Interceptor->dispatch(Object(Magento\Framework\App\Request\Http))
#50 /srv/www/generated/code/Magento/Framework/App/Http/Interceptor.php(24): Magento\Framework\App\Http->launch()
#51 /srv/www/vendor/magento/framework/App/Bootstrap.php(263): Magento\Framework\App\Http\Interceptor->launch()
#52 /srv/www/pub/index.php(40): Magento\Framework\App\Bootstrap->run(Object(Magento\Framework\App\Http\Interceptor))
#53 {main}
mrobichaud-absolunet commented 3 years ago

After some investigation, the problem comes mainly from this interface that changes with in store pickup. Magento\InventorySourceSelectionApi\Model\GetSourceItemQtyAvailableInterface

Normally if points to this code Magento\InventorySourceSelectionApi\Model\GetSourceItemQtyAvailableService public function execute(SourceItemInterface $sourceItem): float { return $sourceItem->getQuantity(); } But with the in store pickup module a new class is created and the code is as follow Magento\InventoryInStorePickupSales\Model\SourceSelection\GetSourceItemQtyAvailableService

public function execute(SourceItemInterface $sourceItem): float
    {
        /* TODO: create config and check if store pickup is enabled? */
        return $sourceItem->getQuantity() - $this->getStorePickupReservedQty($sourceItem);
    }

Since this extension disable reservation $this->getStorePickupReservedQty($sourceItem); isn't really necessary. Our solution was to revert back the preference in a di.xml <preference for="Magento\InventorySourceSelectionApi\Model\GetSourceItemQtyAvailableInterface" type="Magento\InventorySourceSelectionApi\Model\GetSourceItemQtyAvailableService" />

liamjtoohey commented 3 years ago

hi @mrobichaudb, could you provide a few more steps on how to replicate this error? I presume it's only when there are store pick up orders associated with the source item? We are currently trying to ensure this module is viable for Magento 2.4 and are keen to implement a fix for this (potentially the aforementioned di.xml fix) but do not want to lose any non-MSI functionality on the new in-store pickup feature.

mrobichaud-absolunet commented 3 years ago
  1. You need order that has the Pickup in store as delivery option and is not completed. (I think it need to be a connected user but I'm not sure)
  2. You need to do a second order with a different connected user (this time it needs to be a user for sure). You should have an error when you try placing the order
  3. What I don't know is if the two orders needs to be the same product or the same pickup location to throw the error.
convenient commented 3 years ago

@mrobichaud-absolunet we believe this issue is resolved as of https://github.com/AmpersandHQ/magento2-disable-stock-reservation/pull/43

Can you please review and let me know if you have any further issues.

Thanks