Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stock Removal. Throw an exception and allow to delete just unassigned stocks #171

Merged
merged 7 commits into from Nov 10, 2017
@@ -0,0 +1,49 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\InventoryCatalog\Plugin\InventoryApi\StockRepository\PreventDeleting;

use Magento\Framework\Exception\CouldNotDeleteException;
use Magento\InventoryApi\Api\StockRepositoryInterface;
use Magento\InventorySales\Model\GetAssignedSalesChannelsForStockInterface;

/**
* Prevent deleting of Stock which assigned at least one Sales Channel
*/
class AssignedToSalesChannelsStockPlugin
{
/**
* @var GetAssignedSalesChannelsForStockInterface
*/
private $assignedSalesChannelsForStock;

/**
* @param GetAssignedSalesChannelsForStockInterface $assignedSalesChannelsForStock
*/
public function __construct(
GetAssignedSalesChannelsForStockInterface $assignedSalesChannelsForStock
) {
$this->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.'));
}
}
}
Expand Up @@ -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
Expand All @@ -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)
Expand Down
@@ -0,0 +1,52 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\InventoryCatalog\Test\Api\StockRepository;

use Magento\Framework\Webapi\Exception;
use Magento\Framework\Webapi\Rest\Request;
use Magento\TestFramework\TestCase\WebapiAbstract;

class PreventAssignedToSalesChannelsStockDeletingTest extends WebapiAbstract
{
/**
* @magentoApiDataFixture ../../../../app/code/Magento/InventorySalesApi/Test/_files/stock_with_sales_channels.php
* @throws \Exception
*/
public function testCouldNotDeleteException()
{
$stockId = 10;
$serviceInfo = [
'rest' => [
'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;
}
}
}
}
Expand Up @@ -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;

Expand All @@ -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
Expand All @@ -32,8 +30,6 @@ protected function setUp()
}

/**
* Test that default Stock could not be deleted
*
* @throws \Exception
*/
public function testCouldNotDeleteException()
Expand Down
Expand Up @@ -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;

Expand All @@ -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
{
Expand All @@ -28,6 +30,11 @@ class DeleteButton implements ButtonProviderInterface
*/
private $defaultStockProvider;

/**
* @var GetAssignedSalesChannelsForStockInterface
*/
private $assignedSalesChannelsForStock;

/**
* @var RequestInterface
*/
Expand All @@ -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;
}

Expand All @@ -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 [];
}

Expand Down
5 changes: 4 additions & 1 deletion app/code/Magento/InventoryCatalog/etc/di.xml
Expand Up @@ -9,6 +9,9 @@
<preference for="Magento\InventoryCatalog\Api\DefaultStockProviderInterface" type="Magento\InventoryCatalog\Model\DefaultStockProvider"/>
<preference for="Magento\InventoryCatalog\Api\DefaultSourceProviderInterface" type="Magento\InventoryCatalog\Model\DefaultSourceProvider"/>
<type name="Magento\InventoryApi\Api\StockRepositoryInterface">
<plugin name="prevent_default_stock_deletion" type="Magento\InventoryCatalog\Plugin\Model\StockRepositoryPlugin" sortOrder="1"/>
<plugin name="prevent_default_stock_deleting"
type="Magento\InventoryCatalog\Plugin\InventoryApi\StockRepository\PreventDeleting\DefaultStockPlugin"/>
<plugin name="prevent_assigned_to_sales_channels_stock_deleting"
type="Magento\InventoryCatalog\Plugin\InventoryApi\StockRepository\PreventDeleting\AssignedToSalesChannelsStockPlugin"/>
</type>
</config>
Expand Up @@ -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);
2 changes: 1 addition & 1 deletion dev/tests/integration/phpunit.xml.dist
Expand Up @@ -54,7 +54,7 @@
<!-- Semicolon-separated 'glob' patterns, that match global XML configuration files -->
<const name="TESTS_GLOBAL_CONFIG_DIR" value="../../../app/etc"/>
<!-- Whether to cleanup the application before running tests or not -->
<const name="TESTS_CLEANUP" value="0"/>
<const name="TESTS_CLEANUP" value="enabled"/>
<!-- Memory usage and estimated leaks thresholds -->
<!--<const name="TESTS_MEM_USAGE_LIMIT" value="1024M"/>-->
<const name="TESTS_MEM_LEAK_LIMIT" value=""/>
Expand Down