Skip to content

Commit d400205

Browse files
authored
Merge branch '2.4-develop' into 2.4-develop
2 parents 72bb6ef + d01ee51 commit d400205

File tree

73 files changed

+6571
-366
lines changed

Some content is hidden

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

73 files changed

+6571
-366
lines changed
Lines changed: 59 additions & 4 deletions
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/Catalog/Block/Rss/Product/NewProducts.php

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,13 @@
55
*/
66
namespace Magento\Catalog\Block\Rss\Product;
77

8-
use Magento\Framework\App\Rss\DataProviderInterface;
8+
use Magento\Framework\App\Rss\DataProviderInterface as DProviderInterface;
9+
use Magento\Framework\DataObject\IdentityInterface as IdInterface;
910

10-
/**
11-
* Class NewProducts
12-
* @package Magento\Catalog\Block\Rss\Product
13-
*/
14-
class NewProducts extends \Magento\Framework\View\Element\AbstractBlock implements DataProviderInterface
11+
class NewProducts extends \Magento\Framework\View\Element\AbstractBlock implements DProviderInterface, IdInterface
1512
{
13+
public const CACHE_TAG = 'rss_p_new';
14+
1615
/**
1716
* @var \Magento\Catalog\Helper\Image
1817
*/
@@ -55,6 +54,8 @@ public function __construct(
5554
}
5655

5756
/**
57+
* Configure class
58+
*
5859
* @return void
5960
*/
6061
protected function _construct()
@@ -64,15 +65,15 @@ protected function _construct()
6465
}
6566

6667
/**
67-
* {@inheritdoc}
68+
* @inheritdoc
6869
*/
6970
public function isAllowed()
7071
{
7172
return $this->_scopeConfig->isSetFlag('rss/catalog/new', \Magento\Store\Model\ScopeInterface::SCOPE_STORE);
7273
}
7374

7475
/**
75-
* {@inheritdoc}
76+
* @inheritdoc
7677
*/
7778
public function getRssData()
7879
{
@@ -132,6 +133,8 @@ public function getRssData()
132133
}
133134

134135
/**
136+
* Get current store id
137+
*
135138
* @return int
136139
*/
137140
protected function getStoreId()
@@ -177,14 +180,16 @@ protected function renderPriceHtml(\Magento\Catalog\Model\Product $product)
177180
}
178181

179182
/**
180-
* {@inheritdoc}
183+
* @inheritdoc
181184
*/
182185
public function getCacheLifetime()
183186
{
184187
return 600;
185188
}
186189

187190
/**
191+
* Generate rss feed
192+
*
188193
* @return array
189194
*/
190195
public function getFeeds()
@@ -199,10 +204,18 @@ public function getFeeds()
199204
}
200205

201206
/**
202-
* {@inheritdoc}
207+
* @inheritdoc
203208
*/
204209
public function isAuthRequired()
205210
{
206211
return false;
207212
}
213+
214+
/**
215+
* @inheritdoc
216+
*/
217+
public function getIdentities()
218+
{
219+
return [self::CACHE_TAG];
220+
}
208221
}

app/code/Magento/Catalog/Model/Product.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2413,6 +2413,7 @@ public function getIdentities()
24132413
|| $this->isObjectNew();
24142414
if ($isProductNew && ($isStatusChanged || $this->getStatus() == Status::STATUS_ENABLED)) {
24152415
$identities[] = \Magento\Catalog\Block\Product\NewProduct::CACHE_TAG;
2416+
$identities[] = \Magento\Catalog\Block\Rss\Product\NewProducts::CACHE_TAG;
24162417
}
24172418

24182419
return array_unique($identities);

app/code/Magento/Catalog/Test/Unit/Model/ProductTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -983,7 +983,7 @@ private function getNoStockStatusChangesData(MockObject $extensionAttributesMock
983983
private function getNewProductProviderData(): array
984984
{
985985
return [
986-
['cat_p_1', 'cat_c_p_1', 'cat_p_new'],
986+
['cat_p_1', 'cat_c_p_1', 'cat_p_new', 'rss_p_new'],
987987
null,
988988
[
989989
'id' => 1,

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

Lines changed: 1 addition & 1 deletion
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

Lines changed: 76 additions & 9 deletions
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
}

0 commit comments

Comments
 (0)