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 @@ + + +