Skip to content

Commit dbf74dc

Browse files
committed
Merge branch '2.3-develop' of github.com:magento/magento2ce into MAGETWO-90563
2 parents 42d4e1b + b2eb795 commit dbf74dc

File tree

41 files changed

+1250
-363
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1250
-363
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ env:
2222
global:
2323
- COMPOSER_BIN_DIR=~/bin
2424
- INTEGRATION_SETS=3
25-
- NODE_JS_VERSION=6
25+
- NODE_JS_VERSION=8
2626
- MAGENTO_HOST_NAME="magento2.travis"
2727
matrix:
2828
- TEST_SUITE=unit

COPYING.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Copyright © 2013-2018 Magento, Inc.
1+
Copyright © 2013-present Magento, Inc.
22

33
Each Magento source file included in this distribution is licensed under OSL 3.0 or the Magento Enterprise Edition (MEE) license
44

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use Magento\Framework\Serialize\Serializer\Json;
1313
use Magento\Framework\EntityManager\MetadataPool;
1414
use Magento\Bundle\Model\ResourceModel\Selection\Collection\FilterApplier as SelectionCollectionFilterApplier;
15+
use Magento\Bundle\Model\ResourceModel\Selection\Collection as Selections;
1516

1617
/**
1718
* Bundle Type Model
@@ -484,7 +485,9 @@ public function getSelectionsCollection($optionIds, $product)
484485
\Magento\Catalog\Api\Data\ProductInterface::class
485486
);
486487

487-
$selectionsCollection = $this->_bundleCollection->create()
488+
/** @var Selections $selectionsCollection */
489+
$selectionsCollection = $this->_bundleCollection->create();
490+
$selectionsCollection
488491
->addAttributeToSelect($this->_config->getProductAttributes())
489492
->addAttributeToSelect('tax_class_id') //used for calculation item taxes in Bundle with Dynamic Price
490493
->setFlag('product_children', true)
@@ -853,8 +856,9 @@ public function getSelectionsByIds($selectionIds, $product)
853856

854857
if (!$usedSelections || $usedSelectionsIds !== $selectionIds) {
855858
$storeId = $product->getStoreId();
856-
$usedSelections = $this->_bundleCollection
857-
->create()
859+
/** @var Selections $usedSelections */
860+
$usedSelections = $this->_bundleCollection->create();
861+
$usedSelections
858862
->addAttributeToSelect('*')
859863
->setFlag('product_children', true)
860864
->addStoreFilter($this->getStoreFilter($product))

app/code/Magento/Bundle/Model/ResourceModel/Selection/Collection.php

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -163,22 +163,36 @@ public function setPositionOrder()
163163
}
164164

165165
/**
166-
* Add filtering of product then havent enoght stock
166+
* Add filtering of products that have 0 items left.
167167
*
168168
* @return $this
169169
* @since 100.2.0
170170
*/
171171
public function addQuantityFilter()
172172
{
173+
$stockItemTable = $this->getTable('cataloginventory_stock_item');
174+
$stockStatusTable = $this->getTable('cataloginventory_stock_status');
173175
$this->getSelect()
174176
->joinInner(
175-
['stock' => $this->getTable('cataloginventory_stock_status')],
177+
['stock' => $stockStatusTable],
176178
'selection.product_id = stock.product_id',
177179
[]
180+
)->joinInner(
181+
['stock_item' => $stockItemTable],
182+
'selection.product_id = stock_item.product_id',
183+
[]
178184
)
179185
->where(
180-
'(selection.selection_can_change_qty or selection.selection_qty <= stock.qty) and stock.stock_status'
181-
);
186+
'('
187+
. 'selection.selection_can_change_qty > 0'
188+
. ' or '
189+
. 'selection.selection_qty <= stock.qty'
190+
. ' or '
191+
.'stock_item.manage_stock = 0'
192+
. ')'
193+
)
194+
->where('stock.stock_status = 1');
195+
182196
return $this;
183197
}
184198

app/code/Magento/Bundle/Test/Unit/Model/ResourceModel/Selection/CollectionTest.php

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -111,17 +111,46 @@ protected function setUp()
111111

