Skip to content

Commit d01b91a

Browse files
author
Oleksii Korshenko
authored
Merge pull request magento#851 from magento-tango/MAGETWO-61826_63014
Fixed issues: MAGETWO-61826 Cannot save a product with imported custom options MAGETWO-63014 Configurable shows lowest price after product was assigned to another website
2 parents 92d2d0d + 2edfc08 commit d01b91a

File tree

9 files changed

+235
-29
lines changed

9 files changed

+235
-29
lines changed

app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderByBasePrice.php

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -98,15 +98,15 @@ public function build($productId)
9898
->limit(1);
9999
$priceSelect = $this->baseSelectProcessor->process($priceSelect);
100100

101-
$priceSelectDefault = clone $priceSelect;
102-
$priceSelectDefault->where('t.store_id = ?', Store::DEFAULT_STORE_ID);
103-
$select[] = $priceSelectDefault;
104-
105101
if (!$this->catalogHelper->isPriceGlobal()) {
106-
$priceSelect->where('t.store_id = ?', $this->storeManager->getStore()->getId());
107-
$select[] = $priceSelect;
102+
$priceSelectStore = clone $priceSelect;
103+
$priceSelectStore->where('t.store_id = ?', $this->storeManager->getStore()->getId());
104+
$selects[] = $priceSelectStore;
108105
}
109106

110-
return $select;
107+
$priceSelect->where('t.store_id = ?', Store::DEFAULT_STORE_ID);
108+
$selects[] = $priceSelect;
109+
110+
return $selects;
111111
}
112112
}

app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderBySpecialPrice.php

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -142,15 +142,15 @@ public function build($productId)
142142
->limit(1);
143143
$specialPrice = $this->baseSelectProcessor->process($specialPrice);
144144

145-
$specialPriceDefault = clone $specialPrice;
146-
$specialPriceDefault->where('t.store_id = ?', Store::DEFAULT_STORE_ID);
147-
$select[] = $specialPriceDefault;
148-
149145
if (!$this->catalogHelper->isPriceGlobal()) {
150-
$specialPrice->where('t.store_id = ?', $this->storeManager->getStore()->getId());
151-
$select[] = $specialPrice;
146+
$priceSelectStore = clone $specialPrice;
147+
$priceSelectStore->where('t.store_id = ?', $this->storeManager->getStore()->getId());
148+
$selects[] = $priceSelectStore;
152149
}
153150

154-
return $select;
151+
$specialPrice->where('t.store_id = ?', Store::DEFAULT_STORE_ID);
152+
$selects[] = $specialPrice;
153+
154+
return $selects;
155155
}
156156
}

app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderByTierPrice.php

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -100,15 +100,15 @@ public function build($productId)
100100
->limit(1);
101101
$priceSelect = $this->baseSelectProcessor->process($priceSelect);
102102

103-
$priceSelectDefault = clone $priceSelect;
104-
$priceSelectDefault->where('t.website_id = ?', self::DEFAULT_WEBSITE_ID);
105-
$select[] = $priceSelectDefault;
106-
107103
if (!$this->catalogHelper->isPriceGlobal()) {
108-
$priceSelect->where('t.website_id = ?', $this->storeManager->getStore()->getWebsiteId());
109-
$select[] = $priceSelect;
104+
$priceSelectStore = clone $priceSelect;
105+
$priceSelectStore->where('t.website_id = ?', $this->storeManager->getStore()->getWebsiteId());
106+
$selects[] = $priceSelectStore;
110107
}
111108

112-
return $select;
109+
$priceSelect->where('t.website_id = ?', self::DEFAULT_WEBSITE_ID);
110+
$selects[] = $priceSelect;
111+
112+
return $selects;
113113
}
114114
}

