Skip to content

Commit 1e5118b

Browse files
committed
Merge remote-tracking branch 'origin/AC-15304-V1' into spartans_pr_19112025
2 parents 58e6e24 + 51956dd commit 1e5118b

File tree

2 files changed

+172
-57
lines changed

2 files changed

+172
-57
lines changed

app/code/Magento/Checkout/view/frontend/templates/cart/form.phtml

Lines changed: 58 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -10,72 +10,73 @@
1010
?>
1111
<?php $mergedCells = ($this->helper(Magento\Tax\Helper\Data::class)->displayCartBothPrices() ? 2 : 1); ?>
1212
<?= $block->getChildHtml('form_before') ?>
13-
<form action="<?= $block->escapeUrl($block->getUrl('checkout/cart/updatePost')) ?>"
13+
<form action="<?= $escaper->escapeUrl($block->getUrl('checkout/cart/updatePost')) ?>"
1414
method="post"
1515
id="form-validate"
1616
data-mage-init='{"Magento_Checkout/js/action/update-shopping-cart":
17-
{"validationURL" : "<?= $block->escapeUrl($block->getUrl('checkout/cart/updateItemQty')) ?>",
18-
"updateCartActionContainer": "#update_cart_action_container"}
17+
{"validationURL" : "<?= $escaper->escapeUrl($block->getUrl('checkout/cart/updateItemQty')) ?>",
18+
"updateCartActionContainer": "#update_cart_action_container"},
19+
"Magento_Checkout/js/cart/ensure-subtotal-sync": {}
1920
}'
2021
class="form form-cart">
21-
<?= $block->getBlockHtml('formkey') ?>
22-
<div class="cart table-wrapper<?= $mergedCells == 2 ? ' detailed' : '' ?>">
23-
<?php if ($block->getPagerHtml()): ?>
24-
<div class="cart-products-toolbar cart-products-toolbar-top toolbar"
25-
data-attribute="cart-products-toolbar-top"><?= $block->getPagerHtml() ?>
26-
</div>
27-
<?php endif ?>
28-
<table id="shopping-cart-table"
29-
class="cart items data table"
30-
data-mage-init='{"shoppingCart":{"emptyCartButton": ".action.clear",
22+
<?= $block->getBlockHtml('formkey') ?>
23+
<div class="cart table-wrapper<?= $mergedCells == 2 ? ' detailed' : '' ?>">
24+
<?php if ($block->getPagerHtml()): ?>
25+
<div class="cart-products-toolbar cart-products-toolbar-top toolbar"
26+
data-attribute="cart-products-toolbar-top"><?= $block->getPagerHtml() ?>
27+
</div>
28+
<?php endif ?>
29+
<table id="shopping-cart-table"
30+
class="cart items data table"
31+
data-mage-init='{"shoppingCart":{"emptyCartButton": ".action.clear",
3132
"updateCartActionContainer": "#update_cart_action_container"}}'>
32-
<caption class="table-caption"><?= $block->escapeHtml(__('Shopping Cart Items')) ?></caption>
33-
<thead>
33+
<caption class="table-caption"><?= $escaper->escapeHtml(__('Shopping Cart Items')) ?></caption>
34+
<thead>
3435
<tr>
35-
<th class="col item" scope="col"><span><?= $block->escapeHtml(__('Item')) ?></span></th>
36-
<th class="col price" scope="col"><span><?= $block->escapeHtml(__('Price')) ?></span></th>
37-
<th class="col qty" scope="col"><span><?= $block->escapeHtml(__('Qty')) ?></span></th>
38-
<th class="col subtotal" scope="col"><span><?= $block->escapeHtml(__('Subtotal')) ?></span></th>
36+
<th class="col item" scope="col"><span><?= $escaper->escapeHtml(__('Item')) ?></span></th>
37+
<th class="col price" scope="col"><span><?= $escaper->escapeHtml(__('Price')) ?></span></th>
38+
<th class="col qty" scope="col"><span><?= $escaper->escapeHtml(__('Qty')) ?></span></th>
39+
<th class="col subtotal" scope="col"><span><?= $escaper->escapeHtml(__('Subtotal')) ?></span></th>
3940
</tr>
40-
</thead>
41-
<?php foreach ($block->getItems() as $_item): ?>
42-
<?= $block->getItemHtml($_item) ?>
43-
<?php endforeach ?>
44-
</table>
45-
<?php if ($block->getPagerHtml()): ?>
46-
<div class="cart-products-toolbar cart-products-toolbar-bottom toolbar"
47-
data-attribute="cart-products-toolbar-bottom"><?= $block->getPagerHtml() ?>
48-
</div>
49-
<?php endif ?>
50-
</div>
51-
<div class="cart main actions">
52-
<?php if ($block->getContinueShoppingUrl()): ?>
53-
<a class="action continue"
54-
href="<?= $block->escapeUrl($block->getContinueShoppingUrl()) ?>"
55-
title="<?= $block->escapeHtml(__('Continue Shopping')) ?>">
56-
<span><?= $block->escapeHtml(__('Continue Shopping')) ?></span>
57-
</a>
58-
<?php endif; ?>
59-
<?php if ($block->getViewModel()->isClearShoppingCartEnabled()): ?>
60-
<button type="button"
41+
</thead>
42+
<?php foreach ($block->getItems() as $_item): ?>
43+
<?= $block->getItemHtml($_item) ?>
44+
<?php endforeach ?>
45+
</table>
46+
<?php if ($block->getPagerHtml()): ?>
47+
<div class="cart-products-toolbar cart-products-toolbar-bottom toolbar"
48+
data-attribute="cart-products-toolbar-bottom"><?= $block->getPagerHtml() ?>
49+
</div>
50+
<?php endif ?>
51+
</div>
52+
<div class="cart main actions">
53+
<?php if ($block->getContinueShoppingUrl()): ?>
54+
<a class="action continue"
55+
href="<?= $escaper->escapeUrl($block->getContinueShoppingUrl()) ?>"
56+
title="<?= $escaper->escapeHtml(__('Continue Shopping')) ?>">
57+
<span><?= $escaper->escapeHtml(__('Continue Shopping')) ?></span>
58+
</a>
59+
<?php endif; ?>
60+
<?php if ($block->getViewModel()->isClearShoppingCartEnabled()): ?>
61+
<button type="button"
62+
name="update_cart_action"
63+
data-cart-empty=""
64+
value="empty_cart"
65+
title="<?= $escaper->escapeHtml(__('Clear Shopping Cart')) ?>"
66+
class="action clear" id="empty_cart_button">
67+
<span><?= $escaper->escapeHtml(__('Clear Shopping Cart')) ?></span>
68+
</button>
69+
<?php endif ?>
70+
<button type="submit"
6171
name="update_cart_action"
62-
data-cart-empty=""
63-
value="empty_cart"
64-
title="<?= $block->escapeHtml(__('Clear Shopping Cart')) ?>"
65-
class="action clear" id="empty_cart_button">
66-
<span><?= $block->escapeHtml(__('Clear Shopping Cart')) ?></span>
72+
data-cart-item-update=""
73+
value="update_qty"
74+
title="<?= $escaper->escapeHtml(__('Update Shopping Cart')) ?>"
75+
class="action update">
76+
<span><?= $escaper->escapeHtml(__('Update Shopping Cart')) ?></span>
6777
</button>
68-
<?php endif ?>
69-
<button type="submit"
70-
name="update_cart_action"
71-
data-cart-item-update=""
72-
value="update_qty"
73-
title="<?= $block->escapeHtml(__('Update Shopping Cart')) ?>"
74-
class="action update">
75-
<span><?= $block->escapeHtml(__('Update Shopping Cart')) ?></span>
76-
</button>
77-
<input type="hidden" value="" id="update_cart_action_container" data-cart-item-update=""/>
78-
</div>
79-
</form>
78+
<input type="hidden" value="" id="update_cart_action_container" data-cart-item-update=""/>
79+
</div>
80+
</form>
8081
<?= $block->getChildHtml('checkout.cart.order.actions') ?>
8182
<?= $block->getChildHtml('shopping.cart.table.after') ?>
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/**
2+
* Copyright 2025 Adobe
3+
* All Rights Reserved.
4+
*/
5+
6+
define([
7+
'jquery',
8+
'mage/translate'
9+
], function ($) {
10+
'use strict';
11+
12+
/**
13+
* Compare central cart subtotal with summary subtotal and click Update once if mismatched.
14+
*/
15+
return function initEnsureSubtotalSync(config, element)
16+
{
17+
const $root = $(element || document);
18+
let clicked = false;
19+
20+
function parsePrice(text)
21+
{
22+
if (!text) {
23+
return NaN;
24+
}
25+
26+
// Remove non-numeric except . , - then normalize
27+
let cleaned = ('' + text).replace(/[^0-9,.-]/g, '');
28+
29+
// If both , and . exist, assume , is thousands
30+
if (cleaned.indexOf(',') > -1 && cleaned.indexOf('.') > -1) {
31+
cleaned = cleaned.replace(/,/g, '');
32+
} else if (cleaned.indexOf(',') > -1 && cleaned.indexOf('.') === -1) {
33+
// European format: swap , to .
34+
cleaned = cleaned.replace(/,/g, '.');
35+
}
36+
37+
const n = parseFloat(cleaned);
38+
39+
return isNaN(n) ? NaN : Math.round(n * 100) / 100;
40+
}
41+
42+
function getCentralSubtotal()
43+
{
44+
// Sum of row totals on the table
45+
let sum = 0;
46+
47+
$root.find('#shopping-cart-table .col.subtotal .cart-price').each(function () {
48+
const text = $(this).text(), val = parsePrice(text);
49+
50+
if (!isNaN(val)) {
51+
sum += val;
52+
}
53+
});
54+
55+
return Math.round(sum * 100) / 100;
56+
}
57+
58+
function getSummarySubtotal()
59+
{
60+
// Summary subtotal in cart totals knockout template
61+
const text = $('#cart-totals .totals.sub .amount .price').first().text();
62+
63+
return parsePrice(text);
64+
}
65+
66+
function trySync()
67+
{
68+
// Skip when cart uses pagination; visible rows don't represent full subtotal
69+
if ($root.find('.cart-products-toolbar').length) {
70+
return;
71+
}
72+
73+
if (clicked) {
74+
return;
75+
}
76+
77+
const central = getCentralSubtotal(), summary = getSummarySubtotal();
78+
79+
if (!isNaN(central) && !isNaN(summary) && central !== summary) {
80+
const $updateBtn = $root.find('.cart.main.actions button.action.update');
81+
82+
if ($updateBtn.length) {
83+
clicked = true;
84+
$updateBtn.trigger('click');
85+
}
86+
}
87+
}
88+
89+
// Initial attempt after DOM ready and after a short delay to allow KO to render totals
90+
$(function () {
91+
trySync();
92+
setTimeout(trySync, 300);
93+
94+
// Observe changes in totals area to re-check once
95+
const totals = document.getElementById('cart-totals');
96+
97+
if (totals && typeof MutationObserver !== 'undefined') {
98+
const obs = new MutationObserver(function () {
99+
trySync();
100+
101+
if (clicked) {
102+
obs.disconnect();
103+
}
104+
});
105+
106+
obs.observe(totals, {
107+
childList: true,
108+
subtree: true,
109+
characterData: true
110+
});
111+
}
112+
});
113+
};
114+
});

0 commit comments

Comments
 (0)