Skip to content
Open
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
dfc2a18
#40209 - Conditional TotalCollect Trigger for mutation and Virtual Carts
Oct 11, 2025
170d7bc
Merge branch '2.4-develop' into Fix-QuoteGraphQl-CartPrices-Performance
senthilengg Oct 11, 2025
82f2786
#40209 - Fixing Implicit Null of ScopeConfig
Oct 11, 2025
43760cc
#40209 - Fixing Static Tests
Oct 11, 2025
3fd68ba
#40209 - Removing Extenstion Attribute after Shipping getData()
Oct 13, 2025
a7ca4ed
Merge branch '2.4-develop' into Fix-QuoteGraphQl-CartPrices-Performance
senthilengg Oct 13, 2025
64c6b34
Merge branch '2.4-develop' into Fix-QuoteGraphQl-CartPrices-Performance
senthilengg Oct 15, 2025
1cbabde
Merge branch '2.4-develop' into Fix-QuoteGraphQl-CartPrices-Performance
senthilengg Oct 17, 2025
5474f24
Merge branch '2.4-develop' into Fix-QuoteGraphQl-CartPrices-Performance
senthilengg Oct 21, 2025
2c13e99
Fix copyright comment formatting in CartPrices.php
senthilengg Oct 21, 2025
723bb3d
Fix copyright notice formatting in CartPricesTest.php
senthilengg Oct 21, 2025
11b684c
Merge branch '2.4-develop' into Fix-QuoteGraphQl-CartPrices-Performance
senthilengg Oct 24, 2025
62f16f9
Merge branch '2.4-develop' into Fix-QuoteGraphQl-CartPrices-Performance
engcom-Hotel Oct 27, 2025
4b41a49
Merge branch '2.4-develop' into Fix-QuoteGraphQl-CartPrices-Performance
senthilengg Oct 29, 2025
602829c
#40209 - Fixing discount lable null and WebAPI Test Failures
Oct 29, 2025
0495e41
#40209 - Adding Discounts to graphql response retriving from extensio…
Oct 30, 2025
f4ee24a
#40209 - Made new params in constructor as optional and updated unit …
Oct 30, 2025
7dc3e80
#40209 - Removed Extension Attributes and skip optimisation if discou…
Oct 31, 2025
e2d9c23
Merge branch '2.4-develop' into Fix-QuoteGraphQl-CartPrices-Performance
senthilengg Oct 31, 2025
0fd2f0a
#40209 - Removed unused variable
Nov 1, 2025
e1843a8
#40209 - Adding Trigger Recollect as a condition to fire collectTotals
Nov 1, 2025
2d74943
Merge branch '2.4-develop' into Fix-QuoteGraphQl-CartPrices-Performance
senthilengg Nov 1, 2025
5e0e3fe
Remove unused TotalTestHelper import from CartPricesTest
senthilengg Nov 1, 2025
39be9e8
#40209 - Bringing back the helper removed from last merge
Nov 2, 2025
cdf6d63
Merge branch '2.4-develop' into Fix-QuoteGraphQl-CartPrices-Performance
engcom-Hotel Nov 3, 2025
1daad58
#40209 - Addressing review comments
Nov 3, 2025
86400ac
Merge branch '2.4-develop' into Fix-QuoteGraphQl-CartPrices-Performance
senthilengg Nov 3, 2025
03df35f
Merge branch '2.4-develop' into Fix-QuoteGraphQl-CartPrices-Performance
senthilengg Nov 4, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 104 additions & 24 deletions app/code/Magento/QuoteGraphQl/Model/Resolver/CartPrices.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php
/**
* Copyright 2019 Adobe
* Copyright 2025 Adobe
* All Rights Reserved.
*/
declare(strict_types=1);
Expand All @@ -13,13 +13,20 @@
use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Query\ResolverInterface;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Magento\Framework\Api\DataObjectHelper;
use Magento\Framework\Api\ExtensibleDataInterface;
use Magento\Quote\Api\Data\TotalsInterface as QuoteTotalsInterface;
use Magento\Quote\Api\Data\TotalsInterfaceFactory;
use Magento\Quote\Model\Quote;
use Magento\Quote\Model\Quote\Address\Total;
use Magento\Quote\Model\Cart\Totals as CartTotals;
use Magento\QuoteGraphQl\Model\Cart\TotalsCollector;
use Magento\Store\Model\ScopeInterface;

/**
* @inheritdoc
*
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class CartPrices implements ResolverInterface
{
Expand All @@ -28,21 +35,44 @@ class CartPrices implements ResolverInterface
*/
private $totalsCollector;

/**
* @var string
*/
private const QUERY_TYPE = 'query';

/**
* @var ScopeConfigInterface
*/
private ScopeConfigInterface $scopeConfig;

/**
* @var TotalsInterfaceFactory
*/
private TotalsInterfaceFactory $totalsFactory;

/**
* @var DataObjectHelper
*/
private DataObjectHelper $dataObjectHelper;

/**
* @param TotalsCollector $totalsCollector
* @param ScopeConfigInterface|null $scopeConfig
*/
* @param TotalsInterfaceFactory|null $totalsFactory
* @param DataObjectHelper|null $dataObjectHelper
* */
public function __construct(
TotalsCollector $totalsCollector,
?ScopeConfigInterface $scopeConfig = null
?ScopeConfigInterface $scopeConfig = null,
?TotalsInterfaceFactory $totalsFactory = null,
?DataObjectHelper $dataObjectHelper = null
) {
$this->totalsCollector = $totalsCollector;
$this->scopeConfig = $scopeConfig ?? ObjectManager::getInstance()->get(ScopeConfigInterface::class);
$this->totalsFactory = $totalsFactory ??
ObjectManager::getInstance()->get(TotalsInterfaceFactory::class);
$this->dataObjectHelper = $dataObjectHelper ??
ObjectManager::getInstance()->get(DataObjectHelper::class);
}

