Skip to content

Commit 633ef00

Browse files
author
Prabhu Ram
committed
added support for adding bundle options with custom quantity
- added e2e tests for adding bundle options ith custom quantity
1 parent b26a7e5 commit 633ef00

File tree

4 files changed

+308
-0
lines changed

4 files changed

+308
-0
lines changed

app/code/Magento/QuoteBundleOptions/Model/Cart/BuyRequest/BundleDataProvider.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,21 @@ public function execute(CartItem $cartItem): array
4040
$bundleOptionsData['bundle_option'][$optionId] = $optionValueId;
4141
$bundleOptionsData['bundle_option_qty'][$optionId] = $optionQuantity;
4242
}
43+
//for bundle options with custom quantity
44+
foreach ($cartItem->getEnteredOptions() as $option) {
45+
// phpcs:ignore Magento2.Functions.DiscouragedFunction
46+
$optionData = \explode('/', base64_decode($option->getId()));
47+
48+
if ($this->isProviderApplicable($optionData) === false) {
49+
continue;
50+
}
51+
$this->validateInput($optionData);
52+
53+
[, $optionId, $optionValueId] = $optionData;
54+
$optionQuantity = $option->getValue();
55+
$bundleOptionsData['bundle_option'][$optionId] = $optionValueId;
56+
$bundleOptionsData['bundle_option_qty'][$optionId] = $optionQuantity;
57+
}
4358

4459
return $bundleOptionsData;
4560
}

dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/AddBundleProductToCartSingleMutationTest.php

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,4 +222,130 @@ public function testAddBundleToCartWithWrongBundleOptions()
222222
$response['addProductsToCart']['userInputErrors'][0]['message']
223223
);
224224
}
225+
226+
/**
227+
* @magentoApiDataFixture Magento/Bundle/_files/product_with_multiple_options_and_custom_quantity.php
228+
* @magentoApiDataFixture Magento/Checkout/_files/active_quote.php
229+
*/
230+
public function testUpdateBundleItemWithCustomOptionQuantity()
231+
{
232+
233+
$this->quoteResource->load(
234+
$this->quote,
235+
'test_order_1',
236+
'reserved_order_id'
237+
);
238+
$maskedQuoteId = $this->quoteIdToMaskedId->execute((int)$this->quote->getId());
239+
$response = $this->graphQlQuery($this->getProductQuery("bundle-product"));
240+
$bundleItem = $response['products']['items'][0];
241+
$sku = $bundleItem['sku'];
242+
$bundleOptions = $bundleItem['items'];
243+
244+
$uId0 = $bundleOptions[0]['options'][0]['uid'];
245+
$uId1 = $bundleOptions[1]['options'][0]['uid'];
246+
$response = $this->graphQlMutation(
247+
$this->getMutationsQuery($maskedQuoteId, $uId0, $uId1, $sku)
248+
);
249+
$bundleOptions = $response['addProductsToCart']['cart']['items'][0]['bundle_options'];
250+
$this->assertEquals(5, $bundleOptions[0]['values'][0]['quantity']);
251+
$this->assertEquals(1, $bundleOptions[1]['values'][0]['quantity']);
252+
}
253+
254+
/**
255+
* Returns GraphQL query for retrieving a product with customizable options
256+
*
257+
* @param string $sku
258+
* @return string
259+
*/
260+
private function getProductQuery(string $sku): string
261+
{
262+
return <<<QUERY
263+
{
264+
products(search: "{$sku}") {
265+
items {
266+
sku
267+
... on BundleProduct {
268+
items {
269+
sku
270+
option_id
271+
required
272+
type
273+
title
274+
options {
275+
uid
276+
label
277+
product {
278+
sku
279+
}
280+
can_change_quantity
281+
id
282+
price
283+
284+
quantity
285+
}
286+
}
287+
}
288+
}
289+
}
290+
}
291+
QUERY;
292+
}
293+
294+
private function getMutationsQuery(
295+
string $maskedQuoteId,
296+
string $optionUid0,
297+
string $optionUid1,
298+
string $sku
299+
): string {
300+
return <<<QUERY
301+
mutation {
302+
addProductsToCart(
303+
cartId: "{$maskedQuoteId}",
304+
cartItems: [
305+
{
306+
sku: "{$sku}"
307+
quantity: 2
308+
selected_options: [
309+
"{$optionUid1}", "{$optionUid0}"
310+
],
311+
entered_options: [{
312+
id: "{$optionUid0}"
313+
value: "5"
314+
},
315+
{
316+
id: "{$optionUid1}"
317+
value: "5"
318+
}]
319+
}
320+
]
321+
) {
322+
cart {
323+
items {
324+
id
325+
quantity
326+
product {
327+
sku
328+
}
329+
... on BundleCartItem {
330+
bundle_options {
331+
id
332+
label
333+
type
334+
values {
335+
id
336+
label
337+
price
338+
quantity
339+
}
340+
}
341+
}
342+
}
343+
}
344+
userInputErrors {
345+
message
346+
}
347+
}
348+
}
349+
QUERY;
350+
}
225351
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
use Magento\TestFramework\Workaround\Override\Fixture\Resolver;
8+
9+
Resolver::getInstance()->requireDataFixture('Magento/Catalog/_files/multiple_products.php');
10+
11+
$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
12+
13+
$productIds = range(10, 12, 1);
14+
foreach ($productIds as $productId) {
15+
/** @var \Magento\CatalogInventory\Model\Stock\Item $stockItem */
16+
$stockItem = $objectManager->create(\Magento\CatalogInventory\Model\Stock\Item::class);
17+
$stockItem->load($productId, 'product_id');
18+
19+
if (!$stockItem->getProductId()) {
20+
$stockItem->setProductId($productId);
21+
}
22+
$stockItem->setUseConfigManageStock(1);
23+
$stockItem->setQty(1000);
24+
$stockItem->setIsQtyDecimal(0);
25+
$stockItem->setIsInStock(1);
26+
$stockItem->save();
27+
}
28+
29+
/** @var $product \Magento\Catalog\Model\Product */
30+
$product = $objectManager->create(\Magento\Catalog\Model\Product::class);
31+
$product->setTypeId(\Magento\Catalog\Model\Product\Type::TYPE_BUNDLE)
32+
->setId(3)
33+
->setAttributeSetId(4)
34+
->setName('Bundle Product')
35+
->setSku('bundle-product')
36+
->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH)
37+
->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED)
38+
->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1])
39+
->setWebsiteIds([1])
40+
->setPriceType(1)
41+
->setPrice(10.0)
42+
->setShipmentType(0)
43+
->setPriceView(1)
44+
->setBundleOptionsData(
45+
[
46+
// Required "Drop-down" option
47+
[
48+
'title' => 'Option 1',
49+
'default_title' => 'Option 1',
50+
'type' => 'select',
51+
'required' => 1,
52+
'position' => 1,
53+
'delete' => '',
54+
],
55+
// Required "Radio Buttons" option
56+
[
57+
'title' => 'Option 2',
58+
'default_title' => 'Option 2',
59+
'type' => 'radio',
60+
'required' => 1,
61+
'position' => 2,
62+
'delete' => '',
63+
],
64+
]
65+
)->setBundleSelectionsData(
66+
[
67+
[
68+
[
69+
'product_id' => 10,
70+
'selection_qty' => 1,
71+
'selection_can_change_qty' => 1,
72+
'delete' => '',
73+
'option_id' => 1
74+
],
75+
[
76+
'product_id' => 11,
77+
'selection_qty' => 1,
78+
'selection_can_change_qty' => 1,
79+
'delete' => '',
80+
'option_id' => 1
81+
]
82+
],
83+
[
84+
[
85+
'product_id' => 10,
86+
'selection_qty' => 1,
87+
'selection_can_change_qty' => 0,
88+
'delete' => '',
89+
'option_id' => 2
90+
],
91+
[
92+
'product_id' => 11,
93+
'selection_qty' => 1,
94+
'selection_can_change_qty' => 0,
95+
'delete' => '',
96+
'option_id' => 2
97+
]
98+
]
99+
]
100+
);
101+
$productRepository = $objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class);
102+
103+
if ($product->getBundleOptionsData()) {
104+
$options = [];
105+
foreach ($product->getBundleOptionsData() as $key => $optionData) {
106+
if (!(bool)$optionData['delete']) {
107+
$option = $objectManager->create(\Magento\Bundle\Api\Data\OptionInterfaceFactory::class)
108+
->create(['data' => $optionData]);
109+
$option->setSku($product->getSku());
110+
$option->setOptionId(null);
111+
112+
$links = [];
113+
$bundleLinks = $product->getBundleSelectionsData();
114+
if (!empty($bundleLinks[$key])) {
115+
foreach ($bundleLinks[$key] as $linkData) {
116+
if (!(bool)$linkData['delete']) {
117+
$link = $objectManager->create(\Magento\Bundle\Api\Data\LinkInterfaceFactory::class)
118+
->create(['data' => $linkData]);
119+
$linkProduct = $productRepository->getById($linkData['product_id']);
120+
$link->setSku($linkProduct->getSku());
121+
$link->setQty($linkData['selection_qty']);
122+
if (isset($linkData['selection_can_change_qty'])) {
123+
$link->setCanChangeQuantity($linkData['selection_can_change_qty']);
124+
}
125+
$links[] = $link;
126+
}
127+
}
128+
$option->setProductLinks($links);
129+
$options[] = $option;
130+
}
131+
}
132+
}
133+
$extension = $product->getExtensionAttributes();
134+
$extension->setBundleProductOptions($options);
135+
$product->setExtensionAttributes($extension);
136+
}
137+
$product->save();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
use Magento\TestFramework\Workaround\Override\Fixture\Resolver;
8+
use Magento\TestFramework\Helper\Bootstrap;
9+
10+
Resolver::getInstance()->requireDataFixture('Magento/Catalog/_files/multiple_products_rollback.php');
11+
12+
$objectManager = Bootstrap::getObjectManager();
13+
/** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */
14+
$productRepository = $objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class);
15+
/** @var \Magento\Framework\Registry $registry */
16+
$registry = Bootstrap::getObjectManager()->get(\Magento\Framework\Registry::class);
17+
18+
19+
$registry->unregister('isSecureArea');
20+
$registry->register('isSecureArea', true);
21+
22+
try {
23+
$product = $productRepository->get('bundle-product');
24+
$productRepository->delete($product);
25+
} catch (\Magento\Framework\Exception\NoSuchEntityException $exception) {
26+
//Product already removed
27+
}
28+
29+
$registry->unregister('isSecureArea');
30+
$registry->register('isSecureArea', false);

0 commit comments

Comments
 (0)