Skip to content

Commit babf1f2

Browse files
committed
Merge remote-tracking branch 'origin/2.2-develop-pr72' into 2.2-develop-pr72
2 parents 13884c3 + 3d4e815 commit babf1f2

File tree

16 files changed

+394
-23
lines changed

16 files changed

+394
-23
lines changed

app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
-->
88

99
<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
10-
xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd">
10+
xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd">
1111
<!--Filter the product grid by new from date filter-->
1212
<actionGroup name="filterProductGridBySetNewFromDate">
1313
<conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFilters"/>
@@ -33,6 +33,7 @@
3333
<actionGroup name="deleteProductUsingProductGrid">
3434
<arguments>
3535
<argument name="product"/>
36+
<argument name="productCount" type="string" defaultValue="1"/>
3637
</arguments>
3738
<!--TODO use other action group for filtering grid when MQE-539 is implemented -->
3839
<amOnPage url="{{AdminProductIndexPage.url}}" stepKey="visitAdminProductPage"/>
@@ -48,7 +49,7 @@
4849
<click selector="{{AdminProductGridSection.bulkActionOption('Delete')}}" stepKey="clickDeleteAction"/>
4950
<waitForElementVisible selector="{{AdminProductGridConfirmActionSection.title}}" stepKey="waitForConfirmModal"/>
5051
<click selector="{{AdminProductGridConfirmActionSection.ok}}" stepKey="confirmProductDelete"/>
51-
<see selector="{{AdminMessagesSection.success}}" userInput="A total of 1 record(s) have been deleted." stepKey="seeSuccessMessage"/>
52+
<see selector="{{AdminMessagesSection.success}}" userInput="A total of {{productCount}} record(s) have been deleted." stepKey="seeSuccessMessage"/>
5253
<conditionalClick selector="{{AdminProductGridFilterSection.clearFilters}}" dependentSelector="{{AdminProductGridFilterSection.clearFilters}}" visible="true" stepKey="clickClearFiltersInitial2"/>
5354
</actionGroup>
5455

app/code/Magento/Catalog/Test/Mftf/Data/ProductConfigurableAttributeData.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@
77
-->
88

99
<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
10-
xsi:noNamespaceSchemaLocation="../../../../../../../dev/tests/acceptance/vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/DataGenerator/etc/dataProfileSchema.xsd">
10+
xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/dataProfileSchema.xsd">
1111
<entity name="colorProductAttribute" type="product_attribute">
1212
<data key="default_label" unique="suffix">Color</data>
1313
<data key="attribute_quantity">1</data>
14+
<data key="input_type">Dropdown</data>
1415
</entity>
1516
<entity name="colorProductAttribute1" type="product_attribute">
1617
<data key="name" unique="suffix">White</data>

app/code/Magento/Catalog/Test/Mftf/Section/AdminProductFormSection.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,5 +188,6 @@
188188
</section>
189189
<section name="AdminChooseAffectedAttributeSetPopup">
190190
<element name="confirm" type="button" selector="button[data-index='confirm_button']" timeout="30"/>
191+
<element name="closePopUp" type="button" selector=".modal-popup._show [data-role='closeBtn']" timeout="30"/>
191192
</section>
192193
</sections>

app/code/Magento/CatalogInventory/Observer/CancelOrderItemObserver.php

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,21 @@
66

77
namespace Magento\CatalogInventory\Observer;
88

9-
use Magento\Framework\Event\ObserverInterface;
109
use Magento\CatalogInventory\Api\StockManagementInterface;
10+
use Magento\CatalogInventory\Model\Configuration;
1111
use Magento\Framework\Event\Observer as EventObserver;
12+
use Magento\Framework\Event\ObserverInterface;
1213

