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