Skip to content

Commit fdce4a4

Browse files
committed
BUG#AC-1881: GraphQL Bundle Options could not resolved via customer query - Issue fixed
1 parent 43259af commit fdce4a4

File tree

5 files changed

+318
-5
lines changed

5 files changed

+318
-5
lines changed

app/code/Magento/Backend/Test/Mftf/ActionGroup/AdminLoginActionGroup.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313
<description>Login to Backend Admin using provided User Data. PLEASE NOTE: This Action Group does NOT validate that you are Logged In.</description>
1414
</annotations>
1515
<arguments>
16-
<argument name="username" type="string" defaultValue="{{_CREDS.magento/MAGENTO_ADMIN_USERNAME}}"/>
17-
<argument name="password" type="string" defaultValue="{{_CREDS.magento/MAGENTO_ADMIN_PASSWORD}}"/>
16+
<argument name="username" type="string" defaultValue="{{_ENV.MAGENTO_ADMIN_USERNAME}}"/>
17+
<argument name="password" type="string" defaultValue="{{_ENV.MAGENTO_ADMIN_PASSWORD}}"/>
1818
</arguments>
1919

2020
<amOnPage url="{{AdminLoginPage.url}}" stepKey="navigateToAdmin"/>

app/code/Magento/BundleGraphQl/Model/Resolver/Order/Item/BundleOptions.php

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,6 @@ class BundleOptions implements ResolverInterface
3030
private const OPTION_TYPE = 'bundle';
3131

