Skip to content

Commit 2edd048

Browse files
committed
Merge remote-tracking branch 'origin/2.3-develop' into MPI-PR-10012019
2 parents 8b0f53a + 62b8f8a commit 2edd048

File tree

52 files changed

+1172
-1090
lines changed

Some content is hidden

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

52 files changed

+1172
-1090
lines changed

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

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
use Magento\Catalog\Model\Product;
99
use Magento\Catalog\Model\Product\Type;
1010

11+
/**
12+
* Provides duplicating bundle options and selections
13+
*/
1114
class Bundle implements \Magento\Catalog\Model\Product\CopyConstructorInterface
1215
{
1316
/**
@@ -27,7 +30,17 @@ public function build(Product $product, Product $duplicate)
2730
$bundleOptions = $product->getExtensionAttributes()->getBundleProductOptions() ?: [];
2831
$duplicatedBundleOptions = [];
2932
foreach ($bundleOptions as $key => $bundleOption) {
30-
$duplicatedBundleOptions[$key] = clone $bundleOption;
33+
$duplicatedBundleOption = clone $bundleOption;
34+
/**
35+
* Set option and selection ids to 'null' in order to create new option(selection) for duplicated product,
36+
* but not modifying existing one, which led to lost of option(selection) in original product.
37+
*/
38+
$productLinks = $duplicatedBundleOption->getProductLinks() ?: [];
39+
foreach ($productLinks as $productLink) {
40+
$productLink->setSelectionId(null);
41+
}
42+
$duplicatedBundleOption->setOptionId(null);
43+
$duplicatedBundleOptions[$key] = $duplicatedBundleOption;
3144
}
3245
$duplicate->getExtensionAttributes()->setBundleProductOptions($duplicatedBundleOptions);
3346
}

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

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
namespace Magento\Bundle\Test\Unit\Model\Product\CopyConstructor;
77

88
use Magento\Bundle\Api\Data\BundleOptionInterface;
9+
use Magento\Bundle\Model\Link;
910
use Magento\Bundle\Model\Product\CopyConstructor\Bundle;
1011
use Magento\Catalog\Api\Data\ProductExtensionInterface;
1112
use Magento\Catalog\Model\Product;
@@ -45,6 +46,7 @@ public function testBuildNegative()
4546
*/
4647
public function testBuildPositive()
4748
{
49+
/** @var Product|\PHPUnit_Framework_MockObject_MockObject $product */
4850
$product = $this->getMockBuilder(Product::class)
4951
->disableOriginalConstructor()
5052
->getMock();
@@ -60,18 +62,42 @@ public function testBuildPositive()
6062
->method('getExtensionAttributes')
6163
->willReturn($extensionAttributesProduct);
6264

65+
$productLink = $this->getMockBuilder(Link::class)
66+
->setMethods(['setSelectionId'])
67+
->disableOriginalConstructor()
68+
->getMock();
69+
$productLink->expects($this->exactly(2))
70+
->method('setSelectionId')
71+
->with($this->identicalTo(null));
72+
$firstOption = $this->getMockBuilder(BundleOptionInterface::class)
73+
->setMethods(['getProductLinks'])
74+
->disableOriginalConstructor()
75+
->getMockForAbstractClass();
76+
$firstOption->expects($this->once())
77+
->method('getProductLinks')
78+
->willReturn([$productLink]);
79+
$firstOption->expects($this->once())
80+
->method('setOptionId')
81+
->with($this->identicalTo(null));
82+
$secondOption = $this->getMockBuilder(BundleOptionInterface::class)
83+
->setMethods(['getProductLinks'])
84+
->disableOriginalConstructor()
85+
->getMockForAbstractClass();
86+
$secondOption->expects($this->once())
87+
->method('getProductLinks')
88+
->willReturn([$productLink]);
89+
$secondOption->expects($this->once())
90+
->method('setOptionId')
91+
->with($this->identicalTo(null));
6392
$bundleOptions = [
64-
$this->getMockBuilder(BundleOptionInterface::class)
65-
->disableOriginalConstructor()
66-
->getMockForAbstractClass(),
67-
$this->getMockBuilder(BundleOptionInterface::class)
68-
->disableOriginalConstructor()
69-
->getMockForAbstractClass()
93+
$firstOption,
94+
$secondOption
7095
];
7196
$extensionAttributesProduct->expects($this->once())
7297
->method('getBundleProductOptions')
7398
->willReturn($bundleOptions);
7499

100+
/** @var Product|\PHPUnit_Framework_MockObject_MockObject $duplicate */
75101
$duplicate = $this->getMockBuilder(Product::class)
76102
->disableOriginalConstructor()
77103
->getMock();

app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/StockDataFilter.php

Lines changed: 2 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77

88
use Magento\CatalogInventory\Api\StockConfigurationInterface;
99
use Magento\Framework\App\Config\ScopeConfigInterface;
10-
use Magento\CatalogInventory\Model\Stock;
1110

1211
/**
1312
* Class StockDataFilter
@@ -61,8 +60,8 @@ public function filter(array $stockData)
6160
$stockData['qty'] = self::MAX_QTY_VALUE;
6261
}
6362

64-
if (isset($stockData['min_qty'])) {
65-
$stockData['min_qty'] = $this->purifyMinQty($stockData['min_qty'], $stockData['backorders']);
63+
if (isset($stockData['min_qty']) && (int)$stockData['min_qty'] < 0) {
64+
$stockData['min_qty'] = 0;
6665
}
6766

6867
if (!isset($stockData['is_decimal_divided']) || $stockData['is_qty_decimal'] == 0) {
@@ -71,27 +70,4 @@ public function filter(array $stockData)
7170

7271
return $stockData;
7372
}
74-
75-
/**
76-
* Purifies min_qty.
77-
*
78-
* @param int $minQty
79-
* @param int $backOrders
80-
* @return float
81-
*/
82-
private function purifyMinQty(int $minQty, int $backOrders): float
83-
{
84-
/**
85-
* As described in the documentation if the Backorders Option is disabled
86-
* it is recommended to set the Out Of Stock Threshold to a positive number.
87-
* That's why to clarify the logic to the end user the code below prevent him to set a negative number so such
88-
* a number will turn to zero.
89-
* @see https://docs.magento.com/m2/ce/user_guide/catalog/inventory-backorders.html
90-
*/
91-
if ($backOrders === Stock::BACKORDERS_NO && $minQty < 0) {
92-
$minQty = 0;
93-
}
94-
95-
return (float)$minQty;
96-
}
9773
}

