diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/MassCancel.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/MassCancel.php index 5d7a55bc4a56c..05a22245dc004 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/MassCancel.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/MassCancel.php @@ -11,6 +11,7 @@ use Magento\Ui\Component\MassAction\Filter; use Magento\Sales\Model\ResourceModel\Order\CollectionFactory; use Magento\Framework\App\Request\Http as HttpRequest; +use Magento\Sales\Api\OrderManagementInterface; class MassCancel extends \Magento\Sales\Controller\Adminhtml\Order\AbstractMassAction { @@ -18,16 +19,29 @@ class MassCancel extends \Magento\Sales\Controller\Adminhtml\Order\AbstractMassA * Authorization level of a basic admin session */ const ADMIN_RESOURCE = 'Magento_Sales::cancel'; + + /** + * @var OrderManagementInterface + */ + private $orderManagement; /** * @param Context $context * @param Filter $filter * @param CollectionFactory $collectionFactory + * @param OrderManagementInterface|null $orderManagement */ - public function __construct(Context $context, Filter $filter, CollectionFactory $collectionFactory) - { + public function __construct( + Context $context, + Filter $filter, + CollectionFactory $collectionFactory, + OrderManagementInterface $orderManagement = null + ) { parent::__construct($context, $filter); $this->collectionFactory = $collectionFactory; + $this->orderManagement = $orderManagement ?: \Magento\Framework\App\ObjectManager::getInstance()->get( + \Magento\Sales\Api\OrderManagementInterface::class + ); } /** @@ -54,11 +68,10 @@ protected function massAction(AbstractCollection $collection) { $countCancelOrder = 0; foreach ($collection->getItems() as $order) { - if (!$order->canCancel()) { + $isCanceled = $this->orderManagement->cancel($order->getEntityId()); + if ($isCanceled === false) { continue; } - $order->cancel(); - $order->save(); $countCancelOrder++; } $countNonCancelOrder = $collection->count() - $countCancelOrder; diff --git a/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/MassCancelTest.php b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/MassCancelTest.php new file mode 100644 index 0000000000000..756bade3c83c9 --- /dev/null +++ b/app/code/Magento/Sales/Test/Unit/Controller/Adminhtml/Order/MassCancelTest.php @@ -0,0 +1,291 @@ +contextMock = $this->createMock(\Magento\Backend\App\Action\Context::class); + $this->messageManagerMock = $this->createMock(\Magento\Framework\Message\Manager::class); + $this->responseMock = $this->createMock(\Magento\Framework\App\ResponseInterface::class); + $this->requestMock = $this->createMock(\Magento\Framework\App\Request\Http::class); + $this->objectManagerMock = $this->createMock(\Magento\Framework\ObjectManager\ObjectManager::class); + + $resultRedirectFactory = $this->createMock(\Magento\Backend\Model\View\Result\RedirectFactory::class); + + $this->orderCollectionMock = $this->getMockBuilder(\Magento\Sales\Model\ResourceModel\Order\Collection::class) + ->disableOriginalConstructor() + ->getMock(); + + $resourceCollection = \Magento\Sales\Model\ResourceModel\Order\CollectionFactory::class; + $this->orderCollectionFactoryMock = $this->getMockBuilder($resourceCollection) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + + $this->sessionMock = $this->createPartialMock(\Magento\Backend\Model\Session::class, ['setIsUrlNotice']); + $this->actionFlagMock = $this->createPartialMock(\Magento\Framework\App\ActionFlag::class, ['get', 'set']); + $this->helperMock = $this->createPartialMock(\Magento\Backend\Helper\Data::class, ['getUrl']); + $this->resultRedirectMock = $this->createMock(\Magento\Backend\Model\View\Result\Redirect::class); + $resultRedirectFactory->expects($this->any())->method('create')->willReturn($this->resultRedirectMock); + + $redirectMock = $this->getMockBuilder(\Magento\Backend\Model\View\Result\Redirect::class) + ->disableOriginalConstructor() + ->getMock(); + + $resultFactoryMock = $this->getMockBuilder(\Magento\Framework\Controller\ResultFactory::class) + ->disableOriginalConstructor() + ->getMock(); + $resultFactoryMock->expects($this->any()) + ->method('create') + ->with(\Magento\Framework\Controller\ResultFactory::TYPE_REDIRECT) + ->willReturn($redirectMock); + + $this->contextMock->expects($this->once())->method('getMessageManager')->willReturn($this->messageManagerMock); + $this->contextMock->expects($this->once())->method('getRequest')->willReturn($this->requestMock); + $this->contextMock->expects($this->once())->method('getResponse')->willReturn($this->responseMock); + $this->contextMock->expects($this->once())->method('getObjectManager')->willReturn($this->objectManagerMock); + $this->contextMock->expects($this->once())->method('getSession')->willReturn($this->sessionMock); + $this->contextMock->expects($this->once())->method('getActionFlag')->willReturn($this->actionFlagMock); + $this->contextMock->expects($this->once())->method('getHelper')->willReturn($this->helperMock); + $this->contextMock->expects($this->once()) + ->method('getResultRedirectFactory') + ->willReturn($resultRedirectFactory); + $this->contextMock->expects($this->any()) + ->method('getResultFactory') + ->willReturn($resultFactoryMock); + $this->filterMock = $this->createMock(\Magento\Ui\Component\MassAction\Filter::class); + $this->filterMock->expects($this->once()) + ->method('getCollection') + ->with($this->orderCollectionMock) + ->willReturn($this->orderCollectionMock); + $this->orderCollectionFactoryMock->expects($this->once()) + ->method('create') + ->willReturn($this->orderCollectionMock); + $this->orderManagementMock = $this->createMock(\Magento\Sales\Api\OrderManagementInterface::class); + + $this->massAction = $objectManagerHelper->getObject( + \Magento\Sales\Controller\Adminhtml\Order\MassCancel::class, + [ + 'context' => $this->contextMock, + 'filter' => $this->filterMock, + 'collectionFactory' => $this->orderCollectionFactoryMock, + 'orderManagement' => $this->orderManagementMock + ] + ); + } + + /** + * Test for selected orders + * Two orders, only $order1 can be canceled + */ + public function testExecuteCanCancelOneOrder() + { + $order1id = 100; + $order2id = 200; + + $order1 = $this->getMockBuilder(\Magento\Sales\Model\Order::class) + ->disableOriginalConstructor() + ->getMock(); + $order2 = $this->getMockBuilder(\Magento\Sales\Model\Order::class) + ->disableOriginalConstructor() + ->getMock(); + $orders = [$order1, $order2]; + $countOrders = count($orders); + + $this->orderCollectionMock->expects($this->any()) + ->method('getItems') + ->willReturn($orders); + + $order1->expects($this->once()) + ->method('getEntityId') + ->willReturn($order1id); + + $order2->expects($this->once()) + ->method('getEntityId') + ->willReturn($order2id); + + $this->orderCollectionMock->expects($this->once()) + ->method('count') + ->willReturn($countOrders); + + $this->orderManagementMock->expects($this->at(0))->method('cancel')->with($order1id)->willReturn(true); + $this->orderManagementMock->expects($this->at(1))->method('cancel')->with($order2id)->willReturn(false); + + $this->messageManagerMock->expects($this->once()) + ->method('addError') + ->with('1 order(s) cannot be canceled.'); + + $this->messageManagerMock->expects($this->once()) + ->method('addSuccess') + ->with('We canceled 1 order(s).'); + + $this->resultRedirectMock->expects($this->once()) + ->method('setPath') + ->with('sales/*/') + ->willReturnSelf(); + + $this->massAction->execute(); + } + + /** + * Test for excluded orders + * Two orders could't be canceled + */ + public function testExcludedCannotCancelOrders() + { + $order1 = $this->getMockBuilder(\Magento\Sales\Model\Order::class) + ->disableOriginalConstructor() + ->getMock(); + $order2 = $this->getMockBuilder(\Magento\Sales\Model\Order::class) + ->disableOriginalConstructor() + ->getMock(); + + $orders = [$order1, $order2]; + $countOrders = count($orders); + + $order1->expects($this->once()) + ->method('getEntityId') + ->willReturn(100); + + $order2->expects($this->once()) + ->method('getEntityId') + ->willReturn(200); + + $this->orderCollectionMock->expects($this->any()) + ->method('getItems') + ->willReturn([$order1, $order2]); + + $this->orderCollectionMock->expects($this->once()) + ->method('count') + ->willReturn($countOrders); + + $this->orderManagementMock->expects($this->atLeastOnce())->method('cancel')->willReturn(false); + + $this->messageManagerMock->expects($this->once()) + ->method('addError') + ->with('You cannot cancel the order(s).'); + + $this->resultRedirectMock->expects($this->once()) + ->method('setPath') + ->with('sales/*/') + ->willReturnSelf(); + + $this->massAction->execute(); + } + + /** + * Order throws exception while canceling + */ + public function testException() + { + $exception = new \Exception('Can not cancel'); + + $order1 = $this->getMockBuilder(\Magento\Sales\Model\Order::class) + ->disableOriginalConstructor() + ->getMock(); + $this->orderCollectionMock->expects($this->any()) + ->method('getItems') + ->willReturn([$order1]); + + $order1->expects($this->once()) + ->method('getEntityId') + ->willReturn(100); + + $this->orderManagementMock->expects($this->atLeastOnce())->method('cancel')->willThrowException($exception); + + $this->messageManagerMock->expects($this->once()) + ->method('addError') + ->with('Can not cancel'); + + $this->massAction->execute(); + } +}