app/code/Magento/Catalog/Model/ResourceModel/Product/LinkedProductSelectBuilderComposite.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,11 @@ public function __construct($linkedProductSelectBuilder)
2626
*/
2727
public function build($productId)
2828
{
29-
$select = [];
29+
$selects = [];
3030
foreach ($this->linkedProductSelectBuilder as $productSelectBuilder) {
31-
$select = array_merge($select, $productSelectBuilder->build($productId));
31+
$selects = array_merge($selects, $productSelectBuilder->build($productId));
3232
}
3333

34-
return $select;
34+
return $selects;
3535
}
3636
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<?php
2+
/**
3+
* Copyright © 2013-2017 Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\Catalog\Model\ResourceModel\Product\Website;
7+
8+
use Magento\Catalog\Model\ResourceModel\Product\BaseSelectProcessorInterface;
9+
use Magento\Framework\DB\Select;
10+
use Magento\Framework\EntityManager\MetadataPool;
11+
use Magento\Catalog\Api\Data\ProductInterface;
12+
use Magento\Framework\App\ResourceConnection;
13+
use Magento\Store\Model\StoreManagerInterface;
14+
15+
/**
16+
* Filter products that belongs to current website
17+
*/
18+
class SelectProcessor implements BaseSelectProcessorInterface
19+
{
20+
/**
21+
* @var ResourceConnection
22+
*/
23+
private $resource;
24+
25+
/**
26+
* @var MetadataPool
27+
*/
28+
private $metadataPool;
29+
30+
/**
31+
* @var StoreManagerInterface
32+
*/
33+
private $storeManager;
34+
35+
/**
36+
* @param MetadataPool $metadataPool
37+
* @param ResourceConnection $resource
38+
* @param StoreManagerInterface $storeManager
39+
*/
40+
public function __construct(
41+
MetadataPool $metadataPool,
42+
ResourceConnection $resource,
43+
StoreManagerInterface $storeManager
44+
) {
45+
$this->metadataPool = $metadataPool;
46+
$this->resource = $resource;
47+
$this->storeManager = $storeManager;
48+
}
49+
50+
/**
51+
* Joins website-product relation table to filter products that are only in current website
52+
*
53+
* {@inheritdoc}
54+
*/
55+
public function process(Select $select)
56+
{
57+
$linkField = $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField();
58+
$select->joinInner(
59+
['pw' => $this->resource->getTableName('catalog_product_website')],
60+
'pw.product_id = ' . BaseSelectProcessorInterface::PRODUCT_TABLE_ALIAS . '.' . $linkField
61+
. ' AND pw.website_id = ' . $this->storeManager->getWebsite()->getId(),
62+
[]
63+
);
64+
65+
return $select;
66+
}
67+
}

app/code/Magento/Catalog/etc/di.xml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -851,14 +851,26 @@
851851
<type name="Magento\Quote\Model\Quote\Item\ToOrderItem">
852852
<plugin name="copy_quote_files_to_order" type="Magento\Catalog\Model\Plugin\QuoteItemProductOption"/>
853853
</type>
854-
<preference for="Magento\Catalog\Model\ResourceModel\Product\BaseSelectProcessorInterface" type="Magento\Catalog\Model\ResourceModel\Product\CompositeBaseSelectProcessor" />
854+
<preference for="Magento\Catalog\Model\ResourceModel\Product\BaseSelectProcessorInterface" type="Magento\Catalog\Model\ResourceModel\Product\CompositeWithWebsiteProcessor" />
855855
<type name="Magento\Catalog\Model\ResourceModel\Product\CompositeBaseSelectProcessor">
856856
<arguments>
857857
<argument name="baseSelectProcessors" xsi:type="array">
858858
<item name="status" xsi:type="object">Magento\Catalog\Model\ResourceModel\Product\StatusBaseSelectProcessor</item>
859859
</argument>
860860
</arguments>
861861
</type>
862+
<virtualType name="Magento\Catalog\Model\ResourceModel\Product\CompositeWithWebsiteProcessor" type="Magento\Catalog\Model\ResourceModel\Product\CompositeBaseSelectProcessor">
863+
<arguments>
864+
<argument name="baseSelectProcessors" xsi:type="array">
865+
<item name="website" xsi:type="object">Magento\Catalog\Model\ResourceModel\Product\Website\SelectProcessor</item>
866+
</argument>
867+
</arguments>
868+
</virtualType>
869+
<type name="Magento\Catalog\Model\ResourceModel\Product\Indexer\LinkedProductSelectBuilderByIndexPrice">
870+
<arguments>
871+
<argument name="baseSelectProcessor" xsi:type="object">Magento\Catalog\Model\ResourceModel\Product\CompositeBaseSelectProcessor</argument>
872+
</arguments>
873+
</type>
862874
<type name="Magento\Catalog\Model\Product\Price\CostStorage">
863875
<arguments>
864876
<argument name="allowedProductTypes" xsi:type="array">

