Skip to content

Commit 4109919

Browse files
authored
ENGCOM-7787: Configurable Product Links Validation Bug when int value is 0 #29001
2 parents 5327040 + faade59 commit 4109919

File tree

3 files changed

+107
-19
lines changed

3 files changed

+107
-19
lines changed

app/code/Magento/ConfigurableProduct/Model/Plugin/ProductRepositorySave.php

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
* Copyright © Magento, Inc. All rights reserved.
44
* See COPYING.txt for license details.
55
*/
6+
7+
declare(strict_types=1);
8+
69
namespace Magento\ConfigurableProduct\Model\Plugin;
710

811
use Magento\Catalog\Api\Data\ProductInterface;
@@ -56,7 +59,7 @@ public function beforeSave(
5659
ProductRepositoryInterface $subject,
5760
ProductInterface $product,
5861
$saveOptions = false
59-
) {
62+
): array {
6063
$result[] = $product;
6164
if ($product->getTypeId() !== Configurable::TYPE_CODE) {
6265
return $result;
@@ -102,7 +105,7 @@ public function afterSave(
102105
ProductInterface $result,
103106
ProductInterface $product,
104107
$saveOptions = false
105-
) {
108+
): ProductInterface {
106109
if ($product->getTypeId() !== Configurable::TYPE_CODE) {
107110
return $result;
108111
}
@@ -120,19 +123,23 @@ public function afterSave(
120123
* @throws InputException
121124
* @throws NoSuchEntityException
122125
*/
123-
private function validateProductLinks(array $attributeCodes, array $linkIds)
126+
private function validateProductLinks(array $attributeCodes, array $linkIds): void
124127
{
125128
$valueMap = [];
126129
foreach ($linkIds as $productId) {
127130
$variation = $this->productRepository->getById($productId);
128131
$valueKey = '';
129132
foreach ($attributeCodes as $attributeCode) {
130-
if (!$variation->getData($attributeCode)) {
133+
if ($variation->getData($attributeCode) === null) {
131134
throw new InputException(
132-
__('Product with id "%1" does not contain required attribute "%2".', $productId, $attributeCode)
135+
__(
136+
'Product with id "%1" does not contain required attribute "%2".',
137+
$productId,
138+
$attributeCode
139+
)
133140
);
134141
}
135-
$valueKey = $valueKey . $attributeCode . ':' . $variation->getData($attributeCode) . ';';
142+
$valueKey .= $attributeCode . ':' . $variation->getData($attributeCode) . ';';
136143
}
137144
if (isset($valueMap[$valueKey])) {
138145
throw new InputException(

app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/ProductRepositorySaveTest.php

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* Copyright © Magento, Inc. All rights reserved.
44
* See COPYING.txt for license details.
55
*/
6+
67
declare(strict_types=1);
78

89
namespace Magento\ConfigurableProduct\Test\Unit\Model\Plugin;
@@ -18,6 +19,7 @@
1819
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
1920
use PHPUnit\Framework\MockObject\MockObject;
2021
use PHPUnit\Framework\TestCase;
22+
use Magento\Framework\Exception\InputException;
2123

2224
/**
2325
* Test for ProductRepositorySave plugin
@@ -71,7 +73,8 @@ class ProductRepositorySaveTest extends TestCase
7173
*/
7274
protected function setUp(): void
7375
{
74-
$this->productAttributeRepository = $this->getMockForAbstractClass(ProductAttributeRepositoryInterface::class);
76+
$this->productAttributeRepository =
77+
$this->getMockForAbstractClass(ProductAttributeRepositoryInterface::class);
7578

7679
$this->product = $this->getMockBuilder(Product::class)
7780
->disableOriginalConstructor()
@@ -105,8 +108,10 @@ protected function setUp(): void
105108

106109
/**
107110
* Validating the result after saving a configurable product
111+
*
112+
* @return void
108113
*/
109-
public function testBeforeSaveWhenProductIsSimple()
114+
public function testBeforeSaveWhenProductIsSimple(): void
110115
{
111116
$this->product->expects(static::once())
112117
->method('getTypeId')
@@ -122,8 +127,10 @@ public function testBeforeSaveWhenProductIsSimple()
122127

123128
/**
124129
* Test saving a configurable product without attribute options
130+
*
131+
* @return void
125132
*/
126-
public function testBeforeSaveWithoutOptions()
133+
public function testBeforeSaveWithoutOptions(): void
127134
{
128135
$this->product->expects(static::once())
129136
->method('getTypeId')
@@ -151,10 +158,12 @@ public function testBeforeSaveWithoutOptions()
151158

152159
/**
153160
* Test saving a configurable product with same set of attribute values
161+
*
162+
* @return void
154163
*/
155-
public function testBeforeSaveWithLinks()
164+
public function testBeforeSaveWithLinks(): void
156165
{
157-
$this->expectException('Magento\Framework\Exception\InputException');
166+
$this->expectException(InputException::class);
158167
$this->expectExceptionMessage('Products "5" and "4" have the same set of attribute values.');
159168
$links = [4, 5];
160169
$this->product->expects(static::once())
@@ -191,10 +200,12 @@ public function testBeforeSaveWithLinks()
191200

192201
/**
193202
* Test saving a configurable product with missing attribute
203+
*
204+
* @return void
194205
*/
195-
public function testBeforeSaveWithLinksWithMissingAttribute()
206+
public function testBeforeSaveWithLinksWithMissingAttribute(): void
196207
{
197-
$this->expectException('Magento\Framework\Exception\InputException');
208+
$this->expectException(InputException::class);
198209
$this->expectExceptionMessage('Product with id "4" does not contain required attribute "color".');
199210
$simpleProductId = 4;
200211
$links = [$simpleProductId, 5];
@@ -239,17 +250,19 @@ public function testBeforeSaveWithLinksWithMissingAttribute()
239250
$product->expects(static::once())
240251
->method('getData')
241252
->with($attributeCode)
242-
->willReturn(false);
253+
->willReturn(null);
243254

244255
$this->plugin->beforeSave($this->productRepository, $this->product);
245256
}
246257

247258
/**
248259
* Test saving a configurable product with duplicate attributes
260+
*
261+
* @return void
249262
*/
250-
public function testBeforeSaveWithLinksWithDuplicateAttributes()
263+
public function testBeforeSaveWithLinksWithDuplicateAttributes(): void
251264
{
252-
$this->expectException('Magento\Framework\Exception\InputException');
265+
$this->expectException(InputException::class);
253266
$this->expectExceptionMessage('Products "5" and "4" have the same set of attribute values.');
254267
$links = [4, 5];
255268
$attributeCode = 'color';

dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/ProductRepositoryTest.php

Lines changed: 71 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,20 @@
66
namespace Magento\ConfigurableProduct\Api;
77

88
use Magento\Catalog\Api\Data\ProductInterface;
9+
use Magento\Catalog\Api\ProductRepositoryInterface;
910
use Magento\Catalog\Model\Entity\Attribute;
1011
use Magento\Eav\Model\Config;
1112
use Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\Collection;
1213
use Magento\Framework\Api\ExtensibleDataInterface;
1314
use Magento\Framework\ObjectManagerInterface;
1415
use Magento\Framework\Webapi\Rest\Request;
1516
use Magento\TestFramework\Helper\Bootstrap;
17+
use Magento\ConfigurableProduct\Model\Product\Type\Configurable;
1618
use Magento\TestFramework\TestCase\WebapiAbstract;
1719

1820
/**
1921
* Class ProductRepositoryTest for testing ConfigurableProduct integration
22+
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
2023
*/
2124
class ProductRepositoryTest extends WebapiAbstract
2225
{
@@ -28,17 +31,22 @@ class ProductRepositoryTest extends WebapiAbstract
2831
/**
2932
* @var Config
3033
*/
31-
protected $eavConfig;
34+
private $eavConfig;
3235

3336
/**
3437
* @var ObjectManagerInterface
3538
*/
36-
protected $objectManager;
39+
private $objectManager;
3740

3841
/**
3942
* @var Attribute
4043
*/
41-
protected $configurableAttribute;
44+
private $configurableAttribute;
45+
46+
/**
47+
* @var ProductRepositoryInterface
48+
*/
49+
private $productRepository;
4250

4351
/**
4452
* @inheritdoc
@@ -47,6 +55,7 @@ protected function setUp(): void
4755
{
4856
$this->objectManager = Bootstrap::getObjectManager();
4957
$this->eavConfig = $this->objectManager->get(Config::class);
58+
$this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class);
5059
}
5160

5261
/**
@@ -164,6 +173,65 @@ public function testCreateConfigurableProduct()
164173
$this->assertEquals([$productId1, $productId2], $resultConfigurableProductLinks);
165174
}
166175

176+
/**
177+
* Create configurable with simple which has zero attribute value
178+
*
179+
* @magentoApiDataFixture Magento/ConfigurableProduct/_files/configurable_attribute_with_source_model.php
180+
* @magentoApiDataFixture Magento/Catalog/_files/product_simple.php
181+
* @return void
182+
*/
183+
public function testCreateConfigurableProductWithZeroOptionValue(): void
184+
{
185+
$attributeCode = 'test_configurable_with_sm';
186+
$attributeValue = 0;
187+
188+
$product = $this->productRepository->get('simple');
189+
$product->setCustomAttribute($attributeCode, $attributeValue);
190+
$this->productRepository->save($product);
191+
192+
$configurableAttribute = $this->eavConfig->getAttribute('catalog_product', $attributeCode);
193+
194+
$productData = [
195+
'sku' => self::CONFIGURABLE_PRODUCT_SKU,
196+
'name' => self::CONFIGURABLE_PRODUCT_SKU,
197+
'type_id' => Configurable::TYPE_CODE,
198+
'attribute_set_id' => 4,
199+
'extension_attributes' => [
200+
'configurable_product_options' => [
201+
[
202+
'attribute_id' => $configurableAttribute->getId(),
203+
'label' => 'Test configurable with source model',
204+
'values' => [
205+
['value_index' => '0'],
206+
],
207+
],
208+
],
209+
'configurable_product_links' => [$product->getId()],
210+
],
211+
];
212+
213+
$response = $this->createProduct($productData);
214+
215+
$this->assertArrayHasKey(ProductInterface::SKU, $response);
216+
$this->assertEquals(self::CONFIGURABLE_PRODUCT_SKU, $response[ProductInterface::SKU]);
217+
218+
$this->assertArrayHasKey(ProductInterface::TYPE_ID, $response);
219+
$this->assertEquals('configurable', $response[ProductInterface::TYPE_ID]);
220+
221+
$this->assertArrayHasKey(ProductInterface::EXTENSION_ATTRIBUTES_KEY, $response);
222+
$this->assertArrayHasKey(
223+
'configurable_product_options',
224+
$response[ProductInterface::EXTENSION_ATTRIBUTES_KEY]
225+
);
226+
$configurableProductOption =
227+
current($response[ProductInterface::EXTENSION_ATTRIBUTES_KEY]['configurable_product_options']);
228+
229+
$this->assertArrayHasKey('attribute_id', $configurableProductOption);
230+
$this->assertEquals($configurableAttribute->getId(), $configurableProductOption['attribute_id']);
231+
$this->assertArrayHasKey('values', $configurableProductOption);
232+
$this->assertEquals($attributeValue, $configurableProductOption['values'][0]['value_index']);
233+
}
234+
167235
/**
168236
* @magentoApiDataFixture Magento/ConfigurableProduct/_files/product_configurable.php
169237
*/

0 commit comments

Comments
 (0)