112112
public function testAddQuantityFilter()
113113
{
114-
$tableName = 'cataloginventory_stock_status';
115-
$this->entity->expects($this->once())
114+
$statusTableName = 'cataloginventory_stock_status';
115+
$itemTableName = 'cataloginventory_stock_item';
116+
$this->entity->expects($this->exactly(2))
116117
->method('getTable')
117-
->willReturn($tableName);
118-
$this->select->expects($this->once())
118+
->willReturnMap([
119+
['cataloginventory_stock_item', $itemTableName],
120+
['cataloginventory_stock_status', $statusTableName],
121+
]);
122+
$this->select->expects($this->exactly(2))
119123
->method('joinInner')
120-
->with(
121-
['stock' => $tableName],
122-
'selection.product_id = stock.product_id',
123-
[]
124+
->withConsecutive(
125+
[
126+
['stock' => $statusTableName],
127+
'selection.product_id = stock.product_id',
128+
[],
129+
],
130+
[
131+
['stock_item' => $itemTableName],
132+
'selection.product_id = stock_item.product_id',
133+
[],
134+
]
124135
)->willReturnSelf();
136+
$this->select
137+
->expects($this->exactly(2))
138+
->method('where')
139+
->withConsecutive(
140+
[
141+
'('
142+
. 'selection.selection_can_change_qty > 0'
143+
. ' or '
144+
. 'selection.selection_qty <= stock.qty'
145+
. ' or '
146+
.'stock_item.manage_stock = 0'
147+
. ')',
148+
],
149+
[
150+
'stock.stock_status = 1',
151+
]
152+
)->willReturnSelf();
153+
125154
$this->assertEquals($this->model, $this->model->addQuantityFilter());
126155
}
127156
}

app/code/Magento/BundleImportExport/Model/Export/RowCustomizer.php

Lines changed: 132 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@
99
use Magento\CatalogImportExport\Model\Export\RowCustomizerInterface;
1010
use Magento\CatalogImportExport\Model\Import\Product as ImportProductModel;
1111
use Magento\Bundle\Model\ResourceModel\Selection\Collection as SelectionCollection;
12-
use Magento\ImportExport\Controller\Adminhtml\Import;
1312
use Magento\ImportExport\Model\Import as ImportModel;
14-
use \Magento\Catalog\Model\Product\Type\AbstractType;
13+
use Magento\Catalog\Model\Product\Type\AbstractType;
14+
use Magento\Store\Model\StoreManagerInterface;
1515

