Skip to content

Commit 583e1a5

Browse files
Merge branch '2.4-develop' into Tier4-PR-2024-08-05
2 parents f587a16 + ea32163 commit 583e1a5

File tree

60 files changed

+5956
-293
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+5956
-293
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,45 @@
11
<?php
2-
/**
3-
* Copyright © Magento, Inc. All rights reserved.
4-
* See COPYING.txt for license details.
2+
/************************************************************************
3+
*
4+
* Copyright 2024 Adobe
5+
* All Rights Reserved.
6+
*
7+
* NOTICE: All information contained herein is, and remains
8+
* the property of Adobe and its suppliers, if any. The intellectual
9+
* and technical concepts contained herein are proprietary to Adobe
10+
* and its suppliers and are protected by all applicable intellectual
11+
* property laws, including trade secret and copyright laws.
12+
* Dissemination of this information or reproduction of this material
13+
* is strictly forbidden unless prior written permission is obtained
14+
* from Adobe.
15+
* ************************************************************************
516
*/
617
declare(strict_types=1);
718

819
namespace Magento\BundleGraphQl\Model\Resolver;
920

21+
use Magento\Bundle\Model\Product\Price;
1022
use Magento\Catalog\Model\Product;
1123
use Magento\Framework\Exception\LocalizedException;
24+
use Magento\Framework\Exception\NoSuchEntityException;
1225
use Magento\Framework\GraphQl\Config\Element\Field;
1326
use Magento\Framework\GraphQl\Query\ResolverInterface;
1427
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
28+
use Magento\Quote\Api\CartRepositoryInterface;
29+
use Magento\Quote\Api\Data\CartItemInterface;
1530

1631
class BundlePriceDetails implements ResolverInterface
1732
{
33+
/**
34+
* BundlePriceDetails Constructor
35+
*
36+
* @param CartRepositoryInterface $cartRepository
37+
*/
38+
public function __construct(
39+
private readonly CartRepositoryInterface $cartRepository
40+
) {
41+
}
42+
1843
/**
1944
* @inheritdoc
2045
*/
@@ -25,14 +50,44 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value
2550
}
2651
/** @var Product $product */
2752
$product = $value['model'];
28-
2953
$price = $product->getPrice();
3054
$finalPrice = $product->getFinalPrice();
3155
$discountPercentage = ($price) ? (100 - (($finalPrice * 100) / $price)) : 0;
56+
if ((int)$product->getPriceType() === Price::PRICE_TYPE_DYNAMIC && isset($value['cart_item'])) {
57+
$discountPercentage = $this->getDiscountPercentageForBundleProduct($value['cart_item']);
58+
}
3259
return [
3360
'main_price' => $price,
3461
'main_final_price' => $finalPrice,
3562
'discount_percentage' => $discountPercentage
3663
];
3764
}
65+
66+
/**
67+
* Calculate discount percentage for bundle product with dynamic pricing enabled
68+
*
69+
* @param CartItemInterface $cartItem
70+
* @return float
71+
* @throws NoSuchEntityException
72+
*/
73+
private function getDiscountPercentageForBundleProduct(CartItemInterface $cartItem): float
74+
{
75+
if (empty($cartItem->getAppliedRuleIds())) {
76+
return 0;
77+
}
78+
$itemAmount = 0;
79+
$discountAmount = 0;
80+
$cart = $this->cartRepository->get($cartItem->getQuoteId());
81+
foreach ($cart->getAllItems() as $item) {
82+
if ($item->getParentItemId() == $cartItem->getId()) {
83+
$itemAmount += $item->getPrice();
84+
$discountAmount += $item->getDiscountAmount();
85+
}
86+
}
87+
if ($itemAmount && $discountAmount) {
88+
return ($discountAmount / $itemAmount) * 100;
89+
}
90+
91+
return 0;
92+
}
3893
}

app/code/Magento/CatalogInventoryGraphQl/Model/Resolver/NotAvailableMessageResolver.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value
4949
if ((int) $this->scopeConfig->getValue('cataloginventory/options/not_available_message') === 1) {
5050
return sprintf(
5151
'Only %s available for sale. Please adjust the quantity to continue',
52-
(string) $this->productStock->getProductAvailableStock($cartItem)
52+
(string) $this->productStock->getProductSaleableQty($cartItem)
5353
);
5454
}
5555

app/code/Magento/CatalogInventoryGraphQl/Model/Resolver/StockStatusProvider.php

+76-9
Original file line numberDiff line numberDiff line change
@@ -8,29 +8,51 @@
88
namespace Magento\CatalogInventoryGraphQl\Model\Resolver;
99