app/code/Magento/Catalog/Controller/Adminhtml/Product/Set/Edit.php

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,45 +6,63 @@
66
*/
77
namespace Magento\Catalog\Controller\Adminhtml\Product\Set;
88

9-
use Magento\Framework\App\Action\HttpGetActionInterface as HttpGetActionInterface;
9+
use Magento\Framework\Registry;
10+
use Magento\Backend\App\Action\Context;
11+
use Magento\Framework\App\ObjectManager;
12+
use Magento\Backend\Model\View\Result\Page;
13+
use Magento\Framework\View\Result\PageFactory;
14+
use Magento\Framework\Controller\ResultInterface;
15+
use Magento\Eav\Api\AttributeSetRepositoryInterface;
16+
use Magento\Catalog\Controller\Adminhtml\Product\Set;
17+
use Magento\Framework\Exception\NoSuchEntityException;
18+
use Magento\Framework\App\Action\HttpGetActionInterface;
1019

11-
class Edit extends \Magento\Catalog\Controller\Adminhtml\Product\Set implements HttpGetActionInterface
20+
/**
21+
* Edit attribute set controller.
22+
*/
23+
class Edit extends Set implements HttpGetActionInterface
1224
{
1325
/**
14-
* @var \Magento\Framework\View\Result\PageFactory
26+
* @var PageFactory
1527
*/
1628
protected $resultPageFactory;
1729

1830
/**
19-
* @param \Magento\Backend\App\Action\Context $context
20-
* @param \Magento\Framework\Registry $coreRegistry
21-
* @param \Magento\Framework\View\Result\PageFactory $resultPageFactory
31+
* @var AttributeSetRepositoryInterface
32+
*/
33+
private $attributeSetRepository;
34+
35+
/**
36+
* @param Context $context
37+
* @param Registry $coreRegistry
38+
* @param PageFactory $resultPageFactory
39+
* @param AttributeSetRepositoryInterface $attributeSetRepository
2240
*/
2341
public function __construct(
24-
\Magento\Backend\App\Action\Context $context,
25-
\Magento\Framework\Registry $coreRegistry,
26-
\Magento\Framework\View\Result\PageFactory $resultPageFactory
42+
Context $context,
43+
Registry $coreRegistry,
44+
PageFactory $resultPageFactory,
45+
AttributeSetRepositoryInterface $attributeSetRepository = null
2746
) {
2847
parent::__construct($context, $coreRegistry);
2948
$this->resultPageFactory = $resultPageFactory;
49+
$this->attributeSetRepository = $attributeSetRepository ?:
50+
ObjectManager::getInstance()->get(AttributeSetRepositoryInterface::class);
3051
}
3152

3253
/**
33-
* @return \Magento\Backend\Model\View\Result\Page
54+
* @inheritdoc
3455
*/
3556
public function execute()
3657
{
3758
$this->_setTypeId();
38-
$attributeSet = $this->_objectManager->create(\Magento\Eav\Model\Entity\Attribute\Set::class)
39-
->load($this->getRequest()->getParam('id'));
40-
59+
$attributeSet = $this->attributeSetRepository->get($this->getRequest()->getParam('id'));
4160
if (!$attributeSet->getId()) {
4261
return $this->resultRedirectFactory->create()->setPath('catalog/*/index');
4362
}
44-
4563
$this->_coreRegistry->register('current_attribute_set', $attributeSet);
4664

47-
/** @var \Magento\Backend\Model\View\Result\Page $resultPage */
65+
/** @var Page $resultPage */
4866
$resultPage = $this->resultPageFactory->create();
4967
$resultPage->setActiveMenu('Magento_Catalog::catalog_attributes_sets');
5068
$resultPage->getConfig()->getTitle()->prepend(__('Attribute Sets'));

app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Eav/Source.php

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
use Magento\Catalog\Model\Product\Attribute\Source\Status as ProductStatus;
99
use Magento\Catalog\Api\Data\ProductInterface;
1010
use Magento\Catalog\Api\Data\ProductAttributeInterface;
11+
use Magento\Framework\DB\Select;
12+
use Magento\Framework\DB\Sql\UnionExpression;
1113

1214
/**
1315
* Catalog Product Eav Select and Multiply Select Attributes Indexer resource model
@@ -199,13 +201,52 @@ protected function _prepareSelectIndex($entityIds = null, $attributeId = null)
199201
'dd.attribute_id',
200202
's.store_id',
201203
'value' => new \Zend_Db_Expr('COALESCE(ds.value, dd.value)'),
202-
'cpe.entity_id',
204+
'cpe.entity_id AS source_id',
203205
]
204206
);
205207

206208
if ($entityIds !== null) {
207209
$ids = implode(',', array_map('intval', $entityIds));
210+
$selectWithoutDefaultStore = $connection->select()->from(
211+
['wd' => $this->getTable('catalog_product_entity_int')],
212+
[
213+
'cpe.entity_id',
214+
'attribute_id',
215+
'store_id',
216+
'value',
217+
'cpe.entity_id',
218+
]
219+
)->joinLeft(
220+
['cpe' => $this->getTable('catalog_product_entity')],
221+
"cpe.{$productIdField} = wd.{$productIdField}",
222+
[]
223+
)->joinLeft(
224+
['d2d' => $this->getTable('catalog_product_entity_int')],
225+
sprintf(
226+
"d2d.store_id = 0 AND d2d.{$productIdField} = wd.{$productIdField} AND d2d.attribute_id = %s",
227+
$this->_eavConfig->getAttribute(\Magento\Catalog\Model\Product::ENTITY, 'status')->getId()
228+
),
229+
[]
230+
)->joinLeft(
231+
['d2s' => $this->getTable('catalog_product_entity_int')],
232+
"d2s.store_id != 0 AND d2s.attribute_id = d2d.attribute_id AND " .
233+
"d2s.{$productIdField} = d2d.{$productIdField}",
234+
[]
235+
)
236+
->where((new \Zend_Db_Expr('COALESCE(d2s.value, d2d.value)')) . ' = ' . ProductStatus::STATUS_ENABLED)
237+
->where("wd.attribute_id IN({$attrIdsFlat})")
238+
->where('wd.value IS NOT NULL')
239+
->where('wd.store_id != 0')
240+
->where("cpe.entity_id IN({$ids})");
208241
$select->where("cpe.entity_id IN({$ids})");
242+
$selects = new UnionExpression(
243+
[$select, $selectWithoutDefaultStore],
244+
Select::SQL_UNION,
245+
'( %s )'
246+
);
247+
248+
$select = $connection->select();
249+
$select->from(['u' => $selects]);
209250
}
210251

211252
/**
@@ -342,7 +383,7 @@ private function getMultiSelectAttributeWithSourceModels($attrIds)
342383
ProductAttributeInterface::ENTITY_TYPE_CODE,
343384
$criteria
344385
)->getItems();
345-
386+
346387
$options = [];
347388
foreach ($attributes as $attribute) {
348389
$sourceModelOptions = $attribute->getOptions();

app/code/Magento/Catalog/Test/Mftf/Test/AdminBackorderAllowedAddProductToCartTest.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,13 @@
2323
<createData entity="SimpleProductInStockQuantityZero" stepKey="createProduct"/>
2424

2525
<!-- Configure Magento to show out of stock products and to allow backorders -->
26+
<magentoCLI command="config:set {{CatalogInventoryOptionsShowOutOfStockEnable.path}} {{CatalogInventoryOptionsShowOutOfStockEnable.value}}" stepKey="setConfigShowOutOfStockTrue"/>
2627
<magentoCLI command="config:set {{CatalogInventoryItemOptionsBackordersEnable.path}} {{CatalogInventoryItemOptionsBackordersEnable.value}}" stepKey="setConfigAllowBackordersTrue"/>
2728
</before>
2829

2930
<after>
3031
<!-- Set Magento back to default configuration -->
32+
<magentoCLI command="config:set {{CatalogInventoryOptionsShowOutOfStockDisable.path}} {{CatalogInventoryOptionsShowOutOfStockDisable.value}}" stepKey="setConfigShowOutOfStockFalse"/>
3133
<magentoCLI command="config:set {{CatalogInventoryItemOptionsBackordersDisable.path}} {{CatalogInventoryItemOptionsBackordersDisable.value}}" stepKey="setConfigAllowBackordersFalse"/>
3234
<deleteData createDataKey="createProduct" stepKey="deleteProduct"/>
3335
</after>

0 commit comments

Comments
 (0)