3232
/**
33-
* Serializer
34-
*
3533
* @var Json
3634
*/
3735
private $serializer;
@@ -148,7 +146,7 @@ private function formatBundleOptionItems(
148146
$optionDetails = [
149147
self::OPTION_TYPE,
150148
$bundleChildAttributes['option_id'],
151-
implode(',', $options),
149+
is_array($options) ? implode(',', $options) : $options,
152150
(int) $childOrderItemOptions['info_buyRequest']['qty']
153151
];
154152

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\GraphQl\Sales;
9+
10+
use Magento\Framework\Exception\AuthenticationException;
11+
use Magento\Framework\Registry;
12+
use Magento\GraphQl\GetCustomerAuthenticationHeader;
13+
use Magento\GraphQl\Sales\Fixtures\CustomerPlaceOrder;
14+
use Magento\Sales\Api\OrderRepositoryInterface;
15+
use Magento\Sales\Model\Order;
16+
use Magento\Sales\Model\ResourceModel\Order\Collection;
17+
use Magento\TestFramework\Helper\Bootstrap;
18+
use Magento\TestFramework\TestCase\GraphQlAbstract;
19+
20+
/**
21+
* Test for orders with bundle product.
22+
*/
23+
class RetrieveOrdersWithBundleProductOptionsTest extends GraphQlAbstract
24+
{
25+
/** @var OrderRepositoryInterface */
26+
private $orderRepository;
27+
28+
/** @var GetCustomerAuthenticationHeader */
29+
private $customerAuthenticationHeader;
30+
31+
protected function setUp(): void
32+
{
33+
parent::setUp();
34+
$objectManager = Bootstrap::getObjectManager();
35+
$this->customerAuthenticationHeader = $objectManager->get(GetCustomerAuthenticationHeader::class);
36+
$this->orderRepository = $objectManager->get(OrderRepositoryInterface::class);
37+
}
38+
39+
protected function tearDown(): void
40+
{
41+
$this->deleteOrder();
42+
}
43+
44+
/**
45+
* Test customer order details for bundle product single child item
46+
*
47+
* @magentoApiDataFixture Magento/Customer/_files/customer.php
48+
* @magentoApiDataFixture Magento/Bundle/_files/bundle_product_single_dropdown_option.php
49+
*/
50+
public function testGetCustomerOrderBundleProduct(): void
51+
{
52+
$qty = 1;
53+
$bundleSku = 'bundle-product-single-dropdown-option';
54+
/** @var CustomerPlaceOrder $bundleProductOrderFixture */
55+
$bundleProductOrderFixture = Bootstrap::getObjectManager()->create(CustomerPlaceOrder::class);
56+
$bundleProductOrderFixture->placeOrderWithBundleProduct(
57+
['email' => 'customer@example.com', 'password' => 'password'],
58+
['sku' => $bundleSku, 'quantity' => $qty]
59+
);
60+
$customerOrderResponse = $this->getCustomerOrderQueryBundleProduct();
61+
$customerOrderItems = $customerOrderResponse[0];
62+
$this->assertEquals("Pending", $customerOrderItems['status']);
63+
$bundledItemInTheOrder = $customerOrderItems['items'][0];
64+
$this->assertEquals(
65+
'bundle-product-single-dropdown-option-simple1',
66+
$bundledItemInTheOrder['product_sku']
67+
);
68+
$this->assertArrayHasKey('bundle_options', $bundledItemInTheOrder);
69+
$bundleOptionsFromResponse = $bundledItemInTheOrder['bundle_options'];
70+
$this->assertNotEmpty($bundleOptionsFromResponse);
71+
$this->assertEquals(1, count($bundleOptionsFromResponse));
72+
$expectedBundleOptions =
73+
[
74+
['__typename' => 'ItemSelectedBundleOption',
75+
'label' => 'Drop Down Option 1',
76+
'values' => [
77+
[
78+
'product_name' => 'Simple Product1',
79+
'product_sku' => 'simple1',
80+
'price' => [
81+
'value' => 1,
82+
'currency' => 'USD'
83+
]
84+
]
85+
]
86+
],
87+
];
88+
$this->assertEquals($expectedBundleOptions, $bundleOptionsFromResponse);
89+
}
90+
91+
/**
92+
* Get customer order query for bundle order items
93+
*
94+
* @return mixed
95+
* @throws AuthenticationException
96+
*/
97+
private function getCustomerOrderQueryBundleProduct()
98+
{
99+
$query =
100+
<<<QUERY
101+
{
102+
customer {
103+
orders(pageSize: 20) {
104+
items {
105+
id
106+
order_date
107+
items {
108+
product_name
109+
product_sku
110+
... on BundleOrderItem {
111+
bundle_options {
112+
__typename
113+
label
114+
values {
115+
product_name
116+
product_sku
117+
price {
118+
value
119+
currency
120+
}
121+
}
122+
}
123+
}
124+
}
125+
total {
126+
grand_total {
127+
value
128+
currency
129+
}
130+
}
131+
status
132+
}
133+
}
134+
}
135+
}
136+
QUERY;
137+
$response = $this->graphQlQuery(
138+
$query,
139+
[],
140+
'',
141+
$this->customerAuthenticationHeader->execute('customer@example.com', 'password')
142+
);
143+
144+
$this->assertArrayHasKey('orders', $response['customer']);
145+
$this->assertArrayHasKey('items', $response['customer']['orders']);
146+
return $response['customer']['orders']['items'];
147+
}
148+
149+
/**
150+
* @return void
151+
*/
152+
private function deleteOrder(): void
153+
{
154+
/** @var Registry $registry */
155+
$registry = Bootstrap::getObjectManager()->get(Registry::class);
156+
$registry->unregister('isSecureArea');
157+
$registry->register('isSecureArea', true);
158+
159+
/** @var $order Order */
160+
$orderCollection = Bootstrap::getObjectManager()->create(Collection::class);
161+
foreach ($orderCollection as $order) {
162+
$this->orderRepository->delete($order);
163+
}
164+
165+
$registry->unregister('isSecureArea');
166+
$registry->register('isSecureArea', false);
167+
}
168+
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
use Magento\Bundle\Api\Data\LinkInterfaceFactory;
8+
use Magento\Bundle\Api\Data\OptionInterfaceFactory;
9+
use Magento\Catalog\Api\ProductRepositoryInterface;
10+
use Magento\Catalog\Model\Product;
11+
use Magento\Catalog\Model\Product\Attribute\Source\Status;
12+
use Magento\Catalog\Model\Product\Type;
13+
use Magento\Catalog\Model\Product\Visibility;
14+
use Magento\CatalogInventory\Model\Stock\Item;
15+
use Magento\TestFramework\Helper\Bootstrap;
16+
use Magento\TestFramework\Workaround\Override\Fixture\Resolver;
17+
18+
Resolver::getInstance()->requireDataFixture('Magento/Catalog/_files/multiple_products.php');
19+
20+
$objectManager = Bootstrap::getObjectManager();
21+
22+
/** @var Item $stockItem */
23+
$stockItem = $objectManager->create(Item::class);
24+
$stockItem->load(10, 'product_id');
25+
26+
if (!$stockItem->getProductId()) {
27+
$stockItem->setProductId(10);
28+
}
29+
30+
$stockItem->setUseConfigManageStock(1);
31+
$stockItem->setQty(1000);
32+
$stockItem->setIsQtyDecimal(0);
33+
$stockItem->setIsInStock(1);
34+
$stockItem->save();
35+
36+
/** @var $product Product */
37+
$product = $objectManager->create(Product::class);
38+
$product->setTypeId(Type::TYPE_BUNDLE)
39+
->setAttributeSetId(4)
40+
->setWebsiteIds([1])
41+
->setName('Bundle Product With Single dropdown option')
42+
->setSku('bundle-product-single-dropdown-option')
43+
->setVisibility(Visibility::VISIBILITY_BOTH)
44+
->setStatus(Status::STATUS_ENABLED)
45+
->setStockData(['use_config_manage_stock' => 1, 'qty' => 100, 'is_qty_decimal' => 0, 'is_in_stock' => 1])
46+
->setPriceView(1)
47+
->setPriceType(1)
48+
->setPrice(10.0)
49+
->setShipmentType(0)
50+
->setBundleOptionsData(
51+
[
52+
[
53+
'title' => 'Drop Down Option 1',
54+
'default_title' => 'Option 1',
55+
'type' => 'select',
56+
'required' => 0,
57+
'position' => 1,
58+
'delete' => '',
59+
],
60+
]
61+
)->setBundleSelectionsData(
62+
[
63+
[
64+
[
65+
'product_id' => 10,
66+
'selection_qty' => 1,
67+
'selection_price_value' => 1.00,
68+
'selection_can_change_qty' => 1,
69+
'delete' => '',
70+
'option_id' => 1,
71+
'option_id' => 1,
72+
],
73+
],
74+
]
75+
);
76+
$productRepository = $objectManager->create(ProductRepositoryInterface::class);
77+
78+
if ($product->getBundleOptionsData()) {
79+
$options = [];
80+
foreach ($product->getBundleOptionsData() as $key => $optionData) {
81+
if (!$optionData['delete']) {
82+
$option = $objectManager->create(OptionInterfaceFactory::class)
83+
->create(['data' => $optionData]);
84+
$option->setSku($product->getSku());
85+
$option->setOptionId(null);
86+
87+
$links = [];
88+
$bundleLinks = $product->getBundleSelectionsData();
89+
if (!empty($bundleLinks[$key])) {
90+
foreach ($bundleLinks[$key] as $linkData) {
91+
if (!$linkData['delete']) {
92+
$link = $objectManager->create(LinkInterfaceFactory::class)
93+
->create(['data' => $linkData]);
94+
$linkProduct = $productRepository->getById($linkData['product_id']);
95+
$link->setSku($linkProduct->getSku());
96+
$link->setQty($linkData['selection_qty']);
97+
$link->setPrice($linkData['selection_price_value']);
98+
if (isset($linkData['selection_can_change_qty'])) {
99+
$link->setCanChangeQuantity($linkData['selection_can_change_qty']);
100+
}
101+
$links[] = $link;
102+
}
103+
}
104+
$option->setProductLinks($links);
105+
$options[] = $option;
106+
}
107+
}
108+
}
109+
$extension = $product->getExtensionAttributes();
110+
$extension->setBundleProductOptions($options);
111+
$product->setExtensionAttributes($extension);
112+
}
113+
$productRepository->save($product, true);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
use Magento\Catalog\Api\ProductRepositoryInterface;
9+
use Magento\Framework\Exception\NoSuchEntityException;
10+
use Magento\Framework\Registry;
11+
use Magento\TestFramework\Helper\Bootstrap;
12+
use Magento\TestFramework\Workaround\Override\Fixture\Resolver;
13+
use Psr\Log\LoggerInterface;
14+
15+
Resolver::getInstance()->requireDataFixture('Magento/Catalog/_files/multiple_products_rollback.php');
16+
17+
$objectManager = Bootstrap::getObjectManager();
18+
/** @var Registry $registry */
19+
$registry = $objectManager->get(Registry::class);
20+
$registry->unregister('isSecureArea');
21+
$registry->register('isSecureArea', true);
22+
/** @var ProductRepositoryInterface $productRepository */
23+
$productRepository = $objectManager->get(ProductRepositoryInterface::class);
24+
25+
try {
26+
$product = $productRepository->get('bundle-product-single-dropdown-option', false, null, true);
27+
$productRepository->delete($product);
28+
} catch (NoSuchEntityException $exception) {
29+
$logger = $objectManager->get(LoggerInterface::class);
30+
$logger->log($exception->getCode(), $exception->getMessage());
31+
}
32+
33+
$registry->unregister('isSecureArea');
34+
$registry->register('isSecureArea', false);

0 commit comments

Comments
 (0)