diff --git a/app/code/Magento/InventoryCatalog/Plugin/InventoryApi/StockRepository/PreventDeleting/AssignedToSalesChannelsStockPlugin.php b/app/code/Magento/InventoryCatalog/Plugin/InventoryApi/StockRepository/PreventDeleting/AssignedToSalesChannelsStockPlugin.php new file mode 100644 index 000000000000..3105844520f0 --- /dev/null +++ b/app/code/Magento/InventoryCatalog/Plugin/InventoryApi/StockRepository/PreventDeleting/AssignedToSalesChannelsStockPlugin.php @@ -0,0 +1,49 @@ +assignedSalesChannelsForStock = $assignedSalesChannelsForStock; + } + + /** + * Prevent deleting of Stock which assigned at least one Sales Channel + * + * @param StockRepositoryInterface $subject + * @param int $stockId + * @return void + * @throws CouldNotDeleteException + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function beforeDeleteById(StockRepositoryInterface $subject, int $stockId) + { + $assignSalesChannels = $this->assignedSalesChannelsForStock->execute($stockId); + if (count($assignSalesChannels)) { + throw new CouldNotDeleteException(__('Stock has at least one sale channel and could not be deleted.')); + } + } +} diff --git a/app/code/Magento/InventoryCatalog/Plugin/Model/StockRepositoryPlugin.php b/app/code/Magento/InventoryCatalog/Plugin/InventoryApi/StockRepository/PreventDeleting/DefaultStockPlugin.php similarity index 75% rename from app/code/Magento/InventoryCatalog/Plugin/Model/StockRepositoryPlugin.php rename to app/code/Magento/InventoryCatalog/Plugin/InventoryApi/StockRepository/PreventDeleting/DefaultStockPlugin.php index a44f014c80e3..a4852c5c8d94 100644 --- a/app/code/Magento/InventoryCatalog/Plugin/Model/StockRepositoryPlugin.php +++ b/app/code/Magento/InventoryCatalog/Plugin/InventoryApi/StockRepository/PreventDeleting/DefaultStockPlugin.php @@ -3,17 +3,18 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); -namespace Magento\InventoryCatalog\Plugin\Model; +namespace Magento\InventoryCatalog\Plugin\InventoryApi\StockRepository\PreventDeleting; use Magento\Framework\Exception\CouldNotDeleteException; use Magento\InventoryApi\Api\StockRepositoryInterface; use Magento\InventoryCatalog\Api\DefaultStockProviderInterface; /** - * Class provide Before Plugin on StockRepositoryInterface::deleteByItem to prevent default stock could be deleted + * Prevent deleting of Default Stock */ -class StockRepositoryPlugin +class DefaultStockPlugin { /** * @var DefaultStockProviderInterface @@ -23,17 +24,17 @@ class StockRepositoryPlugin /** * @param DefaultStockProviderInterface $defaultStockProvider */ - public function __construct(DefaultStockProviderInterface $defaultStockProvider) - { + public function __construct( + DefaultStockProviderInterface $defaultStockProvider + ) { $this->defaultStockProvider = $defaultStockProvider; } /** - * Prevent default source to be deleted + * Prevent deleting of Default Stock * * @param StockRepositoryInterface $subject * @param int $stockId - * * @return void * @throws CouldNotDeleteException * @SuppressWarnings(PHPMD.UnusedFormalParameter) diff --git a/app/code/Magento/InventoryCatalog/Test/Api/StockRepository/PreventAssignedToSalesChannelsStockDeletingTest.php b/app/code/Magento/InventoryCatalog/Test/Api/StockRepository/PreventAssignedToSalesChannelsStockDeletingTest.php new file mode 100644 index 000000000000..331285ff9f10 --- /dev/null +++ b/app/code/Magento/InventoryCatalog/Test/Api/StockRepository/PreventAssignedToSalesChannelsStockDeletingTest.php @@ -0,0 +1,52 @@ + [ + 'resourcePath' => '/V1/inventory/stock/' . $stockId, + 'httpMethod' => Request::HTTP_METHOD_DELETE, + ], + 'soap' => [ + 'service' => 'inventoryApiStockRepositoryV1', + 'operation' => 'inventoryApiStockRepositoryV1DeleteById', + ], + ]; + $expectedMessage = 'Stock has at least one sale channel and could not be deleted.'; + try { + (TESTS_WEB_API_ADAPTER == self::ADAPTER_REST) + ? $this->_webApiCall($serviceInfo) + : $this->_webApiCall($serviceInfo, ['stockId' => $stockId]); + $this->fail('Expected throwing exception'); + } catch (\Exception $e) { + if (TESTS_WEB_API_ADAPTER == self::ADAPTER_REST) { + $errorData = $this->processRestExceptionResult($e); + self::assertEquals($expectedMessage, $errorData['message']); + self::assertEquals(Exception::HTTP_BAD_REQUEST, $e->getCode()); + } elseif (TESTS_WEB_API_ADAPTER == self::ADAPTER_SOAP) { + $this->assertInstanceOf('SoapFault', $e); + $this->checkSoapFault($e, $expectedMessage, 'env:Sender'); + } else { + throw $e; + } + } + } +} diff --git a/app/code/Magento/InventoryCatalog/Test/Api/StockRepository/CouldNotDeleteDefaultStockExceptionTest.php b/app/code/Magento/InventoryCatalog/Test/Api/StockRepository/PreventDefaultStockDeletingTest.php similarity index 92% rename from app/code/Magento/InventoryCatalog/Test/Api/StockRepository/CouldNotDeleteDefaultStockExceptionTest.php rename to app/code/Magento/InventoryCatalog/Test/Api/StockRepository/PreventDefaultStockDeletingTest.php index 0e2a002f53ca..d17b73a05bc2 100644 --- a/app/code/Magento/InventoryCatalog/Test/Api/StockRepository/CouldNotDeleteDefaultStockExceptionTest.php +++ b/app/code/Magento/InventoryCatalog/Test/Api/StockRepository/PreventDefaultStockDeletingTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\InventoryCatalog\Test\Api\StockRepository; @@ -12,10 +13,7 @@ use Magento\TestFramework\Helper\Bootstrap; use Magento\InventoryCatalog\Api\DefaultStockProviderInterface; -/** - * Class CouldNotDeleteDefaultStockExceptionTest - */ -class CouldNotDeleteDefaultStockExceptionTest extends WebapiAbstract +class PreventDefaultStockDeletingTest extends WebapiAbstract { /** * @var DefaultStockProviderInterface @@ -32,8 +30,6 @@ protected function setUp() } /** - * Test that default Stock could not be deleted - * * @throws \Exception */ public function testCouldNotDeleteException() diff --git a/app/code/Magento/InventoryCatalog/Ui/Component/Control/Stock/DeleteButton.php b/app/code/Magento/InventoryCatalog/Ui/Component/Control/Stock/DeleteButton.php index 7b8e2546bcec..f9e20031d3b8 100644 --- a/app/code/Magento/InventoryCatalog/Ui/Component/Control/Stock/DeleteButton.php +++ b/app/code/Magento/InventoryCatalog/Ui/Component/Control/Stock/DeleteButton.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\InventoryCatalog\Ui\Component\Control\Stock; @@ -11,10 +12,11 @@ use Magento\InventoryApi\Api\Data\StockInterface; use Magento\InventoryCatalog\Api\DefaultStockProviderInterface; use Magento\Framework\App\RequestInterface; +use Magento\InventorySales\Model\GetAssignedSalesChannelsForStockInterface; /** * Represents delete button with pre-configured options - * Provide an ability to show delete button only when stock id is not default + * Provide an ability to show delete button only when stock id is not default or doesn't have assigned sales channels */ class DeleteButton implements ButtonProviderInterface { @@ -28,6 +30,11 @@ class DeleteButton implements ButtonProviderInterface */ private $defaultStockProvider; + /** + * @var GetAssignedSalesChannelsForStockInterface + */ + private $assignedSalesChannelsForStock; + /** * @var RequestInterface */ @@ -36,15 +43,18 @@ class DeleteButton implements ButtonProviderInterface /** * @param StockDeleteButton $deleteButton * @param DefaultStockProviderInterface $defaultStockProvider + * @param GetAssignedSalesChannelsForStockInterface $assignedSalesChannelsForStock * @param RequestInterface $request */ public function __construct( StockDeleteButton $deleteButton, DefaultStockProviderInterface $defaultStockProvider, + GetAssignedSalesChannelsForStockInterface $assignedSalesChannelsForStock, RequestInterface $request ) { $this->deleteButton = $deleteButton; $this->defaultStockProvider = $defaultStockProvider; + $this->assignedSalesChannelsForStock = $assignedSalesChannelsForStock; $this->request = $request; } @@ -53,7 +63,9 @@ public function __construct( */ public function getButtonData() { - if ((int)$this->request->getParam(StockInterface::STOCK_ID) === $this->defaultStockProvider->getId()) { + $stockId = (int)$this->request->getParam(StockInterface::STOCK_ID); + $assignSalesChannels = $this->assignedSalesChannelsForStock->execute($stockId); + if ($stockId === $this->defaultStockProvider->getId() || count($assignSalesChannels)) { return []; } diff --git a/app/code/Magento/InventoryCatalog/etc/di.xml b/app/code/Magento/InventoryCatalog/etc/di.xml index 997bbe26b3c4..d7e40373206c 100644 --- a/app/code/Magento/InventoryCatalog/etc/di.xml +++ b/app/code/Magento/InventoryCatalog/etc/di.xml @@ -9,6 +9,9 @@ - + + diff --git a/app/code/Magento/InventorySalesApi/Test/_files/stock_with_sales_channels_rollback.php b/app/code/Magento/InventorySalesApi/Test/_files/stock_with_sales_channels_rollback.php index de50c82457f7..aa40d2e6d91e 100644 --- a/app/code/Magento/InventorySalesApi/Test/_files/stock_with_sales_channels_rollback.php +++ b/app/code/Magento/InventorySalesApi/Test/_files/stock_with_sales_channels_rollback.php @@ -8,4 +8,10 @@ /** @var StockRepositoryInterface $stockRepository */ $stockRepository = Bootstrap::getObjectManager()->get(StockRepositoryInterface::class); + +// Firstly clear relations with sales channels +$stock = $stockRepository->get(10); +$stock->getExtensionAttributes()->setSalesChannels([]); +$stockRepository->save($stock); + $stockRepository->deleteById(10); diff --git a/dev/tests/integration/phpunit.xml.dist b/dev/tests/integration/phpunit.xml.dist index cca9c1e81f2c..6a7a70fe2cf7 100644 --- a/dev/tests/integration/phpunit.xml.dist +++ b/dev/tests/integration/phpunit.xml.dist @@ -54,7 +54,7 @@ - +