Skip to content

Commit e035365

Browse files
committed
Merge branch 'develop' of github.corp.magento.com:magento2/magento2ce into MAGETWO-48544
2 parents 05e63b2 + b3f5588 commit e035365

File tree

24 files changed

+591
-374
lines changed

24 files changed

+591
-374
lines changed

app/code/Magento/Bundle/Model/Product/CopyConstructor/Bundle.php

Lines changed: 12 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -5,58 +5,30 @@
55
*/
66
namespace Magento\Bundle\Model\Product\CopyConstructor;
77

8+
use Magento\Catalog\Model\Product;
9+
use Magento\Catalog\Model\Product\Type;
10+
811
class Bundle implements \Magento\Catalog\Model\Product\CopyConstructorInterface
912
{
1013
/**
1114
* Duplicating bundle options and selections
1215
*
13-
* @param \Magento\Catalog\Model\Product $product
14-
* @param \Magento\Catalog\Model\Product $duplicate
16+
* @param Product $product
17+
* @param Product $duplicate
1518
* @return void
1619
*/
17-
public function build(\Magento\Catalog\Model\Product $product, \Magento\Catalog\Model\Product $duplicate)
20+
public function build(Product $product, Product $duplicate)
1821
{
19-
if ($product->getTypeId() != \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE) {
22+
if ($product->getTypeId() != Type::TYPE_BUNDLE) {
2023
//do nothing if not bundle
2124
return;
2225
}
2326

24-
$product->getTypeInstance()->setStoreFilter($product->getStoreId(), $product);
25-
$optionCollection = $product->getTypeInstance()->getOptionsCollection($product);
26-
$selectionCollection = $product->getTypeInstance()->getSelectionsCollection(
27-
$product->getTypeInstance()->getOptionsIds($product),
28-
$product
29-
);
30-
$optionCollection->appendSelections($selectionCollection);
31-
32-
$optionRawData = [];
33-
$selectionRawData = [];
34-
35-
$i = 0;
36-
foreach ($optionCollection as $option) {
37-
$optionRawData[$i] = [
38-
'required' => $option->getData('required'),
39-
'position' => $option->getData('position'),
40-
'type' => $option->getData('type'),
41-
'title' => $option->getData('title') ? $option->getData('title') : $option->getData('default_title'),
42-
'delete' => '',
43-
];
44-
foreach ($option->getSelections() as $selection) {
45-
$selectionRawData[$i][] = [
46-
'product_id' => $selection->getProductId(),
47-
'position' => $selection->getPosition(),
48-
'is_default' => $selection->getIsDefault(),
49-
'selection_price_type' => $selection->getSelectionPriceType(),
50-
'selection_price_value' => $selection->getSelectionPriceValue(),
51-
'selection_qty' => $selection->getSelectionQty(),
52-
'selection_can_change_qty' => $selection->getSelectionCanChangeQty(),
53-
'delete' => '',
54-
];
55-
}
56-
$i++;
27+
$bundleOptions = $product->getExtensionAttributes()->getBundleProductOptions();
28+
$duplicatedBundleOptions = [];
29+
foreach ($bundleOptions as $key => $bundleOption) {
30+
$duplicatedBundleOptions[$key] = clone $bundleOption;
5731
}
58-
59-
$duplicate->setBundleOptionsData($optionRawData);
60-
$duplicate->setBundleSelectionsData($selectionRawData);
32+
$duplicate->getExtensionAttributes()->setBundleProductOptions($duplicatedBundleOptions);
6133
}
6234
}

app/code/Magento/Bundle/Test/Unit/Model/Product/CopyConstructor/BundleTest.php

Lines changed: 58 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -5,48 +5,38 @@
55
*/
66
namespace Magento\Bundle\Test\Unit\Model\Product\CopyConstructor;
77

8+
use Magento\Bundle\Api\Data\BundleOptionInterface;
9+
use Magento\Bundle\Model\Product\CopyConstructor\Bundle;
10+
use Magento\Catalog\Api\Data\ProductExtensionInterface;
11+
use Magento\Catalog\Model\Product;
12+
use Magento\Catalog\Model\Product\Type;
13+
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
14+
815
class BundleTest extends \PHPUnit_Framework_TestCase
916
{
1017
/**
11-
* @var PHPUnit_Framework_MockObject_MockObject
12-
*/
13-
protected $product;
14-
15-
/**
16-
* @var PHPUnit_Framework_MockObject_MockObject
17-
*/
18-
protected $duplicate;
19-
20-
/**
21-
* @var \Magento\Bundle\Model\Product\CopyConstructor\Bundle
18+
* @var Bundle
2219
*/
2320
protected $model;
2421

25-
/**
26-
* @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager
27-
*/
28-
protected $objectManager;
29-
3022
protected function setUp()
3123
{
32-
// Magento\Catalog\Model\Product $product, \Magento\Catalog\Model\Product $duplicate
33-
$this->product = $this->getMock('Magento\Catalog\Model\Product', [], [], '', false);
34-
$this->duplicate = $this->getMock(
35-
'Magento\Catalog\Model\Product',
36-
['setBundleOptionsData', 'setBundleSelectionsData', '__wakeup'],
37-
[],
38-
'',
39-
false
40-
);
41-
$this->model = new \Magento\Bundle\Model\Product\CopyConstructor\Bundle();
42-
$this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
24+
$objectManager = new ObjectManager($this);
25+
$this->model = $objectManager->getObject(Bundle::class);
4326
}
4427

4528
public function testBuildNegative()
4629
{
47-
$this->product->expects($this->once())->method('getTypeId')->will($this->returnValue('other product'));
48-
$this->product->expects($this->never())->method('getTypeInstance');
49-
$this->model->build($this->product, $this->duplicate);
30+
$product = $this->getMockBuilder(Product::class)
31+
->disableOriginalConstructor()
32+
->getMock();
33+
$duplicate = $this->getMockBuilder(Product::class)
34+
->disableOriginalConstructor()
35+
->getMock();
36+
$product->expects($this->once())
37+
->method('getTypeId')
38+
->willReturn('other product type');
39+
$this->model->build($product, $duplicate);
5040
}
5141

5242
/**
@@ -55,112 +45,48 @@ public function testBuildNegative()
5545
*/
5646
public function testBuildPositive()
5747
{
58-
//prepare mocks and data samples
59-
$instance = $this->getMock(
60-
'Magento\Bundle\Model\Product\Type',
61-
['setStoreFilter', 'getOptionsCollection', 'getSelectionsCollection', 'getOptionsIds'],
62-
[],
63-
'',
64-
false
65-
);
66-
$option = $this->getMock(
67-
'Magento\Bundle\Model\Option',
68-
['getSelections', '__wakeup', 'getData'],
69-
[],
70-
'',
71-
false
72-
);
73-
$options = [$option];
74-
$optionCollection = $this->objectManager->getCollectionMock(
75-
'Magento\Bundle\Model\ResourceModel\Option\Collection',
76-
$options
77-
);
78-
$optionRawData = [
79-
['required' => true, 'position' => 100, 'type' => 'someType', 'title' => 'title', 'delete' => ''],
80-
];
81-
$selectionRawData = [
82-
[
83-
[
84-
'product_id' => 123,
85-
'position' => 500,
86-
'is_default' => false,
87-
'selection_price_type' => 'priceType',
88-
'selection_price_value' => 'priceVal',
89-
'selection_qty' => 21,
90-
'selection_can_change_qty' => 11,
91-
'delete' => '',
92-
],
93-
],
94-
];
95-
96-
$selection = $this->getMock(
97-
'Magento\Bundle\Model\Selection',
98-
[
99-
'getProductId',
100-
'getPosition',
101-
'getIsDefault',
102-
'getSelectionPriceType',
103-
'getSelectionPriceValue',
104-
'getSelectionQty',
105-
'getSelectionCanChangeQty',
106-
'__wakeup'
107-
],
108-
[],
109-
'',
110-
false
111-
);
112-
$selections = [$selection];
113-
$selectionCollection = $this->getMock(
114-
'Magento\Bundle\Model\ResourceModel\Selection\Collection',
115-
[],
116-
[],
117-
'',
118-
false
119-
);
48+
$product = $this->getMockBuilder(Product::class)
49+
->disableOriginalConstructor()
50+
->getMock();
51+
$extensionAttributesProduct = $this->getMockBuilder(ProductExtensionInterface::class)
52+
->setMethods(['getBundleProductOptions'])
53+
->disableOriginalConstructor()
54+
->getMockForAbstractClass();
12055

121-
// method flow
122-
$this->product->expects($this->once())->method('getTypeId')->will($this->returnValue('bundle'));
123-
$this->product->expects($this->any())->method('getTypeInstance')->will($this->returnValue($instance));
124-
$instance->expects($this->once())->method('setStoreFilter')->with(null, $this->product);
125-
$instance->expects(
126-
$this->once()
127-
)->method(
128-
'getOptionsCollection'
129-
)->with(
130-
$this->product
131-
)->will(
132-
$this->returnValue($optionCollection)
133-
);
134-
$instance->expects(
135-
$this->once()
136-
)->method(
137-
'getSelectionsCollection'
138-
)->with(
139-
null,
140-
$this->product
141-
)->will(
142-
$this->returnValue($selectionCollection)
143-
);
144-
$optionCollection->expects($this->once())->method('appendSelections')->with($selectionCollection);
145-
$option->expects($this->any())->method('getSelections')->will($this->returnValue($selections));
56+
$product->expects($this->once())
57+
->method('getTypeId')
58+
->willReturn(Type::TYPE_BUNDLE);
59+
$product->expects($this->once())
60+
->method('getExtensionAttributes')
61+
->willReturn($extensionAttributesProduct);
14662

147-
$option->expects($this->at(0))->method('getData')->with('required')->will($this->returnValue(true));
148-
$option->expects($this->at(1))->method('getData')->with('position')->will($this->returnValue(100));
149-
$option->expects($this->at(2))->method('getData')->with('type')->will($this->returnValue('someType'));
150-
$option->expects($this->at(3))->method('getData')->with('title')->will($this->returnValue('title'));
151-
$option->expects($this->at(4))->method('getData')->with('title')->will($this->returnValue('title'));
63+
$bundleOptions = [
64+
$this->getMockBuilder(BundleOptionInterface::class)
65+
->disableOriginalConstructor()
66+
->getMockForAbstractClass(),
67+
$this->getMockBuilder(BundleOptionInterface::class)
68+
->disableOriginalConstructor()
69+
->getMockForAbstractClass()
70+
];
71+
$extensionAttributesProduct->expects($this->once())
72+
->method('getBundleProductOptions')
73+
->willReturn($bundleOptions);
15274

153-
$selection->expects($this->once())->method('getProductId')->will($this->returnValue(123));
154-
$selection->expects($this->once())->method('getPosition')->will($this->returnValue(500));
155-
$selection->expects($this->once())->method('getIsDefault')->will($this->returnValue(false));
156-
$selection->expects($this->once())->method('getSelectionPriceType')->will($this->returnValue('priceType'));
157-
$selection->expects($this->once())->method('getSelectionPriceValue')->will($this->returnValue('priceVal'));
158-
$selection->expects($this->once())->method('getSelectionQty')->will($this->returnValue(21));
159-
$selection->expects($this->once())->method('getSelectionCanChangeQty')->will($this->returnValue(11));
75+
$duplicate = $this->getMockBuilder(Product::class)
76+
->disableOriginalConstructor()
77+
->getMock();
78+
$extensionAttributesDuplicate = $this->getMockBuilder(ProductExtensionInterface::class)
79+
->setMethods(['setBundleProductOptions'])
80+
->disableOriginalConstructor()
81+
->getMockForAbstractClass();
16082

161-
$this->duplicate->expects($this->once())->method('setBundleOptionsData')->with($optionRawData);
162-
$this->duplicate->expects($this->once())->method('setBundleSelectionsData')->with($selectionRawData);
83+
$duplicate->expects($this->once())
84+
->method('getExtensionAttributes')
85+
->willReturn($extensionAttributesDuplicate);
86+
$extensionAttributesDuplicate->expects($this->once())
87+
->method('setBundleProductOptions')
88+
->withConsecutive([$bundleOptions]);
16389

164-
$this->model->build($this->product, $this->duplicate);
90+
$this->model->build($product, $duplicate);
16591
}
16692
}

app/code/Magento/Catalog/Model/ProductRepository.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,10 @@ public function save(\Magento\Catalog\Api\Data\ProductInterface $product, $saveO
494494
$this->processMediaGallery($product, $productDataArray['media_gallery_entries']);
495495
}
496496

497+
if (!$product->getOptionsReadonly()) {
498+
$product->setCanSaveCustomOptions(true);
499+
}
500+
497501
$validationResult = $this->resourceModel->validate($product);
498502
if (true !== $validationResult) {
499503
throw new \Magento\Framework\Exception\CouldNotSaveException(

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
</attribute>
3232
<attribute code="category_ids">
3333
<field code="is_searchable" locked="true" />
34+
<field code="used_for_sort_by" locked="true" />
3435
</attribute>
3536
<attribute code="media_gallery">
3637
<field code="is_searchable" locked="true" />

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,16 @@
66
*/
77
-->
88
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
9+
<virtualType name="Magento\Catalog\Model\ResourceModel\Category\Collection\FetchStrategy" type="Magento\Framework\Data\Collection\Db\FetchStrategy\Cache">
10+
<arguments>
11+
<argument name="cacheTags" xsi:type="array">
12+
<item name="categoryTag" xsi:type="const">Magento\Catalog\Model\Category::CACHE_TAG</item>
13+
</argument>
14+
</arguments>
15+
</virtualType>
916
<type name="Magento\Catalog\Model\ResourceModel\Category\Collection">
1017
<arguments>
11-
<argument name="fetchStrategy" xsi:type="object">Magento\Framework\Data\Collection\Db\FetchStrategy\Cache</argument>
18+
<argument name="fetchStrategy" xsi:type="object">Magento\Catalog\Model\ResourceModel\Category\Collection\FetchStrategy</argument>
1219
</arguments>
1320
</type>
1421
<type name="Magento\Quote\Model\Quote\Item\ToOrderItem">

app/code/Magento/Catalog/view/adminhtml/web/js/options.js

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@ define([
1818
'use strict';
1919

2020
return function (config) {
21-
var optionDefaultInputType = 'radio',
22-
attributeOption = {
21+
var attributeOption = {
2322
table: $('attribute-options-table'),
2423
itemCount: 0,
2524
totalItems: 0,
@@ -39,7 +38,7 @@ define([
3938
}
4039

4140
if (!data.intype) {
42-
data.intype = optionDefaultInputType;
41+
data.intype = this.getOptionInputType();
4342
}
4443

4544
if (!this.totalItems) {
@@ -128,6 +127,15 @@ define([
128127
'.ignore-validate textarea';
129128

130129
jQuery('#edit_form').data('validator').settings.forceIgnore = ignore;
130+
},
131+
getOptionInputType: function () {
132+
var optionDefaultInputType = 'radio';
133+
134+
if ($('frontend_input') && $('frontend_input').value === 'multiselect') {
135+
optionDefaultInputType = 'checkbox';
136+
}
137+
138+
return optionDefaultInputType;
131139
}
132140
};
133141

@@ -167,7 +175,7 @@ define([
167175
}
168176

169177
window.attributeOption = attributeOption;
170-
window.optionDefaultInputType = optionDefaultInputType;
178+
window.optionDefaultInputType = attributeOption.getOptionInputType();
171179

172180
rg.set('manage-options-panel', attributeOption);
173181
};

0 commit comments

Comments
 (0)