/**
Expand All @@ -56,16 +86,39 @@ public function resolve(Field $field, $context, ResolveInfo $info, ?array $value

/** @var Quote $quote */
$quote = $value['model'];
/**
* To calculate a right discount value
* before calculate totals
* need to reset Cart Fixed Rules in the quote
*/
$quote->setCartFixedRules([]);
$cartTotals = $this->totalsCollector->collectQuoteTotals($quote);
$currency = $quote->getQuoteCurrencyCode();

$appliedTaxes = $this->getAppliedTaxes($cartTotals, $currency);
// check scenarios require force recollecting totals
// discounts should return rule details, which are calculated as part of collectTotals
if (!$quote->isVirtual() && $quote->getTriggerRecollect() != 1 &&
$info->operation->operation == self::QUERY_TYPE &&
!array_key_exists('discounts', $info->getFieldSelection(1))
) {
$addressTotalsData = $quote->getShippingAddress()->getData();
unset($addressTotalsData[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]);
$cartTotals = $this->totalsFactory->create();
$this->dataObjectHelper->populateWithArray(
$cartTotals,
$addressTotalsData,
QuoteTotalsInterface::class
);

if (isset($addressTotalsData['discount_description'])) {
$cartTotals->setDiscountDescription($addressTotalsData['discount_description']);
}

$appliedTaxes = $this->getAppliedTaxes($quote->getShippingAddress(), $currency);
} else {
/**
* To calculate a right discount value
* before calculate totals
* need to reset Cart Fixed Rules in the quote
*/
$quote->setCartFixedRules([]);
$cartTotals = $this->totalsCollector->collectQuoteTotals($quote);
$appliedTaxes = $this->getAppliedTaxes($cartTotals, $currency);
}

$grandTotal = $cartTotals->getGrandTotal();

$totalAppliedTaxes = 0;
Expand All @@ -92,14 +145,19 @@ public function resolve(Field $field, $context, ResolveInfo $info, ?array $value
/**
* Returns taxes applied to the current quote
*
* @param Total $total
* @param \Magento\Quote\Model\Quote\Address|Total $addressOrTotals
* @param string $currency
* @return array
* @throws \InvalidArgumentException
*/
private function getAppliedTaxes(Total $total, string $currency): array
private function getAppliedTaxes($addressOrTotals, string $currency): array
{
if (!$addressOrTotals instanceof Total && !$addressOrTotals instanceof \Magento\Quote\Model\Quote\Address) {
throw new \InvalidArgumentException('Unsupported totals type: ' . get_class($addressOrTotals));
}

$appliedTaxesData = [];
$appliedTaxes = $total->getAppliedTaxes();
$appliedTaxes = $addressOrTotals->getAppliedTaxes();

if (empty($appliedTaxes)) {
return $appliedTaxesData;
Expand Down Expand Up @@ -133,37 +191,59 @@ private function getAppliedTaxes(Total $total, string $currency): array
/**
* Returns information about an applied discount
*
* @param Total $total
* @param Total|CartTotals $totals
* @param string $currency
* @return array|null
* @throws \InvalidArgumentException
*/
private function getDiscount(Total $total, string $currency)
private function getDiscount($totals, string $currency)
{
if ($total->getDiscountAmount() === 0) {
$this->validateTotalsInstance($totals);

if ($totals->getDiscountAmount() == 0) {
return null;
}
return [
'label' => $total->getDiscountDescription() !== null ? explode(', ', $total->getDiscountDescription()) : [],
'amount' => ['value' => $total->getDiscountAmount(), 'currency' => $currency]
'label' => $totals->getDiscountDescription() !== null ?
explode(', ', $totals->getDiscountDescription()) : [],
'amount' => ['value' => $totals->getDiscountAmount(), 'currency' => $currency]
];
}

/**
* Get Subtotal with discount excluding tax.
*
* @param Total $cartTotals
* @param Total|CartTotals $totals
* @return float
* @throws \InvalidArgumentException
*/
private function getSubtotalWithDiscountExcludingTax(Total $cartTotals): float
private function getSubtotalWithDiscountExcludingTax($totals): float
{
$this->validateTotalsInstance($totals);

$discountIncludeTax = $this->scopeConfig->getValue(
'tax/calculation/discount_tax',
ScopeInterface::SCOPE_STORE
) ?? 0;
$discountExclTax = $discountIncludeTax ?
$cartTotals->getDiscountAmount() + $cartTotals->getDiscountTaxCompensationAmount() :
$cartTotals->getDiscountAmount();
$totals->getDiscountAmount() + $totals->getDiscountTaxCompensationAmount() :
$totals->getDiscountAmount();

return $totals->getSubtotal() + $discountExclTax;
}

return $cartTotals->getSubtotal() + $discountExclTax;
/**
* Validates the provided totals instance to ensure it is of a supported type.
*
* @param Total|CartTotals $totals
* @return void
* @throws \InvalidArgumentException If the provided totals instance is of an unsupported type.
*/
private function validateTotalsInstance($totals)
{

if (!$totals instanceof Total && !$totals instanceof CartTotals) {
throw new \InvalidArgumentException('Unsupported totals type: ' . get_class($totals));
}
}
}
Loading