1616
/**
1717
* Class RowCustomizer
@@ -105,6 +105,34 @@ class RowCustomizer implements RowCustomizerInterface
105105
AbstractType::SHIPMENT_SEPARATELY => 'separately',
106106
];
107107

108+
/**
109+
* @var \Magento\Bundle\Model\ResourceModel\Option\Collection[]
110+
*/
111+
private $optionCollections = [];
112+
113+
/**
114+
* @var array
115+
*/
116+
private $storeIdToCode = [];
117+
118+
/**
119+
* @var string
120+
*/
121+
private $optionCollectionCacheKey = '_cache_instance_options_collection';
122+
123+
/**
124+
* @var StoreManagerInterface
125+
*/
126+
private $storeManager;
127+
128+
/**
129+
* @param StoreManagerInterface $storeManager
130+
*/
131+
public function __construct(StoreManagerInterface $storeManager)
132+
{
133+
$this->storeManager = $storeManager;
134+
}
135+
108136
/**
109137
* Retrieve list of bundle specific columns
110138
* @return array
@@ -205,17 +233,15 @@ protected function populateBundleData($collection)
205233
* @param \Magento\Catalog\Model\Product $product
206234
* @return string
207235
*/
208-
protected function getFormattedBundleOptionValues($product)
236+
protected function getFormattedBundleOptionValues(\Magento\Catalog\Model\Product $product): string
209237
{
210-
/** @var \Magento\Bundle\Model\ResourceModel\Option\Collection $optionsCollection */
211-
$optionsCollection = $product->getTypeInstance()
212-
->getOptionsCollection($product)
213-
->setOrder('position', Collection::SORT_ORDER_ASC);
214-
238+
$optionCollections = $this->getProductOptionCollection($product);
215239
$bundleData = '';
216-
foreach ($optionsCollection as $option) {
240+
$optionTitles = $this->getBundleOptionTitles($product);
241+
foreach ($optionCollections->getItems() as $option) {
242+
$optionValues = $this->getFormattedOptionValues($option, $optionTitles);
217243
$bundleData .= $this->getFormattedBundleSelections(
218-
$this->getFormattedOptionValues($option),
244+
$optionValues,
219245
$product->getTypeInstance()
220246
->getSelectionsCollection([$option->getId()], $product)
221247
->setOrder('position', Collection::SORT_ORDER_ASC)
@@ -267,16 +293,25 @@ function ($value, $key) {
267293
* Retrieve option value of bundle product
268294
*
269295
* @param \Magento\Bundle\Model\Option $option
296+
* @param string[] $optionTitles
270297
* @return string
271298
*/
272-
protected function getFormattedOptionValues($option)
273-
{
274-
return 'name' . ImportProductModel::PAIR_NAME_VALUE_SEPARATOR
275-
. $option->getTitle() . ImportModel::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR
276-
. 'type' . ImportProductModel::PAIR_NAME_VALUE_SEPARATOR
277-
. $option->getType() . ImportModel::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR
278-
. 'required' . ImportProductModel::PAIR_NAME_VALUE_SEPARATOR
279-
. $option->getRequired();
299+
protected function getFormattedOptionValues(
300+
\Magento\Bundle\Model\Option $option,
301+
array $optionTitles = []
302+
): string {
303+
$names = implode(ImportModel::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR, array_map(
304+
function ($title, $storeName) {
305+
return $storeName . ImportProductModel::PAIR_NAME_VALUE_SEPARATOR . $title;
306+
},
307+
$optionTitles[$option->getOptionId()],
308+
array_keys($optionTitles[$option->getOptionId()])
309+
));
310+
return $names . ImportModel::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR
311+
. 'type' . ImportProductModel::PAIR_NAME_VALUE_SEPARATOR
312+
. $option->getType() . ImportModel::DEFAULT_GLOBAL_MULTI_VALUE_SEPARATOR
313+
. 'required' . ImportProductModel::PAIR_NAME_VALUE_SEPARATOR
314+
. $option->getRequired();
280315
}
281316

282317
/**
@@ -381,4 +416,83 @@ private function parseAdditionalAttributes($additionalAttributes)
381416
}
382417
return $preparedAttributes;
383418
}
419+
420+
/**
421+
* Get product options titles.
422+
*
423+
* Values for all store views (default) should be specified with 'name' key.
424+
* If user want to specify value or change existing for non default store views it should be specified with
425+
* 'name_' prefix and needed store view suffix.
426+
*
427+
* For example:
428+
* - 'name=All store views name' for all store views
429+
* - 'name_specific_store=Specific store name' for store view with 'specific_store' store code
430+
*
431+
* @param \Magento\Catalog\Model\Product $product
432+
* @return array
433+
*/
434+
private function getBundleOptionTitles(\Magento\Catalog\Model\Product $product): array
435+
{
436+
$optionCollections = $this->getProductOptionCollection($product);
437+
$optionsTitles = [];
438+
/** @var \Magento\Bundle\Model\Option $option */
439+
foreach ($optionCollections->getItems() as $option) {
440+
$optionsTitles[$option->getId()]['name'] = $option->getTitle();
441+
}
442+
$storeIds = $product->getStoreIds();
443+
if (count($storeIds) > 1) {
444+
foreach ($storeIds as $storeId) {
445+
$optionCollections = $this->getProductOptionCollection($product, (int)$storeId);
446+
/** @var \Magento\Bundle\Model\Option $option */
447+
foreach ($optionCollections->getItems() as $option) {
448+
$optionTitle = $option->getTitle();
449+
if ($optionsTitles[$option->getId()]['name'] != $optionTitle) {
450+
$optionsTitles[$option->getId()]['name_' . $this->getStoreCodeById((int)$storeId)] =
451+
$optionTitle;
452+
}
453+
}
454+
}
455+
}
456+
return $optionsTitles;
457+
}
458+
459+
/**
460+
* Get product options collection by provided product model.
461+
*
462+
* Set given store id to the product if it was defined (default store id will be set if was not).
463+
*
464+
* @param \Magento\Catalog\Model\Product $product $product
465+
* @param int $storeId
466+
* @return \Magento\Bundle\Model\ResourceModel\Option\Collection
467+
*/
468+
private function getProductOptionCollection(
469+
\Magento\Catalog\Model\Product $product,
470+
int $storeId = \Magento\Store\Model\Store::DEFAULT_STORE_ID
471+
): \Magento\Bundle\Model\ResourceModel\Option\Collection {
472+
$productSku = $product->getSku();
473+
if (!isset($this->optionCollections[$productSku][$storeId])) {
474+
$product->unsetData($this->optionCollectionCacheKey);
475+
$product->setStoreId($storeId);
476+
$this->optionCollections[$productSku][$storeId] = $product->getTypeInstance()
477+
->getOptionsCollection($product)
478+
->setOrder('position', Collection::SORT_ORDER_ASC);
479+
}
480+
return $this->optionCollections[$productSku][$storeId];
481+
}
482+
483+
/**
484+
* Retrieve store code by it's ID.
485+
*
486+
* Collect store id in $storeIdToCode[] private variable if it was not initialized earlier.
487+
*
488+
* @param int $storeId
489+
* @return string
490+
*/
491+
private function getStoreCodeById(int $storeId): string
492+
{
493+
if (!isset($this->storeIdToCode[$storeId])) {
494+
$this->storeIdToCode[$storeId] = $this->storeManager->getStore($storeId)->getCode();
495+
}
496+
return $this->storeIdToCode[$storeId];
497+
}
384498
}

0 commit comments

Comments
 (0)