inventory
inventory copied to clipboard
observer "inventory_sales_virtual_source_deduction_processor" destroys checkout for free virtual and downloadable products, when store pickup is active
Preconditions (*)
-
magento/module-inventory-shipping 1.2.2
- Magento Enterprise Edition
2.4.5-p1
Steps to reproduce (*)
- Set
sales/minimum_order/active
to 0 - Activate
carriers/instore/active
- Set
carriers/instore/price
to0.00
- Create a custom stock and source and activate in-store pickup for it.
- Create a product A with a price of $10, assign the source with a qty of 99 to it. Set it to be in stock
- Create a virtual product B with a price of $0, assign the source with a qty of 99 to it. Set it to be in stock.
- Create two customers A and B
- Purchase the product A with the logged in customer A and order it via in-store pickup
- Now log out and log in as customer B
- Try to purchase the free virtual product B in the checkout.
Expected result (*)
- I would expect the order to be successfully placed.
Actual result (*)
- After trying to purchase in the checkout, I get the error message
No entry with orderID = xxx
and the order can not be fulfilled.
We debugged that issue for a while and found out, in the end, that the observer inventory_sales_virtual_source_deduction_processor
is the reason for this issue.
The call stack is the following:
- ShippingAssignmentBuilder.php:79, Magento\Sales\Model\Order\ShippingAssignmentBuilder->create()
- OrderRepository.php:306, Magento\Sales\Model\OrderRepository->setShippingAssignments()
- OrderRepository.php:228, Magento\Sales\Model\OrderRepository->getList()
- Interceptor.php:58, Magento\Sales\Model\OrderRepository\Interceptor->___callParent()
- Interceptor.php:138, Magento\Sales\Model\OrderRepository\Interceptor->Magento\Framework\Interception\{closure:/var/www/html/vendor/magento/framework/Interception/Interceptor.php:104-151}()
- Interceptor.php:153, Magento\Sales\Model\OrderRepository\Interceptor->___callPlugins()
- Interceptor.php:32, Magento\Sales\Model\OrderRepository\Interceptor->getList()
- GetActiveStorePickupOrdersBySource.php:65, Magento\InventoryInStorePickupSales\Model\SourceSelection\GetActiveStorePickupOrdersBySource->execute()
- GetSourceItemQtyAvailableService.php:105, -Magento\InventoryInStorePickupSales\Model\SourceSelection\GetSourceItemQtyAvailableService->getStorePickupOrdersBySourceItem()
- GetSourceItemQtyAvailableService.php:76, Magento\InventoryInStorePickupSales\Model\SourceSelection\GetSourceItemQtyAvailableService->getStorePickupReservedQty()
- GetSourceItemQtyAvailableService.php:64, Magento\InventoryInStorePickupSales\Model\SourceSelection\GetSourceItemQtyAvailableService->execute()
- GetDefaultSortedSourcesResult.php:115, Magento\InventorySourceSelectionApi\Model\Algorithms\Result\GetDefaultSortedSourcesResult->execute()
- PriorityBasedAlgorithm.php:73, Magento\InventorySourceSelection\Model\Algorithms\PriorityBasedAlgorithm->execute()
- SourceSelectionService.php:61, Magento\InventorySourceSelectionApi\Model\SourceSelectionService->execute()
- GetSourceSelectionResultFromInvoice.php:99, Magento\InventoryShipping\Model\GetSourceSelectionResultFromInvoice->execute()
- VirtualSourceDeductionProcessor.php:107, Magento\InventoryShipping\Observer\VirtualSourceDeductionProcessor->execute()
- InvokerDefault.php:88, Magento\Framework\Event\Invoker\InvokerDefault->_callObserverMethod()
- InvokerDefault.php:74, Magento\Framework\Event\Invoker\InvokerDefault->dispatch()
- Manager.php:97, Magento\Staging\Model\Event\Manager->dispatch()
- Proxy.php:95, Magento\Staging\Model\Event\Manager\Proxy->dispatch()
- AbstractModel.php:832, Magento\Framework\Model\AbstractModel->afterSave()
- Interceptor.php:1418, Magento\Sales\Model\Order\Invoice\Interceptor->afterSave()
- AbstractDb.php:56, Magento\Framework\Model\ResourceModel\Db\VersionControl\AbstractDb->processAfterSaves()
- AbstractDb.php:402, Magento\Framework\Model\ResourceModel\Db\AbstractDb->save()
- AbstractModel.php:658, Magento\Framework\Model\AbstractModel->save()
- Interceptor.php:1355, Magento\Sales\Model\Order\Invoice\Interceptor->save()
- Relation.php:94, Magento\Sales\Model\ResourceModel\Order\Relation->processRelation()
- RelationComposite.php:48, Magento\Framework\Model\ResourceModel\Db\VersionControl\RelationComposite->processRelations()
- AbstractDb.php:57, Magento\Framework\Model\ResourceModel\Db\VersionControl\AbstractDb->processAfterSaves()
- AbstractDb.php:402, Magento\Framework\Model\ResourceModel\Db\AbstractDb->save()
- Order.php:180, Magento\Sales\Model\ResourceModel\Order->save()
- Interceptor.php:32, Magento\Sales\Model\ResourceModel\Order\Interceptor->save()
- OrderRepository.php:282, Magento\Sales\Model\OrderRepository->save()
- Interceptor.php:58, Magento\Sales\Model\OrderRepository\Interceptor->___callParent()
- Interceptor.php:138, Magento\Sales\Model\OrderRepository\Interceptor->Magento\Framework\Interception\{closure:/var/www/html/vendor/magento/framework/Interception/Interceptor.php:104-151}()
- Interceptor.php:153, Magento\Sales\Model\OrderRepository\Interceptor->___callPlugins()
- Interceptor.php:59, Magento\Sales\Model\OrderRepository\Interceptor->save()
- OrderService.php:214, Magento\Sales\Model\Service\OrderService->place()
- Interceptor.php:58, Magento\Sales\Model\Service\OrderService\Interceptor->___callParent()
- Interceptor.php:138, Magento\Sales\Model\Service\OrderService\Interceptor->Magento\Framework\Interception\{closure:/var/www/html/vendor/magento/framework/Interception/Interceptor.php:104-151}()
- AppendReservationsAfterOrderPlacementPlugin.php:195, Magento\InventorySales\Plugin\Sales\OrderManagement\AppendReservationsAfterOrderPlacementPlugin->aroundPlace()
- Interceptor.php:135, Magento\Sales\Model\Service\OrderService\Interceptor->Magento\Framework\Interception\{closure:/var/www/html/vendor/magento/framework/Interception/Interceptor.php:104-151}()
- Interceptor.php:153, Magento\Sales\Model\Service\OrderService\Interceptor->___callPlugins()
- Interceptor.php:86, Magento\Sales\Model\Service\OrderService\Interceptor->place()
- QuoteManagement.php:603, Magento\Quote\Model\QuoteManagement->submitQuote()
- QuoteManagement.php:483, Magento\Quote\Model\QuoteManagement->submit()
- Interceptor.php:58, Magento\Quote\Model\QuoteManagement\Interceptor->___callParent()
- Interceptor.php:138, Magento\Quote\Model\QuoteManagement\Interceptor->Magento\Framework\Interception\{closure:/var/www/html/vendor/magento/framework/Interception/Interceptor.php:104-151}()
- CouponUsagesIncrement.php:54, Magento\SalesRule\Plugin\CouponUsagesIncrement->aroundSubmit()
- Interceptor.php:135, Magento\Quote\Model\QuoteManagement\Interceptor->Magento\Framework\Interception\{closure:/var/www/html/vendor/magento/framework/Interception/Interceptor.php:104-151}()
- Interceptor.php:153, Magento\Quote\Model\QuoteManagement\Interceptor->___callPlugins()
- Interceptor.php:68, Magento\Quote\Model\QuoteManagement\Interceptor->submit()
- QuoteManagement.php:441, Magento\Quote\Model\QuoteManagement->placeOrder()
- Interceptor.php:50, Magento\Quote\Model\QuoteManagement\Interceptor->placeOrder()
PaymentInformationManagement.php:125, Magento\Checkout\Model\PaymentInformationManagement->savePaymentInformationAndPlaceOrder()
- Interceptor.php:58, Magento\Checkout\Model\PaymentInformationManagement\Interceptor->___callParent()
- Interceptor.php:138, Magento\Checkout\Model\PaymentInformationManagement\Interceptor->Magento\Framework\Interception\{closure:/var/www/html/vendor/magento/framework/Interception/Interceptor.php:104-151}()
- Interceptor.php:153, Magento\Checkout\Model\PaymentInformationManagement\Interceptor->___callPlugins()
- Interceptor.php:23, Magento\Checkout\Model\PaymentInformationManagement\Interceptor->savePaymentInformationAndPlaceOrder()
- AsyncPaymentInformationCustomerPublisher.php:120, Magento\AsyncOrder\Model\AsyncPaymentInformationCustomerPublisher->savePaymentInformationAndPlaceOrder()
- SynchronousRequestProcessor.php:95, call_user_func_array:{/var/www/html/vendor/magento/module-webapi/Controller/Rest/SynchronousRequestProcessor.php:95}()
- SynchronousRequestProcessor.php:95, Magento\Webapi\Controller\Rest\SynchronousRequestProcessor->process()
- Rest.php:195, Magento\Webapi\Controller\Rest->dispatch()
- Interceptor.php:58, Magento\Webapi\Controller\Rest\Interceptor->___callParent()
- Interceptor.php:138, Magento\Webapi\Controller\Rest\Interceptor->Magento\Framework\Interception\{closure:/var/www/html/vendor/magento/framework/Interception/Interceptor.php:104-151}()
- Interceptor.php:153, Magento\Webapi\Controller\Rest\Interceptor->___callPlugins()
- Interceptor.php:23, Magento\Webapi\Controller\Rest\Interceptor->dispatch()
- Http.php:116, Magento\Framework\App\Http->launch()
- Bootstrap.php:264, Magento\Framework\App\Bootstrap->run()
- index.php:30, {main}()
What happens in the end, is, that the VirtualSourceDeductionProcessor
starts to create a list of all orders, which still need to be collected from the in-store pickup location. (see \Magento\InventoryInStorePickupSales\Model\SourceSelection\GetActiveStorePickupOrdersBySource::execute
).
It fails in the end, when it starts loading these orders.
The reason for that is, that free orders are invoiced directly during the checkout process.
When the old order A is now loaded, the plugin authorization
is calling \Magento\Sales\Model\ResourceModel\Order\Plugin\Authorization::afterLoad
, which calls \Magento\Sales\Model\ResourceModel\Order\Plugin\Authorization::isAllowed
.
As the user context is still customer in the checkout, this check returns false, as user B is not allowed to load the order of user A.
For virtual products, which cost more than $0, this does not happen, as the invoice process starts later and this way, the check is not run.
Our workaround for this issue is to disable the observer completely, as we are lucky and don't need to care about stock in the virtual products of our project. But in general it would be great to find a better solution here to fix this issue properly.
Hi @norgeindian. Thank you for your report. To speed up processing of this issue, make sure that you provided sufficient information.
Add a comment to assign the issue: @magento I am working on this
- Join Magento Community Engineering Slack and ask your questions in #github channel.
Just spent about a day following a stack trace trying to debug this same problem. I see now that someone already figured that out lol.
Sucks to see this hasn't been fixed, but I guess it's not a super common issue to come across. Gonna see if I can code something up to keep the functionality while working around the authorization plugin.
Created a small module to fix the issue. https://github.com/SmartexCanada/magento2-InventoryInStorePickupSalesFix
Adds the following:
- around plugin on
\Magento\InventoryInStorePickupSales\Model\SourceSelection\GetActiveStorePickupOrdersBySource::execute
to set that we're processing the source selection request. - around plugin on
\Magento\Sales\Model\ResourceModel\Order\Plugin\Authorization::afterLoad
to check if we're processing the source selection request, allowing us to ignore thrown error fromisAllowed