1010
use Magento\Catalog\Api\Data\ProductInterface;
11+
use Magento\Catalog\Api\ProductRepositoryInterface;
1112
use Magento\CatalogInventory\Api\Data\StockStatusInterface;
1213
use Magento\CatalogInventory\Api\StockStatusRepositoryInterface;
1314
use Magento\Framework\Exception\LocalizedException;
15+
use Magento\Framework\Exception\NoSuchEntityException;
1416
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
1517
use Magento\Framework\GraphQl\Config\Element\Field;
1618
use Magento\Framework\GraphQl\Query\ResolverInterface;
19+
use Magento\Quote\Model\Quote\Item;
1720

1821
/**
1922
* @inheritdoc
2023
*/
2124
class StockStatusProvider implements ResolverInterface
2225
{
2326
/**
24-
* @var StockStatusRepositoryInterface
27+
* Bundle product type code
2528
*/
26-
private $stockStatusRepository;
29+
private const PRODUCT_TYPE_BUNDLE = "bundle";
2730

2831
/**
32+
* Configurable product type code
33+
*/
34+
private const PRODUCT_TYPE_CONFIGURABLE = "configurable";
35+
36+
/**
37+
* In Stock return code
38+
*/
39+
private const IN_STOCK = "IN_STOCK";
40+
41+
/**
42+
* Out of Stock return code
43+
*/
44+
private const OUT_OF_STOCK = "OUT_OF_STOCK";
45+
46+
/**
47+
* StockStatusProvider Constructor
48+
*
2949
* @param StockStatusRepositoryInterface $stockStatusRepository
50+
* @param ProductRepositoryInterface $productRepositoryInterface
3051
*/
31-
public function __construct(StockStatusRepositoryInterface $stockStatusRepository)
32-
{
33-
$this->stockStatusRepository = $stockStatusRepository;
52+
public function __construct(
53+
private readonly StockStatusRepositoryInterface $stockStatusRepository,
54+
private readonly ProductRepositoryInterface $productRepositoryInterface,
55+
) {
3456
}
3557

3658
/**
@@ -41,13 +63,58 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value
4163
if (!array_key_exists('model', $value) || !$value['model'] instanceof ProductInterface) {
4264
throw new LocalizedException(__('"model" value should be specified'));
4365
}
66+
/** @var Item $cartItem */
67+
$cartItem = $value['cart_item'] ?? [];
68+
if (!$cartItem instanceof Item) {
69+
$product = $value['model'];
70+
$stockStatus = $this->stockStatusRepository->get($product->getId());
4471

45-
/* @var $product ProductInterface */
46-
$product = $value['model'];
72+
return ((int)$stockStatus->getStockStatus()) ? self::IN_STOCK : self::OUT_OF_STOCK;
73+
}
74+
75+
if ($cartItem->getProductType() === self::PRODUCT_TYPE_BUNDLE) {
76+
return $this->getBundleProductStockStatus($cartItem);
77+
}
4778

79+
$product = $this->getVariantProduct($cartItem) ?? $cartItem->getProduct();
4880
$stockStatus = $this->stockStatusRepository->get($product->getId());
49-
$productStockStatus = (int)$stockStatus->getStockStatus();
5081

51-
return $productStockStatus === StockStatusInterface::STATUS_IN_STOCK ? 'IN_STOCK' : 'OUT_OF_STOCK';
82+
return ((int)$stockStatus->getStockStatus()) ? self::IN_STOCK : self::OUT_OF_STOCK;
83+
}
84+
85+
/**
86+
* Get stock status of added bundle options
87+
*
88+
* @param Item $cartItem
89+
* @return string
90+
*/
91+
private function getBundleProductStockStatus(Item $cartItem): string
92+
{
93+
$qtyOptions = $cartItem->getQtyOptions();
94+
foreach ($qtyOptions as $qtyOption) {
95+
$stockStatus = $this->stockStatusRepository->get($qtyOption->getProduct()->getId());
96+
if (!(int)$stockStatus->getStockStatus()) {
97+
return self::OUT_OF_STOCK;
98+
}
99+
}
100+
101+
return self::IN_STOCK;
102+
}
103+
104+
/**
105+
* Returns variant product if available
106+
*
107+
* @param Item $cartItem
108+
* @return ProductInterface|null
109+
* @throws NoSuchEntityException
110+
*/
111+
private function getVariantProduct(Item $cartItem): ?ProductInterface
112+
{
113+
if ($cartItem->getProductType() === self::PRODUCT_TYPE_CONFIGURABLE) {
114+
if ($cartItem->getChildren()[0] !== null) {
115+
return $this->productRepositoryInterface->get($cartItem->getSku());
116+
}
117+
}
118+
return null;
52119
}
53120
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
<?php
2+
/**
3+
* Copyright 2024 Adobe
4+
* All Rights Reserved.
5+
*
6+
* NOTICE: All information contained herein is, and remains
7+
* the property of Adobe and its suppliers, if any. The intellectual
8+
* and technical concepts contained herein are proprietary to Adobe
9+
* and its suppliers and are protected by all applicable intellectual
10+
* property laws, including trade secret and copyright laws.
11+
* Dissemination of this information or reproduction of this material
12+
* is strictly forbidden unless prior written permission is obtained
13+
* from Adobe.
14+
*/
15+
declare(strict_types=1);
16+
17+
namespace Magento\GroupedProductGraphQl\Plugin\Model\Resolver;
18+
19+
use Magento\CatalogGraphQl\Model\Resolver\Product\ProductImage as Subject;
20+
use Magento\Framework\GraphQl\Config\Element\Field;
21+
use Magento\Framework\GraphQl\Query\Resolver\ContextInterface;
22+
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
23+
use Magento\Quote\Model\Quote\Item;
24+
use Magento\StoreGraphQl\Model\Resolver\Store\StoreConfigDataProvider;
25+
26+
class ProductImagePlugin
27+
{
28+
private const CONF_GROUPED_PRODUCT_IMAGE = 'grouped_product_image';
29+
30+
private const CONF_GROUPED_PRODUCT_IMAGE_PARENT = 'parent';
31+
32+
private const FIELD_THUMBNAIL = 'thumbnail';
33+
34+
private const PRODUCT_TYPE_SIMPLE = 'simple';
35+
36+
private const PRODUCT_TYPE_GROUPED = 'grouped';
37+
38+
/**
39+
* ProductImagePlugin Constructor
40+
*
41+
* @param StoreConfigDataProvider $storeConfigDataProvider
42+
*/
43+
public function __construct(
44+
private readonly StoreConfigDataProvider $storeConfigDataProvider,
45+
) {
46+
}
47+
48+
/**
49+
* Update product thumbnail URL to parent product's thumbnail URL for grouped product
50+
*
51+
* @param Subject $subject
52+
* @param array $returnArray
53+
* @param Field $field
54+
* @param ContextInterface $context
55+
* @param ResolveInfo $info
56+
* @param array|null $value
57+
* @param array|null $args
58+
* @return array
59+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
60+
*/
61+
public function afterResolve(
62+
Subject $subject,
63+
array $returnArray,
64+
Field $field,
65+
ContextInterface $context,
66+
ResolveInfo $info,
67+
array $value = null,
68+
array $args = null
69+
): array {
70+
71+
/* @var $cartItem Item */
72+
$cartItem = $value['cart_item'] ?? [];
73+
if (!$cartItem instanceof Item ||
74+
!isset($cartItem['product_type']) ||
75+
$cartItem['product_type'] !== self::PRODUCT_TYPE_SIMPLE ||
76+
$field->getName() !== self::FIELD_THUMBNAIL) {
77+
return $returnArray;
78+
}
79+
80+
$storeConfigData = $this->storeConfigDataProvider->getStoreConfigData(
81+
$context->getExtensionAttributes()->getStore()
82+
);
83+
if ($storeConfigData[self::CONF_GROUPED_PRODUCT_IMAGE] !== self::CONF_GROUPED_PRODUCT_IMAGE_PARENT) {
84+
return $returnArray;
85+
}
86+
87+
return $this->updateThumbnailToParentThumbnail($cartItem, $returnArray);
88+
}
89+
90+
/**
91+
* Update thumbnail URL to parent thumbnail URL
92+
*
93+
* @param Item $cartItem
94+
* @param array $returnArray
95+
* @return array
96+
*/
97+
private function updateThumbnailToParentThumbnail(Item $cartItem, array $returnArray): array
98+
{
99+
foreach ($cartItem->getOptions() as $option) {
100+
$parentProduct = $option->getProduct();
101+
102+
if ($parentProduct->getTypeId() === self::PRODUCT_TYPE_GROUPED && $parentProduct->getThumbnail()) {
103+
$returnArray['model']['thumbnail'] = $parentProduct->getThumbnail();
104+
break;
105+
}
106+
}
107+
108+
return $returnArray;
109+
}
110+
}

app/code/Magento/GroupedProductGraphQl/composer.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
"magento/module-grouped-product": "*",
88
"magento/module-catalog": "*",
99
"magento/module-catalog-graph-ql": "*",
10-
"magento/framework": "*"
10+
"magento/framework": "*",
11+
"magento/module-store-graph-ql": "*",
12+
"magento/module-quote": "*"
1113
},
1214
"license": [
1315
"OSL-3.0",

app/code/Magento/GroupedProductGraphQl/etc/graphql/di.xml

+4
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,8 @@
5353
</argument>
5454
</arguments>
5555
</type>
56+
57+
<type name="Magento\CatalogGraphQl\Model\Resolver\Product\ProductImage">
58+
<plugin name="updateThumbnailToParentProductThumbnail" type="Magento\GroupedProductGraphQl\Plugin\Model\Resolver\ProductImagePlugin" />
59+
</type>
5660
</config>

0 commit comments

Comments
 (0)