Skip to content

Commit 897c2dd

Browse files
ENGCOM-4781: Fix #14958 - remove sales sequence data on store view delete #22296
2 parents f98f380 + 7d14004 commit 897c2dd

File tree

8 files changed

+359
-29
lines changed

8 files changed

+359
-29
lines changed

app/code/Magento/SalesSequence/Model/ResourceModel/Meta.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ protected function _beforeSave(\Magento\Framework\Model\AbstractModel $object)
132132
|| $object->getData('store_id') === null
133133
|| !$object->getData('sequence_table')
134134
) {
135+
// phpcs:ignore Magento2.Exceptions.DirectThrow
135136
throw new Exception(__('Not enough arguments'));
136137
}
137138

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\SalesSequence\Model\Sequence;
9+
10+
use Magento\Framework\App\ResourceConnection as AppResource;
11+
use Magento\SalesSequence\Model\MetaFactory;
12+
use Magento\SalesSequence\Model\ResourceModel\Meta as ResourceMetadata;
13+
14+
/**
15+
* Delete Sequence by Store.
16+
*/
17+
class DeleteByStore
18+
{
19+
/**
20+
* @var ResourceMetadata
21+
*/
22+
private $resourceMetadata;
23+
24+
/**
25+
* @var MetaFactory
26+
*/
27+
private $metaFactory;
28+
29+
/**
30+
* @var AppResource
31+
*/
32+
private $appResource;
33+
34+
/**
35+
* @param ResourceMetadata $resourceMetadata
36+
* @param MetaFactory $metaFactory
37+
* @param AppResource $appResource
38+
*/
39+
public function __construct(
40+
ResourceMetadata $resourceMetadata,
41+
MetaFactory $metaFactory,
42+
AppResource $appResource
43+
) {
44+
$this->resourceMetadata = $resourceMetadata;
45+
$this->metaFactory = $metaFactory;
46+
$this->appResource = $appResource;
47+
}
48+
49+
/**
50+
* Deletes all sequence linked entites
51+
*
52+
* @param int $storeId
53+
* @return void
54+
* @throws \Exception
55+
*/
56+
public function execute($storeId): void
57+
{
58+
$metadataIds = $this->getMetadataIdsByStoreId($storeId);
59+
$profileIds = $this->getProfileIdsByMetadataIds($metadataIds);
60+
61+
$this->appResource->getConnection()->delete(
62+
$this->appResource->getTableName('sales_sequence_profile'),
63+
['profile_id IN (?)' => $profileIds]
64+
);
65+
66+
foreach ($metadataIds as $metadataId) {
67+
$metadata = $this->metaFactory->create();
68+
$this->resourceMetadata->load($metadata, $metadataId);
69+
if (!$metadata->getId()) {
70+
continue;
71+
}
72+
73+
$this->appResource->getConnection()->dropTable(
74+
$metadata->getSequenceTable()
75+
);
76+
$this->resourceMetadata->delete($metadata);
77+
}
78+
}
79+
80+
/**
81+
* Retrieves Metadata Ids by store id
82+
*
83+
* @param int $storeId
84+
* @return int[]
85+
*/
86+
private function getMetadataIdsByStoreId($storeId)
87+
{
88+
$connection = $this->appResource->getConnection();
89+
$bind = ['store_id' => $storeId];
90+
$select = $connection->select()->from(
91+
$this->appResource->getTableName('sales_sequence_meta'),
92+
['meta_id']
93+
)->where(
94+
'store_id = :store_id'
95+
);
96+
97+
return $connection->fetchCol($select, $bind);
98+
}
99+
100+
/**
101+
* Retrieves Profile Ids by metadata ids
102+
*
103+
* @param int[] $metadataIds
104+
* @return int[]
105+
*/
106+
private function getProfileIdsByMetadataIds(array $metadataIds)
107+
{
108+
$connection = $this->appResource->getConnection();
109+
$select = $connection->select()
110+
->from(
111+
$this->appResource->getTableName('sales_sequence_profile'),
112+
['profile_id']
113+
)->where('meta_id IN (?)', $metadataIds);
114+
115+
return $connection->fetchCol($select);
116+
}
117+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\SalesSequence\Observer;
9+
10+
use Magento\Framework\Event\ObserverInterface;
11+
use Magento\Framework\Event\Observer as EventObserver;
12+
use Magento\SalesSequence\Model\Sequence\DeleteByStore;
13+
14+
/**
15+
* Observer for Sequence Removal.
16+
*/
17+
class SequenceRemovalObserver implements ObserverInterface
18+
{
19+
/**
20+
* @var DeleteByStore
21+
*/
22+
private $deleteByStore;
23+
24+
/**
25+
* @param DeleteByStore $deleteByStore
26+
*/
27+
public function __construct(
28+
DeleteByStore $deleteByStore
29+
) {
30+
$this->deleteByStore = $deleteByStore;
31+
}
32+
33+
/**
34+
* Deletes all sequence linked entities.
35+
*
36+
* @param EventObserver $observer
37+
* @return $this
38+
* @throws \Magento\Framework\Exception\LocalizedException
39+
*/
40+
public function execute(EventObserver $observer)
41+
{
42+
if ($store = $observer->getData('store')) {
43+
$this->deleteByStore->execute($store->getId());
44+
}
45+
46+
return $this;
47+
}
48+
}
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\SalesSequence\Test\Unit\Model\Sequence;
7+
8+
use Magento\Framework\App\ResourceConnection;
9+
use Magento\Framework\DB\Adapter\AdapterInterface;
10+
use Magento\Framework\DB\Select;
11+
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
12+
use Magento\SalesSequence\Model\Meta;
13+
use Magento\SalesSequence\Model\MetaFactory;
14+
use Magento\SalesSequence\Model\ResourceModel\Meta as ResourceMeta;
15+
use Magento\SalesSequence\Model\Sequence\DeleteByStore;
16+
use PHPUnit\Framework\MockObject\MockObject;
17+
use PHPUnit\Framework\TestCase;
18+
19+
/**
20+
* Test for \Magento\SalesSequence\Model\Sequence\DeleteByStore class.
21+
*/
22+
class DeleteByStoreTest extends TestCase
23+
{
24+
/**
25+
* @var DeleteByStore
26+
*/
27+
private $deleteByStore;
28+
29+
/**
30+
* @var ResourceMeta | MockObject
31+
*/
32+
private $resourceSequenceMeta;
33+
34+
/**
35+
* @var Meta | MockObject
36+
*/
37+
private $meta;
38+
39+
/**
40+
* @var MetaFactory | MockObject
41+
*/
42+
private $metaFactory;
43+
44+
/**
45+
* @var AdapterInterface | MockObject
46+
*/
47+
private $connectionMock;
48+
49+
/**
50+
* @var ResourceConnection | MockObject
51+
*/
52+
private $resourceMock;
53+
54+
/**
55+
* @var Select | MockObject
56+
*/
57+
private $select;
58+
59+
protected function setUp()
60+
{
61+
$this->connectionMock = $this->getMockForAbstractClass(
62+
AdapterInterface::class,
63+
[],
64+
'',
65+
false,
66+
false,
67+
true,
68+
['delete', 'query']
69+
);
70+
$this->resourceSequenceMeta = $this->createPartialMock(
71+
ResourceMeta::class,
72+
['load', 'delete']
73+
);
74+
$this->meta = $this->createPartialMock(
75+
Meta::class,
76+
['getSequenceTable']
77+
);
78+
$this->resourceMock = $this->createMock(ResourceConnection::class);
79+
$this->select = $this->createMock(Select::class);
80+
$this->metaFactory = $this->createPartialMock(MetaFactory::class, ['create']);
81+
$this->metaFactory->method('create')->willReturn($this->meta);
82+
83+
$helper = new ObjectManager($this);
84+
$this->deleteByStore = $helper->getObject(
85+
DeleteByStore::class,
86+
[
87+
'resourceMetadata' => $this->resourceSequenceMeta,
88+
'metaFactory' => $this->metaFactory,
89+
'appResource' => $this->resourceMock,
90+
]
91+
);
92+
}
93+
94+
/**
95+
* @throws \Exception
96+
* @SuppressWarnings(PHPMD.UnusedLocalVariable)
97+
*/
98+
public function testExecute()
99+
{
100+
$profileTableName = 'sales_sequence_profile';
101+
$storeId = 1;
102+
$metadataIds = [1, 2];
103+
$profileIds = [10, 11];
104+
$this->resourceMock->method('getTableName')
105+
->willReturnCallback(
106+
static function ($tableName) {
107+
return $tableName;
108+
}
109+
);
110+
$this->resourceMock->method('getConnection')
111+
->willReturn($this->connectionMock);
112+
$this->connectionMock
113+
->method('select')
114+
->willReturn($this->select);
115+
116+
$this->select->method('from')
117+
->willReturn($this->select);
118+
$this->select->method('where')
119+
->willReturn($this->select);
120+
121+
$this->connectionMock->method('fetchCol')
122+
->willReturnCallback(
123+
static function ($arg, $arg2) use ($metadataIds, $profileIds) {
124+
if (array_key_exists('store', $arg2)) {
125+
return $metadataIds;
126+
}
127+
128+
return $profileIds;
129+
}
130+
);
131+
132+
$this->connectionMock->expects($this->once())
133+
->method('delete')
134+
->with($profileTableName, ['profile_id IN (?)' => $profileIds])
135+
->willReturn(2);
136+
$this->resourceSequenceMeta
137+
->method('load')
138+
->willReturn($this->meta);
139+
$this->connectionMock
140+
->method('dropTable')
141+
->willReturn(true);
142+
$this->resourceSequenceMeta
143+
->method('delete')
144+
->willReturn($this->resourceSequenceMeta);
145+
$this->deleteByStore->execute($storeId);
146+
}
147+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?xml version="1.0"?>
2+
<!--
3+
/**
4+
* Copyright © Magento, Inc. All rights reserved.
5+
* See COPYING.txt for license details.
6+
*/
7+
-->
8+
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
9+
<event name="store_delete">
10+
<observer name="magento_sequence" instance="Magento\SalesSequence\Observer\SequenceRemovalObserver" />
11+
</event>
12+
</config>

dev/tests/integration/framework/Magento/TestFramework/Application.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -558,7 +558,7 @@ private function copyAppConfigFiles()
558558
}
559559
}
560560
}
561-
561+
562562
/**
563563
* Copies global configuration file from the tests folder (see TESTS_GLOBAL_CONFIG_FILE)
564564
*

dev/tests/integration/testsuite/Magento/Catalog/Model/Product/UrlTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ public function testGetUrlInStore()
5353
* @magentoConfigFixture fixturestore_store web/unsecure/base_url http://sample-second.com/
5454
* @magentoConfigFixture fixturestore_store web/unsecure/base_link_url http://sample-second.com/
5555
* @magentoDataFixture Magento/Catalog/_files/product_simple_multistore.php
56-
* @magentoDbIsolation disabled
5756
* @dataProvider getUrlsWithSecondStoreProvider
57+
* @magentoDbIsolation disabled
5858
* @magentoAppArea adminhtml
5959
*/
6060
public function testGetUrlInStoreWithSecondStore($storeCode, $expectedProductUrl)

0 commit comments

Comments
 (0)