6
6
*/
7
7
namespace Magento \ConfigurableProduct \Api ;
8
8
9
+ use Magento \Catalog \Model \ResourceModel \Eav \Attribute ;
9
10
use Magento \Eav \Model \AttributeRepository ;
11
+ use Magento \Eav \Model \Entity \Attribute \Option ;
12
+ use Magento \Framework \Webapi \Rest \Request ;
13
+ use Magento \TestFramework \TestCase \WebapiAbstract ;
10
14
11
- class LinkManagementTest extends \ Magento \ TestFramework \ TestCase \ WebapiAbstract
15
+ class LinkManagementTest extends WebapiAbstract
12
16
{
13
17
const SERVICE_NAME = 'configurableProductLinkManagementV1 ' ;
18
+ const OPTION_SERVICE_NAME = 'configurableProductOptionRepositoryV1 ' ;
14
19
const SERVICE_VERSION = 'V1 ' ;
15
20
const RESOURCE_PATH = '/V1/configurable-products ' ;
16
21
@@ -88,9 +93,27 @@ public function testAddChildFullRestCreation()
88
93
89
94
$ this ->createConfigurableProduct ($ productSku );
90
95
$ attribute = $ this ->attributeRepository ->get ('catalog_product ' , 'test_configurable ' );
91
- $ attributeValue = $ attribute ->getOptions ()[1 ]->getValue ();
92
- $ this ->addOptionToConfigurableProduct ($ productSku , $ attribute ->getAttributeId (), $ attributeValue );
93
- $ this ->createSimpleProduct ($ childSku , $ attributeValue );
96
+
97
+ $ this ->addOptionToConfigurableProduct (
98
+ $ productSku ,
99
+ $ attribute ->getAttributeId (),
100
+ [
101
+ [
102
+ 'value_index ' => $ attribute ->getOptions ()[1 ]->getValue ()
103
+ ]
104
+ ]
105
+ );
106
+
107
+ $ this ->createSimpleProduct (
108
+ $ childSku ,
109
+ [
110
+ [
111
+ 'attribute_code ' => 'test_configurable ' ,
112
+ 'value ' => $ attribute ->getOptions ()[1 ]->getValue ()
113
+ ]
114
+ ]
115
+ );
116
+
94
117
$ res = $ this ->addChild ($ productSku , $ childSku );
95
118
$ this ->assertTrue ($ res );
96
119
@@ -106,38 +129,167 @@ public function testAddChildFullRestCreation()
106
129
$ this ->assertTrue ($ added );
107
130
108
131
// clean up products
132
+
133
+ $ this ->deleteProduct ($ productSku );
134
+ $ this ->deleteProduct ($ childSku );
135
+ }
136
+
137
+ /**
138
+ * Test if configurable option attribute positions are being preserved after simple products were assigned to a
139
+ * configurable product.
140
+ *
141
+ * @magentoApiDataFixture Magento/ConfigurableProduct/_files/configurable_attributes_for_position_test.php
142
+ */
143
+ public function testConfigurableOptionPositionPreservation ()
144
+ {
145
+ $ productSku = 'configurable-product-sku ' ;
146
+ $ childProductSkus = [
147
+ 'simple-product-sku-1 ' ,
148
+ 'simple-product-sku-2 '
149
+ ];
150
+ $ attributesToAdd = [
151
+ 'custom_attr_1 ' ,
152
+ 'custom_attr_2 ' ,
153
+ ];
154
+
155
+ $ this ->createConfigurableProduct ($ productSku );
156
+
157
+ $ position = 0 ;
158
+ $ attributeOptions = [];
159
+ foreach ($ attributesToAdd as $ attributeToAdd ) {
160
+ /** @var Attribute $attribute */
161
+ $ attribute = $ this ->attributeRepository ->get ('catalog_product ' , $ attributeToAdd );
162
+
163
+ /** @var Option $options[] */
164
+ $ options = $ attribute ->getOptions ();
165
+ array_shift ($ options );
166
+
167
+ $ attributeOptions [$ attributeToAdd ] = $ options ;
168
+
169
+ $ valueIndexesData = [];
170
+ foreach ($ options as $ option ) {
171
+ $ valueIndexesData []['value_index ' ]= $ option ->getValue ();
172
+ }
173
+ $ this ->addOptionToConfigurableProduct (
174
+ $ productSku ,
175
+ $ attribute ->getAttributeId (),
176
+ $ valueIndexesData ,
177
+ $ position
178
+ );
179
+ $ position ++;
180
+ }
181
+
182
+ $ this ->assertArrayHasKey ($ attributesToAdd [0 ], $ attributeOptions );
183
+ $ this ->assertArrayHasKey ($ attributesToAdd [1 ], $ attributeOptions );
184
+ $ this ->assertCount (4 , $ attributeOptions [$ attributesToAdd [0 ]]);
185
+ $ this ->assertCount (4 , $ attributeOptions [$ attributesToAdd [1 ]]);
186
+
187
+ $ attributesBeforeAssign = $ this ->getConfigurableAttribute ($ productSku );
188
+
189
+ $ simpleProdsAttributeData = [];
190
+ foreach ($ attributeOptions as $ attributeCode => $ options ) {
191
+ $ simpleProdsAttributeData [0 ][] = [
192
+ 'attribute_code ' => $ attributeCode ,
193
+ 'value ' => $ options [0 ]->getValue (),
194
+ ];
195
+ $ simpleProdsAttributeData [0 ][] = [
196
+ 'attribute_code ' => $ attributeCode ,
197
+ 'value ' => $ options [1 ]->getValue (),
198
+ ];
199
+ $ simpleProdsAttributeData [1 ][] = [
200
+ 'attribute_code ' => $ attributeCode ,
201
+ 'value ' => $ options [2 ]->getValue (),
202
+ ];
203
+ $ simpleProdsAttributeData [1 ][] = [
204
+ 'attribute_code ' => $ attributeCode ,
205
+ 'value ' => $ options [3 ]->getValue (),
206
+ ];
207
+ }
208
+
209
+ foreach ($ childProductSkus as $ childNum => $ childSku ) {
210
+ $ this ->createSimpleProduct ($ childSku , $ simpleProdsAttributeData [$ childNum ]);
211
+ $ res = $ this ->addChild ($ productSku , $ childSku );
212
+ $ this ->assertTrue ($ res );
213
+ }
214
+
215
+ $ childProductsDiff = array_diff (
216
+ $ childProductSkus ,
217
+ array_column (
218
+ $ this ->getChildren ($ productSku ),
219
+ 'sku '
220
+ )
221
+ );
222
+ $ this ->assertCount (0 , $ childProductsDiff , 'Added child product count mismatch expected result ' );
223
+
224
+ $ attributesAfterAssign = $ this ->getConfigurableAttribute ($ productSku );
225
+
226
+ $ this ->assertEquals (
227
+ $ attributesBeforeAssign [0 ]['position ' ],
228
+ $ attributesAfterAssign [0 ]['position ' ],
229
+ 'Product 1 attribute option position mismatch '
230
+ );
231
+ $ this ->assertEquals (
232
+ $ attributesBeforeAssign [1 ]['position ' ],
233
+ $ attributesAfterAssign [1 ]['position ' ],
234
+ 'Product 2 attribute option position mismatch '
235
+ );
236
+
237
+ foreach ($ childProductSkus as $ childSku ) {
238
+ $ this ->deleteProduct ($ childSku );
239
+ }
240
+ $ this ->deleteProduct ($ productSku );
241
+ }
242
+
243
+ /**
244
+ * Delete product by SKU
245
+ *
246
+ * @param string $sku
247
+ * @return bool
248
+ */
249
+ private function deleteProduct (string $ sku ): bool
250
+ {
109
251
$ serviceInfo = [
110
252
'rest ' => [
111
- 'resourcePath ' => '/V1/products/ ' . $ productSku ,
112
- 'httpMethod ' => \ Magento \ Framework \ Webapi \ Rest \ Request::HTTP_METHOD_DELETE
253
+ 'resourcePath ' => '/V1/products/ ' . $ sku ,
254
+ 'httpMethod ' => Request::HTTP_METHOD_DELETE
113
255
],
114
256
'soap ' => [
115
257
'service ' => 'catalogProductRepositoryV1 ' ,
116
258
'serviceVersion ' => self ::SERVICE_VERSION ,
117
259
'operation ' => 'catalogProductRepositoryV1DeleteById ' ,
118
260
],
119
261
];
120
- $ this ->_webApiCall ($ serviceInfo , ['sku ' => $ productSku ]);
262
+ return $ this ->_webApiCall ($ serviceInfo , ['sku ' => $ sku ]);
263
+ }
264
+
265
+ /**
266
+ * Get configurable product attributes
267
+ *
268
+ * @param string $productSku
269
+ * @return array
270
+ */
271
+ protected function getConfigurableAttribute (string $ productSku ): array
272
+ {
121
273
$ serviceInfo = [
122
274
'rest ' => [
123
- 'resourcePath ' => ' /V1/products/ ' . $ childSku ,
124
- 'httpMethod ' => \ Magento \ Framework \ Webapi \ Rest \ Request::HTTP_METHOD_DELETE
275
+ 'resourcePath ' => self :: RESOURCE_PATH . ' / ' . $ productSku . ' /options/all ' ,
276
+ 'httpMethod ' => Request::HTTP_METHOD_GET
125
277
],
126
278
'soap ' => [
127
- 'service ' => ' catalogProductRepositoryV1 ' ,
279
+ 'service ' => self :: OPTION_SERVICE_NAME ,
128
280
'serviceVersion ' => self ::SERVICE_VERSION ,
129
- 'operation ' => ' catalogProductRepositoryV1DeleteById ' ,
130
- ],
281
+ 'operation ' => self :: OPTION_SERVICE_NAME . ' GetList '
282
+ ]
131
283
];
132
- $ this ->_webApiCall ($ serviceInfo , ['sku ' => $ childSku ]);
284
+ return $ this ->_webApiCall ($ serviceInfo , ['sku ' => $ productSku ]);
133
285
}
134
286
135
287
private function addChild ($ productSku , $ childSku )
136
288
{
137
289
$ serviceInfo = [
138
290
'rest ' => [
139
291
'resourcePath ' => self ::RESOURCE_PATH . '/ ' . $ productSku . '/child ' ,
140
- 'httpMethod ' => \ Magento \ Framework \ Webapi \ Rest \ Request::HTTP_METHOD_POST
292
+ 'httpMethod ' => Request::HTTP_METHOD_POST
141
293
],
142
294
'soap ' => [
143
295
'service ' => self ::SERVICE_NAME ,
@@ -162,7 +314,7 @@ protected function createConfigurableProduct($productSku)
162
314
$ serviceInfo = [
163
315
'rest ' => [
164
316
'resourcePath ' => '/V1/products ' ,
165
- 'httpMethod ' => \ Magento \ Framework \ Webapi \ Rest \ Request::HTTP_METHOD_POST
317
+ 'httpMethod ' => Request::HTTP_METHOD_POST
166
318
],
167
319
'soap ' => [
168
320
'service ' => 'catalogProductRepositoryV1 ' ,
@@ -173,24 +325,22 @@ protected function createConfigurableProduct($productSku)
173
325
return $ this ->_webApiCall ($ serviceInfo , $ requestData );
174
326
}
175
327
176
- protected function addOptionToConfigurableProduct ($ productSku , $ attributeId , $ attributeValue )
328
+ protected function addOptionToConfigurableProduct ($ productSku , $ attributeId , $ attributeValues , $ position = 0 )
177
329
{
178
330
$ requestData = [
179
331
'sku ' => $ productSku ,
180
332
'option ' => [
181
333
'attribute_id ' => $ attributeId ,
182
334
'label ' => 'test_configurable ' ,
183
- 'position ' => 0 ,
335
+ 'position ' => $ position ,
184
336
'is_use_default ' => true ,
185
- 'values ' => [
186
- ['value_index ' => $ attributeValue ],
187
- ]
337
+ 'values ' => $ attributeValues
188
338
]
189
339
];
190
340
$ serviceInfo = [
191
341
'rest ' => [
192
342
'resourcePath ' => '/V1/configurable-products/ ' . $ productSku .'/options ' ,
193
- 'httpMethod ' => \ Magento \ Framework \ Webapi \ Rest \ Request::HTTP_METHOD_POST ,
343
+ 'httpMethod ' => Request::HTTP_METHOD_POST ,
194
344
],
195
345
'soap ' => [
196
346
'service ' => 'configurableProductOptionRepositoryV1 ' ,
@@ -201,7 +351,7 @@ protected function addOptionToConfigurableProduct($productSku, $attributeId, $at
201
351
return $ this ->_webApiCall ($ serviceInfo , $ requestData );
202
352
}
203
353
204
- protected function createSimpleProduct ($ sku , $ attributeValue )
354
+ protected function createSimpleProduct ($ sku , $ customAttributes )
205
355
{
206
356
$ requestData = [
207
357
'product ' => [
@@ -212,15 +362,13 @@ protected function createSimpleProduct($sku, $attributeValue)
212
362
'price ' => 3.62 ,
213
363
'status ' => 1 ,
214
364
'visibility ' => 4 ,
215
- 'custom_attributes ' => [
216
- ['attribute_code ' => 'test_configurable ' , 'value ' => $ attributeValue ],
217
- ]
365
+ 'custom_attributes ' => $ customAttributes
218
366
]
219
367
];
220
368
$ serviceInfo = [
221
369
'rest ' => [
222
370
'resourcePath ' => '/V1/products ' ,
223
- 'httpMethod ' => \ Magento \ Framework \ Webapi \ Rest \ Request::HTTP_METHOD_POST ,
371
+ 'httpMethod ' => Request::HTTP_METHOD_POST ,
224
372
],
225
373
'soap ' => [
226
374
'service ' => 'catalogProductRepositoryV1 ' ,
@@ -247,7 +395,7 @@ protected function removeChild($productSku, $childSku)
247
395
$ serviceInfo = [
248
396
'rest ' => [
249
397
'resourcePath ' => sprintf ($ resourcePath , $ productSku , $ childSku ),
250
- 'httpMethod ' => \ Magento \ Framework \ Webapi \ Rest \ Request::HTTP_METHOD_DELETE
398
+ 'httpMethod ' => Request::HTTP_METHOD_DELETE
251
399
],
252
400
'soap ' => [
253
401
'service ' => self ::SERVICE_NAME ,
@@ -268,7 +416,7 @@ protected function getChildren($productSku)
268
416
$ serviceInfo = [
269
417
'rest ' => [
270
418
'resourcePath ' => self ::RESOURCE_PATH . '/ ' . $ productSku . '/children ' ,
271
- 'httpMethod ' => \ Magento \ Framework \ Webapi \ Rest \ Request::HTTP_METHOD_GET
419
+ 'httpMethod ' => Request::HTTP_METHOD_GET
272
420
],
273
421
'soap ' => [
274
422
'service ' => self ::SERVICE_NAME ,
0 commit comments