app/code/Magento/Catalog/view/adminhtml/web/js/components/dynamic-rows-import-custom-options.js

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,17 +54,27 @@ define([
5454
if (!data) {
5555
return;
5656
}
57-
data.each(function (item) {
57+
_.each(data, function (item) {
5858
if (!item.options) {
5959
return;
6060
}
61-
item.options.each(function (option) {
61+
_.each(item.options, function (option) {
6262
currentOption = utils.copy(option);
6363

6464
if (currentOption.hasOwnProperty('sort_order')) {
6565
delete currentOption['sort_order'];
6666
}
67-
currentOption['option_id'] = ++maxId;
67+
68+
if (currentOption.hasOwnProperty('option_id')) {
69+
delete currentOption['option_id'];
70+
}
71+
72+
if (currentOption.values.length > 0) {
73+
_.each(currentOption.values, function (optionValue) {
74+
delete optionValue['option_id'];
75+
delete optionValue['option_type_id'];
76+
});
77+
}
6878
options.push(currentOption);
6979
});
7080
});
@@ -73,7 +83,7 @@ define([
7383
return;
7484
}
7585
this.cacheGridData = options;
76-
options.each(function (opt) {
86+
_.each(options, function (opt) {
7787
this.mappingValue(opt);
7888
}, this);
7989

dev/tests/integration/testsuite/Magento/ConfigurableProduct/Pricing/Price/LowestPriceOptionProviderTest.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,4 +127,32 @@ public function testGetProductsIfOneOfChildIsOutOfStock()
127127
$lowestPriceChildrenProduct = reset($lowestPriceChildrenProducts);
128128
$this->assertEquals(20, $lowestPriceChildrenProduct->getPrice());
129129
}
130+
131+
/**
132+
* @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php
133+
* @magentoDataFixture Magento/Store/_files/website.php
134+
*/
135+
public function testGetProductsIfOneOfChildrenIsAssignedToOtherWebsite()
136+
{
137+
$configurableProduct = $this->productRepository->getById(1, false, null, true);
138+
$lowestPriceChildrenProducts = $this->lowestPriceOptionsProvider->getProducts($configurableProduct);
139+
$this->assertCount(1, $lowestPriceChildrenProducts);
140+
$lowestPriceChildrenProduct = reset($lowestPriceChildrenProducts);
141+
$this->assertEquals(10, $lowestPriceChildrenProduct->getPrice());
142+
143+
/** @var \Magento\Store\Api\WebsiteRepositoryInterface $webSiteRepository */
144+
$webSiteRepository = Bootstrap::getObjectManager()->get(\Magento\Store\Api\WebsiteRepositoryInterface::class);
145+
$website = $webSiteRepository->get('test');
146+
147+
$attributes = $lowestPriceChildrenProduct->getExtensionAttributes();
148+
$attributes->setWebsiteIds([$website->getId()]);
149+
150+
$lowestPriceChildrenProduct->setExtensionAttributes($attributes);
151+
$this->productRepository->save($lowestPriceChildrenProduct);
152+
153+
$lowestPriceChildrenProducts = $this->lowestPriceOptionsProvider->getProducts($configurableProduct);
154+
$this->assertCount(1, $lowestPriceChildrenProducts);
155+
$lowestPriceChildrenProduct = reset($lowestPriceChildrenProducts);
156+
$this->assertEquals(20, $lowestPriceChildrenProduct->getPrice());
157+
}
130158
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/**
2+
* Copyright © 2013-2017 Magento, Inc. All rights reserved.
3+
* See COPYING.txt for license details.
4+
*/
5+
6+
/*eslint max-nested-callbacks: 0*/
7+
8+
define([
9+
'Magento_Catalog/js/components/dynamic-rows-import-custom-options'
10+
], function (DynamicRows) {
11+
'use strict';
12+
13+
describe('Magento_Catalog/js/components/dynamic-rows-import-custom-options', function () {
14+
var model, data;
15+
16+
beforeEach(function () {
17+
model = new DynamicRows({
18+
index: 'dynamic_rows',
19+
name: 'dynamic_rows',
20+
indexField: 'id',
21+
dataScope: '',
22+
rows: [{
23+
identifier: 'row'
24+
}]
25+
});
26+
data = [{
27+
'options': [
28+
{
29+
'sort_order': 1,
30+
'option_id': 1,
31+
'option_type_id': 1,
32+
'values': [{
33+
'option_id': 1,
34+
'option_type_id': 1,
35+
'some_fake_value': 1
36+
}]
37+
},
38+
{
39+
'sort_order': 2,
40+
'option_id': 2,
41+
'option_type_id': 2,
42+
'values': [{
43+
'option_id': 2,
44+
'option_type_id': 2
45+
}]
46+
}
47+
]
48+
}];
49+
model.source = {
50+
set: jasmine.createSpy()
51+
};
52+
model.insertData = jasmine.createSpy().and.returnValue([]);
53+
});
54+
55+
describe('Check processingInsertData', function () {
56+
it('Check with empty data.', function () {
57+
model.processingInsertData();
58+
expect(model.cacheGridData).toEqual([]);
59+
expect(model.insertData).not.toHaveBeenCalled();
60+
});
61+
62+
it('Check with empty options data.', function () {
63+
data = [{
64+
'options': []
65+
}];
66+
model.processingInsertData(data);
67+
expect(model.cacheGridData).toEqual([]);
68+
expect(model.insertData).not.toHaveBeenCalled();
69+
});
70+
71+
it('Check with fake imported custom options data.', function () {
72+
model.processingInsertData(data);
73+
expect(model.insertData).toHaveBeenCalled();
74+
expect(model.cacheGridData[0]).toEqual({
75+
'option_type_id': 1,
76+
'position': 1,
77+
'values': [{
78+
'some_fake_value': 1
79+
}]
80+
});
81+
expect(model.cacheGridData[1]).toEqual({
82+
'option_type_id': 2,
83+
'position': 2,
84+
'values': [{}]
85+
});
86+
});
87+
});
88+
});
89+
});

0 commit comments

Comments
 (0)