diff --git a/vendor/magento/module-checkout/Block/Cart/AbstractCart.php b/vendor/magento/module-checkout/Block/Cart/AbstractCart.php
index f5adf416e9a27..435e0a50c4ad7 100644
--- a/vendor/magento/module-checkout/Block/Cart/AbstractCart.php
+++ b/vendor/magento/module-checkout/Block/Cart/AbstractCart.php
@@ -5,7 +5,9 @@
*/
namespace Magento\Checkout\Block\Cart;
+use Magento\Framework\App\ObjectManager;
use Magento\Quote\Model\Quote;
+use Magento\Checkout\Observer\CatalogRuleSaveAfterObserver;
/**
* Shopping cart abstract block
@@ -15,7 +17,12 @@ class AbstractCart extends \Magento\Framework\View\Element\Template
/**
* Block alias fallback
*/
- const DEFAULT_TYPE = 'default';
+ public const DEFAULT_TYPE = 'default';
+
+ /**
+ * Session key for last time cart totals were recollected (used with catalog rules cache).
+ */
+ private const SESSION_KEY_LAST_RECOLLECT_AT = 'last_cart_totals_recollect_at';
/**
* @var Quote|null
@@ -44,21 +51,28 @@ class AbstractCart extends \Magento\Framework\View\Element\Template
*/
protected $_checkoutSession;
+ /**
+ * @var \Magento\Framework\App\CacheInterface
+ */
+ private $cache;
+
/**
* @param \Magento\Framework\View\Element\Template\Context $context
* @param \Magento\Customer\Model\Session $customerSession
* @param \Magento\Checkout\Model\Session $checkoutSession
* @param array $data
- * @codeCoverageIgnore
+ * @param \Magento\Framework\App\CacheInterface|null $cache
*/
public function __construct(
\Magento\Framework\View\Element\Template\Context $context,
\Magento\Customer\Model\Session $customerSession,
\Magento\Checkout\Model\Session $checkoutSession,
- array $data = []
+ array $data = [],
+ ?\Magento\Framework\App\CacheInterface $cache = null
) {
$this->_customerSession = $customerSession;
$this->_checkoutSession = $checkoutSession;
+ $this->cache = $cache ?? ObjectManager::getInstance()->get(\Magento\Framework\App\CacheInterface::class);
parent::__construct($context, $data);
$this->_isScopePrivate = true;
}
@@ -107,15 +121,46 @@ public function getQuote()
{
if (null === $this->_quote) {
$this->_quote = $this->_checkoutSession->getQuote();
+
+ if ($this->_quote->getId() && $this->shouldRecollectTotals()) {
+ $existingItemsCount = $this->_quote->getItemsCount();
+ $existingItemsQty = $this->_quote->getItemsQty();
+ $existingVirtualItemsQty = $this->_quote->getData('virtual_items_qty');
+ $this->_quote->setData('totals_collected_flag', false);
+ $this->_quote->collectTotals();
+ $this->_quote->setItemsCount($existingItemsCount);
+ $this->_quote->setItemsQty($existingItemsQty);
+ $this->_quote->setData('virtual_items_qty', $existingVirtualItemsQty);
+ $this->_checkoutSession->setData(
+ self::SESSION_KEY_LAST_RECOLLECT_AT,
+ $this->cache->load(CatalogRuleSaveAfterObserver::CACHE_KEY_CATALOG_RULES_UPDATED_AT)
+ );
+ }
}
return $this->_quote;
}
+ /**
+ * Whether cart totals should be recollected (only after a catalog price rule was saved).
+ *
+ * @return bool
+ */
+ private function shouldRecollectTotals(): bool
+ {
+ $rulesUpdatedAt = (int) ($this->cache->load(
+ CatalogRuleSaveAfterObserver::CACHE_KEY_CATALOG_RULES_UPDATED_AT
+ ) ?: 0);
+ if ($rulesUpdatedAt <= 0) {
+ return false;
+ }
+ $lastRecollectAt = (int) ($this->_checkoutSession->getData(self::SESSION_KEY_LAST_RECOLLECT_AT) ?: 0);
+ return $rulesUpdatedAt > $lastRecollectAt;
+ }
+
/**
* Get all cart items
*
* @return array
- * @codeCoverageIgnore
*/
public function getItems()
{
@@ -135,8 +180,9 @@ public function getItemHtml(\Magento\Quote\Model\Quote\Item $item)
}
/**
+ * Retrieve totals.
+ *
* @return array
- * @codeCoverageIgnore
*/
public function getTotals()
{
@@ -144,6 +190,8 @@ public function getTotals()
}
/**
+ * Retrieve cached totals.
+ *
* @return array
*/
public function getTotalsCache()
diff --git a/vendor/magento/module-checkout/Observer/CatalogRuleSaveAfterObserver.php b/vendor/magento/module-checkout/Observer/CatalogRuleSaveAfterObserver.php
new file mode 100644
index 0000000000000..4ff77dba72dc0
--- /dev/null
+++ b/vendor/magento/module-checkout/Observer/CatalogRuleSaveAfterObserver.php
@@ -0,0 +1,57 @@
+cache = $cache;
+ }
+
+ /**
+ * When a catalog rule is saved, set cache timestamp so storefront cart can refresh totals once.
+ *
+ * @param \Magento\Framework\Event\Observer $observer
+ * @return void
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+ */
+ public function execute(\Magento\Framework\Event\Observer $observer): void
+ {
+ $this->cache->save(
+ (string) time(),
+ self::CACHE_KEY_CATALOG_RULES_UPDATED_AT,
+ [],
+ self::CACHE_LIFETIME
+ );
+ }
+}
diff --git a/vendor/magento/module-checkout/etc/events.xml b/vendor/magento/module-checkout/etc/events.xml
index dd2897f5db6eb..fe22be96dfe92 100644
--- a/vendor/magento/module-checkout/etc/events.xml
+++ b/vendor/magento/module-checkout/etc/events.xml
@@ -9,4 +9,7 @@
+
+
+