From fddd1b6136ed37f050729a20a3cff3a1a3008a00 Mon Sep 17 00:00:00 2001 From: Senthilkumar Muppidathi Date: Fri, 14 Nov 2025 13:12:50 +0530 Subject: [PATCH 1/5] #40235 - Conditionally process collectTotal only if the value of requested fields set during the collectTotals process --- .../Model/Resolver/CartItemPrices.php | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/CartItemPrices.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/CartItemPrices.php index abbd81475e77d..a99b07376cc06 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/CartItemPrices.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/CartItemPrices.php @@ -19,6 +19,7 @@ use Magento\QuoteGraphQl\Model\Cart\TotalsCollector; use Magento\QuoteGraphQl\Model\GetDiscounts; use Magento\QuoteGraphQl\Model\GetOptionsRegularPrice; +use Magento\Bundle\Model\Product\Type as BundleType; /** * @inheritdoc @@ -64,7 +65,13 @@ public function resolve(Field $field, $context, ResolveInfo $info, ?array $value } /** @var Item $cartItem */ $cartItem = $value['model']; - if (!$this->totals) { + // Collect totals only if discount, original item price and original rowtotal is there in the request + // avoid retrieve totals with the below keys if its not absolutely required + if (!$this->totals && !empty(array_intersect( + ['discounts', 'original_item_price', 'original_row_total'], + array_keys($info->getFieldSelection(1)) + )) + ) { // The totals calculation is based on quote address. // But the totals should be calculated even if no address is set $this->totals = $this->totalsCollector->collectQuoteTotals($cartItem->getQuote()); @@ -72,14 +79,11 @@ public function resolve(Field $field, $context, ResolveInfo $info, ?array $value $currencyCode = $cartItem->getQuote()->getQuoteCurrencyCode(); /** calculate bundle product discount */ - if ($cartItem->getProductType() == 'bundle') { - $discounts = $cartItem->getExtensionAttributes()->getDiscounts() ?? []; - $discountAmount = 0; - foreach ($discounts as $discount) { - $discountAmount += $discount->getDiscountData()->getAmount(); + $discountAmount = 0; + if ($cartItem->getProductType() == BundleType::TYPE_CODE) { + foreach ($cartItem->getChildren() as $childItem) { + $discountAmount += $childItem->getDiscountAmount(); } - } else { - $discountAmount = $cartItem->getDiscountAmount(); } return [ From b35222802e6b70660bc65c3457efef202e906022 Mon Sep 17 00:00:00 2001 From: Senthilkumar Muppidathi Date: Fri, 14 Nov 2025 13:14:34 +0530 Subject: [PATCH 2/5] #40235 - Adding the missed Discount --- app/code/Magento/QuoteGraphQl/Model/Resolver/CartItemPrices.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/CartItemPrices.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/CartItemPrices.php index a99b07376cc06..06eb09711ee6e 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/CartItemPrices.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/CartItemPrices.php @@ -86,6 +86,8 @@ public function resolve(Field $field, $context, ResolveInfo $info, ?array $value } } + $discountAmount += $cartItem->getDiscountAmount(); + return [ 'model' => $cartItem, 'price' => [ From a15df221bff708d3db93a633e815b590bc1c77c0 Mon Sep 17 00:00:00 2001 From: Senthilkumar Muppidathi Date: Sun, 16 Nov 2025 11:46:29 +0530 Subject: [PATCH 3/5] #40235 - Fixed Test Failures & added unit test --- .../Model/Resolver/CartItemPrices.php | 8 +- .../Model/Resolver/CartItemPricesTest.php | 215 ++++++++++++++++++ .../GetMaskedQuoteIdByReservedOrderId.php | 8 +- .../Weee/CartItemPricesWithFPTTest.php | 3 +- 4 files changed, 231 insertions(+), 3 deletions(-) create mode 100644 app/code/Magento/QuoteGraphQl/Test/Unit/Model/Resolver/CartItemPricesTest.php diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/CartItemPrices.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/CartItemPrices.php index 06eb09711ee6e..d45fddfdb47ca 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/CartItemPrices.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/CartItemPrices.php @@ -65,10 +65,15 @@ public function resolve(Field $field, $context, ResolveInfo $info, ?array $value } /** @var Item $cartItem */ $cartItem = $value['model']; + // Collect totals only if discount, original item price and original rowtotal is there in the request // avoid retrieve totals with the below keys if its not absolutely required + // except discounts can be removed if original_item_price and original_row_total is saved in db if (!$this->totals && !empty(array_intersect( - ['discounts', 'original_item_price', 'original_row_total'], + [ + 'discounts', 'original_item_price', 'original_row_total', + 'catalog_discount', 'row_catalog_discount' + ], array_keys($info->getFieldSelection(1)) )) ) { @@ -76,6 +81,7 @@ public function resolve(Field $field, $context, ResolveInfo $info, ?array $value // But the totals should be calculated even if no address is set $this->totals = $this->totalsCollector->collectQuoteTotals($cartItem->getQuote()); } + $currencyCode = $cartItem->getQuote()->getQuoteCurrencyCode(); /** calculate bundle product discount */ diff --git a/app/code/Magento/QuoteGraphQl/Test/Unit/Model/Resolver/CartItemPricesTest.php b/app/code/Magento/QuoteGraphQl/Test/Unit/Model/Resolver/CartItemPricesTest.php new file mode 100644 index 0000000000000..187dffdbd6baa --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/Test/Unit/Model/Resolver/CartItemPricesTest.php @@ -0,0 +1,215 @@ +totalsCollectorMock = $this->createMock(TotalsCollector::class); + $this->getDiscountsMock = $this->createMock(GetDiscounts::class); + $this->priceCurrencyMock = $this->createMock(PriceCurrencyInterface::class); + $this->getOptionsRegularPriceMock = $this->createMock(GetOptionsRegularPrice::class); + $this->productMock = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->onlyMethods(['getCustomOption']) + ->getMock(); + $this->fieldMock = $this->createMock(Field::class); + $this->resolveInfoMock = $this->createMock(ResolveInfo::class); + $this->contextMock = $this->createMock(Context::class); + $this->quoteMock = $this->getMockBuilder(Quote::class) + ->disableOriginalConstructor() + ->addMethods(['getQuoteCurrencyCode']) + ->getMock(); + $this->itemMock = $this->getMockBuilder(Item::class) + ->addMethods([ + 'getPriceInclTax', 'getRowTotal', + 'getRowTotalInclTax', 'getDiscountAmount' + ]) + ->onlyMethods([ + 'getCalculationPrice', 'getQuote', 'getExtensionAttributes', + 'getProduct', 'getOriginalPrice' + ]) + ->disableOriginalConstructor() + ->getMock(); + $this->itemExtensionMock = $this->getMockBuilder( + ExtensionAttributesInterface::class + )->addMethods(['getDiscounts'])->getMockForAbstractClass(); + + $this->cartItemPrices = new CartItemPrices( + $this->totalsCollectorMock, + $this->getDiscountsMock, + $this->priceCurrencyMock, + $this->getOptionsRegularPriceMock + ); + } + + public function testResolve(): void + { + $this->valueMock = ['model' => $this->itemMock]; + + $this->resolveInfoMock->expects($this->once()) + ->method('getFieldSelection') + ->with(1) + ->willReturn([]); + + $this->itemMock + ->expects($this->exactly(2)) + ->method('getQuote') + ->willReturn($this->quoteMock); + + $this->quoteMock + ->expects($this->once()) + ->method('getQuoteCurrencyCode') + ->willReturn('USD'); + + $this->itemMock + ->expects($this->once()) + ->method('getDiscountAmount'); + + $this->itemMock + ->expects($this->once()) + ->method('getCalculationPrice'); + + $this->itemMock + ->expects($this->once()) + ->method('getPriceInclTax'); + + $this->itemMock + ->expects($this->any()) + ->method('getOriginalPrice') + ->willReturn(0); + + $this->itemMock + ->expects($this->once()) + ->method('getRowTotal'); + + $this->itemMock + ->expects($this->once()) + ->method('getRowTotalInclTax'); + + $this->itemMock + ->expects($this->once()) + ->method('getExtensionAttributes') + ->willReturn($this->itemExtensionMock); + + $this->itemMock + ->expects($this->any()) + ->method('getProduct') + ->willReturn($this->productMock); + + $this->productMock + ->expects($this->exactly(2)) + ->method('getCustomOption') + ->willReturn(null); + + $this->itemExtensionMock + ->expects($this->once()) + ->method('getDiscounts') + ->willReturn([]); + + $this->getDiscountsMock + ->expects($this->once()) + ->method('execute') + ->with($this->quoteMock, []); + + $this->cartItemPrices->resolve($this->fieldMock, $this->contextMock, $this->resolveInfoMock, $this->valueMock); + } + + public function testResolveWithoutModelInValueParameter(): void + { + $this->expectException(LocalizedException::class); + $this->expectExceptionMessage('"model" value should be specified'); + $this->cartItemPrices->resolve($this->fieldMock, $this->contextMock, $this->resolveInfoMock, $this->valueMock); + } +} + diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetMaskedQuoteIdByReservedOrderId.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetMaskedQuoteIdByReservedOrderId.php index 49dfafca5c14d..bdd3d219a8c0f 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetMaskedQuoteIdByReservedOrderId.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetMaskedQuoteIdByReservedOrderId.php @@ -51,15 +51,21 @@ public function __construct( * Get masked quote id by reserved order id * * @param string $reservedOrderId + * @param bool $forceCollectTotal * @return string * @throws NoSuchEntityException */ - public function execute(string $reservedOrderId): string + public function execute(string $reservedOrderId, bool $forceCollectTotal=false): string { $quote = $this->quoteFactory->create(); $quote->setSharedStoreIds(['*']); $this->quoteResource->load($quote, $reservedOrderId, 'reserved_order_id'); + // If dataprovider is used, we need to collect totals manually and save quote + if ($forceCollectTotal) { + $this->quoteResource->save($quote->collectTotals()); + } + return $this->quoteIdToMaskedId->execute((int)$quote->getId()); } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Weee/CartItemPricesWithFPTTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Weee/CartItemPricesWithFPTTest.php index 99bd59eb34181..d3e34fb43a0db 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Weee/CartItemPricesWithFPTTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Weee/CartItemPricesWithFPTTest.php @@ -113,7 +113,8 @@ private function writeConfig(array $settings): void public function testCartItemFixedProductTax(array $taxSettings, array $expectedFtps): void { $this->writeConfig($taxSettings); - $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + // Second argument true is to ensure collectTotals and save after every quote update + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote', true); $query = $this->getQuery($maskedQuoteId); $result = $this->graphQlQuery($query); $this->assertArrayNotHasKey('errors', $result); From f4f87f834923ffba91075ea62ce0800a18ff8b33 Mon Sep 17 00:00:00 2001 From: Senthilkumar Muppidathi Date: Sun, 16 Nov 2025 12:42:55 +0530 Subject: [PATCH 4/5] #40235 - Removed Bundle product dependency --- .../Magento/QuoteGraphQl/Model/Resolver/CartItemPrices.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/CartItemPrices.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/CartItemPrices.php index d45fddfdb47ca..804d8120e6e06 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/CartItemPrices.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/CartItemPrices.php @@ -19,7 +19,6 @@ use Magento\QuoteGraphQl\Model\Cart\TotalsCollector; use Magento\QuoteGraphQl\Model\GetDiscounts; use Magento\QuoteGraphQl\Model\GetOptionsRegularPrice; -use Magento\Bundle\Model\Product\Type as BundleType; /** * @inheritdoc @@ -86,7 +85,7 @@ public function resolve(Field $field, $context, ResolveInfo $info, ?array $value /** calculate bundle product discount */ $discountAmount = 0; - if ($cartItem->getProductType() == BundleType::TYPE_CODE) { + if ($cartItem->getProductType() == 'bundle') { foreach ($cartItem->getChildren() as $childItem) { $discountAmount += $childItem->getDiscountAmount(); } From 1d3f9efc2adc0d1b79bb9d7ffce3febe8a8746d2 Mon Sep 17 00:00:00 2001 From: Senthilkumar Muppidathi Date: Sun, 16 Nov 2025 14:03:02 +0530 Subject: [PATCH 5/5] #40235 - Fixed Static Test Failures --- .../Test/Unit/Model/Resolver/CartItemPricesTest.php | 2 +- .../GraphQl/Quote/GetMaskedQuoteIdByReservedOrderId.php | 9 +++------ .../Magento/GraphQl/Weee/CartItemPricesWithFPTTest.php | 4 ++-- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/app/code/Magento/QuoteGraphQl/Test/Unit/Model/Resolver/CartItemPricesTest.php b/app/code/Magento/QuoteGraphQl/Test/Unit/Model/Resolver/CartItemPricesTest.php index 187dffdbd6baa..c0bd9409f1393 100644 --- a/app/code/Magento/QuoteGraphQl/Test/Unit/Model/Resolver/CartItemPricesTest.php +++ b/app/code/Magento/QuoteGraphQl/Test/Unit/Model/Resolver/CartItemPricesTest.php @@ -25,6 +25,7 @@ /** * @see CartItemPrices + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class CartItemPricesTest extends TestCase { @@ -212,4 +213,3 @@ public function testResolveWithoutModelInValueParameter(): void $this->cartItemPrices->resolve($this->fieldMock, $this->contextMock, $this->resolveInfoMock, $this->valueMock); } } - diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetMaskedQuoteIdByReservedOrderId.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetMaskedQuoteIdByReservedOrderId.php index bdd3d219a8c0f..bf6dc74826010 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetMaskedQuoteIdByReservedOrderId.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/GetMaskedQuoteIdByReservedOrderId.php @@ -1,7 +1,7 @@ quoteFactory->create(); $quote->setSharedStoreIds(['*']); diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Weee/CartItemPricesWithFPTTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Weee/CartItemPricesWithFPTTest.php index d3e34fb43a0db..78e3270be4057 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Weee/CartItemPricesWithFPTTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Weee/CartItemPricesWithFPTTest.php @@ -1,7 +1,7 @@