1314
/**
1415
* Catalog inventory module observer
1516
*/
1617
class CancelOrderItemObserver implements ObserverInterface
1718
{
19+
/**
20+
* @var \Magento\CatalogInventory\Model\Configuration
21+
*/
22+
protected $configuration;
23+
1824
/**
1925
* @var StockManagementInterface
2026
*/
@@ -26,13 +32,16 @@ class CancelOrderItemObserver implements ObserverInterface
2632
protected $priceIndexer;
2733

2834
/**
35+
* @param Configuration $configuration
2936
* @param StockManagementInterface $stockManagement
3037
* @param \Magento\Catalog\Model\Indexer\Product\Price\Processor $priceIndexer
3138
*/
3239
public function __construct(
40+
Configuration $configuration,
3341
StockManagementInterface $stockManagement,
3442
\Magento\Catalog\Model\Indexer\Product\Price\Processor $priceIndexer
3543
) {
44+
$this->configuration = $configuration;
3645
$this->stockManagement = $stockManagement;
3746
$this->priceIndexer = $priceIndexer;
3847
}
@@ -49,7 +58,8 @@ public function execute(EventObserver $observer)
4958
$item = $observer->getEvent()->getItem();
5059
$children = $item->getChildrenItems();
5160
$qty = $item->getQtyOrdered() - max($item->getQtyShipped(), $item->getQtyInvoiced()) - $item->getQtyCanceled();
52-
if ($item->getId() && $item->getProductId() && empty($children) && $qty) {
61+
if ($item->getId() && $item->getProductId() && empty($children) && $qty && $this->configuration
62+
->getCanBackInStock()) {
5363
$this->stockManagement->backItemQty($item->getProductId(), $qty, $item->getStore()->getWebsiteId());
5464
}
5565
$this->priceIndexer->reindexRow($item->getProductId());

app/code/Magento/CatalogUrlRewrite/Observer/CategoryProcessUrlRewriteSavingObserver.php

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -100,16 +100,19 @@ public function execute(\Magento\Framework\Event\Observer $observer)
100100
}
101101

102102
$mapsGenerated = false;
103-
if ($category->dataHasChangedFor('url_key')
104-
|| $category->dataHasChangedFor('is_anchor')
105-
|| $category->getChangedProductIds()
106-
) {
103+
if ($this->isCategoryHasChanged($category)) {
107104
if ($category->dataHasChangedFor('url_key')) {
108105
$categoryUrlRewriteResult = $this->categoryUrlRewriteGenerator->generate($category);
109106
$this->urlRewriteBunchReplacer->doBunchReplace($categoryUrlRewriteResult);
110107
}
111-
$productUrlRewriteResult = $this->urlRewriteHandler->generateProductUrlRewrites($category);
112-
$this->urlRewriteBunchReplacer->doBunchReplace($productUrlRewriteResult);
108+
if ($this->isChangedOnlyProduct($category)) {
109+
$productUrlRewriteResult =
110+
$this->urlRewriteHandler->updateProductUrlRewritesForChangedProduct($category);
111+
$this->urlRewriteBunchReplacer->doBunchReplace($productUrlRewriteResult);
112+
} else {
113+
$productUrlRewriteResult = $this->urlRewriteHandler->generateProductUrlRewrites($category);
114+
$this->urlRewriteBunchReplacer->doBunchReplace($productUrlRewriteResult);
115+
}
113116
$mapsGenerated = true;
114117
}
115118

@@ -120,8 +123,42 @@ public function execute(\Magento\Framework\Event\Observer $observer)
120123
}
121124

122125
/**
123-
* in case store_id is not set for category then we can assume that it was passed through product import.
124-
* store group must have only one root category, so receiving category's path and checking if one of it parts
126+
* Check is category changed changed.
127+
*
128+
* @param Category $category
129+
* @return bool
130+
*/
131+
private function isCategoryHasChanged(Category $category): bool
132+
{
133+
if ($category->dataHasChangedFor('url_key')
134+
|| $category->dataHasChangedFor('is_anchor')
135+
|| !empty($category->getChangedProductIds())) {
136+
return true;
137+
}
138+
139+
return false;
140+
}
141+
142+
/**
143+
* Check is only product changed.
144+
*
145+
* @param Category $category
146+
* @return bool
147+
*/
148+
private function isChangedOnlyProduct(Category $category): bool
149+
{
150+
if (!empty($category->getChangedProductIds())
151+
&& !$category->dataHasChangedFor('is_anchor')
152+
&& !$category->dataHasChangedFor('url_key')) {
153+
return true;
154+
}
155+
156+
return false;
157+
}
158+
159+
/**
160+
* In case store_id is not set for category then we can assume that it was passed through product import.
161+
* Store group must have only one root category, so receiving category's path and checking if one of it parts
125162
* is the root category for store group, we can set default_store_id value from it to category.
126163
* it prevents urls duplication for different stores
127164
* ("Default Category/category/sub" and "Default Category2/category/sub")

app/code/Magento/CatalogUrlRewrite/Observer/UrlRewriteHandler.php

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
use Magento\UrlRewrite\Service\V1\Data\UrlRewrite;
2525

2626
/**
27+
* Class for management url rewrites.
28+
*
2729
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
2830
*/
2931
class UrlRewriteHandler
@@ -125,7 +127,7 @@ public function generateProductUrlRewrites(Category $category): array
125127
{
126128
$mergeDataProvider = clone $this->mergeDataProviderPrototype;
127129
$this->isSkippedProduct[$category->getEntityId()] = [];
128-
$saveRewriteHistory = $category->getData('save_rewrites_history');
130+
$saveRewriteHistory = (bool)$category->getData('save_rewrites_history');
129131
$storeId = (int)$category->getStoreId();
130132

131133
if ($category->getChangedProductIds()) {
@@ -156,6 +158,30 @@ public function generateProductUrlRewrites(Category $category): array
156158
}
157159

158160
/**
161+
* Update product url rewrites for changed product.
162+
*
163+
* @param Category $category
164+
* @return array
165+
*/
166+
public function updateProductUrlRewritesForChangedProduct(Category $category): array
167+
{
168+
$mergeDataProvider = clone $this->mergeDataProviderPrototype;
169+
$this->isSkippedProduct[$category->getEntityId()] = [];
170+
$saveRewriteHistory = (bool)$category->getData('save_rewrites_history');
171+
$storeIds = $this->getCategoryStoreIds($category);
172+
173+
if ($category->getChangedProductIds()) {
174+
foreach ($storeIds as $storeId) {
175+
$this->generateChangedProductUrls($mergeDataProvider, $category, (int)$storeId, $saveRewriteHistory);
176+
}
177+
}
178+
179+
return $mergeDataProvider->getData();
180+
}
181+
182+
/**
183+
* Delete category rewrites for children.
184+
*
159185
* @param Category $category
160186
* @return void
161187
*/
@@ -184,6 +210,8 @@ public function deleteCategoryRewritesForChildren(Category $category)
184210
}
185211

186212
/**
213+
* Get category products url rewrites.
214+
*
187215
* @param Category $category
188216
* @param int $storeId
189217
* @param bool $saveRewriteHistory
@@ -230,15 +258,15 @@ private function getCategoryProductsUrlRewrites(
230258
*
231259
* @param MergeDataProvider $mergeDataProvider
232260
* @param Category $category
233-
* @param Product $product
234261
* @param int $storeId
235-
* @param $saveRewriteHistory
262+
* @param bool $saveRewriteHistory
263+
* @return void
236264
*/
237265
private function generateChangedProductUrls(
238266
MergeDataProvider $mergeDataProvider,
239267
Category $category,
240268
int $storeId,
241-
$saveRewriteHistory
269+
bool $saveRewriteHistory
242270
) {
243271
$this->isSkippedProduct[$category->getEntityId()] = $category->getAffectedProductIds();
244272

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
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\CatalogUrlRewrite\Test\Unit\Observer;
9+
10+
use Magento\Catalog\Model\Category;
11+
use Magento\CatalogUrlRewrite\Model\CategoryUrlRewriteGenerator;
12+
use Magento\CatalogUrlRewrite\Model\Map\DatabaseMapPool;
13+
use Magento\CatalogUrlRewrite\Model\Map\DataCategoryUrlRewriteDatabaseMap;
14+
use Magento\CatalogUrlRewrite\Model\Map\DataProductUrlRewriteDatabaseMap;
15+
use Magento\CatalogUrlRewrite\Model\UrlRewriteBunchReplacer;
16+
use Magento\CatalogUrlRewrite\Observer\CategoryProcessUrlRewriteSavingObserver;
17+
use Magento\CatalogUrlRewrite\Observer\UrlRewriteHandler;
18+
use Magento\Framework\Event;
19+
use Magento\Framework\Event\Observer;
20+
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
21+
use Magento\Store\Model\ResourceModel\Group\CollectionFactory;
22+
23+
/**
24+
* Tests Magento\CatalogUrlRewrite\Observer\CategoryProcessUrlRewriteSavingObserver.
25+
*/
26+
class CategoryProcessUrlRewriteSavingObserverTest extends \PHPUnit\Framework\TestCase
27+
{
28+
/**
29+
* @var CategoryProcessUrlRewriteSavingObserver
30+
*/
31+
private $observer;
32+
33+
/**
34+
* @var CategoryUrlRewriteGenerator|\PHPUnit_Framework_MockObject_MockObject
35+
*/
36+
private $categoryUrlRewriteGeneratorMock;
37+
38+
/**
39+
* @var UrlRewriteHandler|\PHPUnit_Framework_MockObject_MockObject
40+
*/
41+
private $urlRewriteHandlerMock;
42+
43+
/**
44+
* @var UrlRewriteBunchReplacer|\PHPUnit_Framework_MockObject_MockObject $urlRewriteMock
45+
*/
46+
private $urlRewriteBunchReplacerMock;
47+
48+
/**
49+
* @var DatabaseMapPool|\PHPUnit_Framework_MockObject_MockObject
50+
*/
51+
private $databaseMapPoolMock;
52+
53+
/**
54+
* @inheritdoc
55+
*/
56+
protected function setUp()
57+
{
58+
$objectManager = new ObjectManager($this);
59+
$this->categoryUrlRewriteGeneratorMock = $this->createMock(CategoryUrlRewriteGenerator::class);
60+
$this->urlRewriteHandlerMock = $this->createMock(UrlRewriteHandler::class);
61+
$this->urlRewriteBunchReplacerMock = $this->createMock(UrlRewriteBunchReplacer::class);
62+
$this->databaseMapPoolMock = $this->createMock(DatabaseMapPool::class);
63+
/** @var CollectionFactory|\PHPUnit_Framework_MockObject_MockObject $storeGroupFactoryMock */
64+
$storeGroupCollectionFactoryMock = $this->createMock(CollectionFactory::class);
65+
66+
$this->observer = $objectManager->getObject(
67+
CategoryProcessUrlRewriteSavingObserver::class,
68+
[
69+
'categoryUrlRewriteGenerator' => $this->categoryUrlRewriteGeneratorMock,
70+
'urlRewriteHandler' => $this->urlRewriteHandlerMock,
71+
'urlRewriteBunchReplacer' => $this->urlRewriteBunchReplacerMock,
72+
'databaseMapPool' => $this->databaseMapPoolMock,
73+
'dataUrlRewriteClassNames' => [
74+
DataCategoryUrlRewriteDatabaseMap::class,
75+
DataProductUrlRewriteDatabaseMap::class
76+
],
77+
'storeGroupFactory' => $storeGroupCollectionFactoryMock,
78+
]
79+
);
80+
}
81+
82+
/**
83+
* Covers case when only associated products are changed for category.
84+
*
85+
* @return void
86+
*/
87+
public function testExecuteCategoryOnlyProductHasChanged()
88+
{
89+
$productId = 120;
90+
$productRewrites = ['product-url-rewrite'];
91+
92+
/** @var Observer|\PHPUnit_Framework_MockObject_MockObject $observerMock */
93+
$observerMock = $this->createMock(Observer::class);
94+
/** @var Event|\PHPUnit_Framework_MockObject_MockObject $eventMock */
95+
$eventMock = $this->createMock(Event::class);
96+
/** @var Category|\PHPUnit_Framework_MockObject_MockObject $categoryMock */
97+
$categoryMock = $this->createPartialMock(
98+
Category::class,
99+
[
100+
'hasData',
101+
'dataHasChangedFor',
102+
'getChangedProductIds',
103+
]
104+
);
105+
106+
$categoryMock->expects($this->once())->method('hasData')->with('store_id')->willReturn(true);
107+
$categoryMock->expects($this->exactly(2))->method('getChangedProductIds')->willReturn([$productId]);
108+
$categoryMock->expects($this->any())->method('dataHasChangedFor')
109+
->willReturnMap(
110+
[
111+
['url_key', false],
112+
['is_anchor', false],
113+
]
114+
);
115+
$eventMock->expects($this->once())->method('getData')->with('category')->willReturn($categoryMock);
116+
$observerMock->expects($this->once())->method('getEvent')->willReturn($eventMock);
117+
118+
$this->urlRewriteHandlerMock->expects($this->once())
119+
->method('updateProductUrlRewritesForChangedProduct')
120+
->with($categoryMock)
121+
->willReturn($productRewrites);
122+
123+
$this->urlRewriteBunchReplacerMock->expects($this->once())
124+
->method('doBunchReplace')
125+
->with($productRewrites, 10000);
126+
127+
$this->observer->execute($observerMock);
128+
}
129+
}

0 commit comments

Comments
 (0)