[IMP] website_sale_aplicoop: signal frontend to reload on deploy
When we ship a JS-only fix, users with a long-lived tab keep running the old code until they hard-refresh — there is no clean way to push new code to an already-loaded page. Now /eskaera/check-status returns the module's installed_version as client_version, and the eskaera page embeds the same version in data-build-version on the cart container. The JS captures the page's build version at init; on every check-status response it compares them and triggers window.location. reload() on mismatch. A sessionStorage timestamp guards against reload loops if the versions stay disagreeing (cached HTML upstream). The version bump in __manifest__.py also invalidates the asset bundle URL hash, so even users without this signalling path get fresh JS on their next navigation. This is forward-looking: clients on the old JS (no check) still need one manual refresh today, but every future deploy will auto-recover. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
bd8d87060f
commit
6e6d1e5256
4 changed files with 71 additions and 2 deletions
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
{ # noqa: B018
|
||||
"name": "Website Sale - Aplicoop",
|
||||
"version": "18.0.1.10.0",
|
||||
"version": "18.0.1.10.1",
|
||||
"category": "Website/Sale",
|
||||
"summary": "Modern replacement of legacy Aplicoop - Collaborative consumption group orders",
|
||||
"author": "Odoo Community Association (OCA), Criptomart",
|
||||
|
|
|
|||
|
|
@ -641,6 +641,16 @@ class AplicoopWebsiteSale(WebsiteSale):
|
|||
def _decode_json_body(self):
|
||||
return _utils._decode_json_body(self, request)
|
||||
|
||||
def _get_installed_module_version(self):
|
||||
"""Return the installed version of this addon, used as a build-id
|
||||
signal so the frontend can detect a deploy and force a reload."""
|
||||
module = (
|
||||
request.env["ir.module.module"]
|
||||
.sudo()
|
||||
.search([("name", "=", "website_sale_aplicoop")], limit=1)
|
||||
)
|
||||
return module.installed_version or ""
|
||||
|
||||
def _build_group_order_unavailable_response(self, group_order, status=403):
|
||||
"""Delegate building of unavailable response to utils helper."""
|
||||
self._request = request
|
||||
|
|
@ -827,6 +837,7 @@ class AplicoopWebsiteSale(WebsiteSale):
|
|||
"current_page": page,
|
||||
"has_next": has_next,
|
||||
"total_products": total_products,
|
||||
"module_version": self._get_installed_module_version(),
|
||||
},
|
||||
)
|
||||
|
||||
|
|
@ -1370,6 +1381,7 @@ class AplicoopWebsiteSale(WebsiteSale):
|
|||
),
|
||||
"cutoff_passed": cutoff_passed,
|
||||
"cutoff_date": cutoff_date_str,
|
||||
"client_version": self._get_installed_module_version(),
|
||||
}
|
||||
return request.make_response(
|
||||
json.dumps(response_data),
|
||||
|
|
|
|||
|
|
@ -45,6 +45,13 @@
|
|||
|
||||
console.log("Initializing cart for order:", this.orderId);
|
||||
|
||||
// Capture the build version embedded in the rendered HTML. The
|
||||
// server returns its installed version in /eskaera/check-status,
|
||||
// and if the two disagree we know a deploy happened and the
|
||||
// current page is running stale JS — force a one-shot reload.
|
||||
this._buildVersion = orderIdElement.getAttribute("data-build-version") || null;
|
||||
console.log("[groupOrderShop] Build version:", this._buildVersion);
|
||||
|
||||
// Attach event listeners FIRST (doesn't depend on translations)
|
||||
this._attachEventListeners();
|
||||
console.log("[groupOrderShop] Event listeners attached");
|
||||
|
|
@ -513,6 +520,35 @@
|
|||
console.log("[groupOrderShop] Cart cleared silently for order:", this.orderId);
|
||||
},
|
||||
|
||||
// Avoid a reload loop if for any reason (HTML cached upstream, etc.)
|
||||
// the build versions keep disagreeing after a reload.
|
||||
_shouldReloadForNewBuild: function () {
|
||||
try {
|
||||
var raw = sessionStorage.getItem("eskaera_build_reload_at");
|
||||
if (!raw) {
|
||||
return true;
|
||||
}
|
||||
var last = parseInt(raw, 10);
|
||||
if (isNaN(last)) {
|
||||
return true;
|
||||
}
|
||||
// If we already reloaded in the last 30s, don't try again.
|
||||
return Date.now() - last > 30000;
|
||||
} catch (e) {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
_markReloadAttempt: function () {
|
||||
try {
|
||||
sessionStorage.setItem("eskaera_build_reload_at", String(Date.now()));
|
||||
} catch (e) {
|
||||
// sessionStorage unavailable — fail open; worst case we get a
|
||||
// single extra reload, never a loop because the next render
|
||||
// will carry the new version.
|
||||
}
|
||||
},
|
||||
|
||||
_isClosedOrderResponse: function (xhr) {
|
||||
if (!xhr) {
|
||||
return false;
|
||||
|
|
@ -555,6 +591,27 @@
|
|||
}
|
||||
try {
|
||||
var data = JSON.parse(xhr.responseText || "{}");
|
||||
// Deploy-detection: server tells us the installed module
|
||||
// version; if it differs from the version embedded in
|
||||
// the HTML, we are running stale JS — reload once.
|
||||
if (
|
||||
data &&
|
||||
data.client_version &&
|
||||
self._buildVersion &&
|
||||
data.client_version !== self._buildVersion &&
|
||||
self._shouldReloadForNewBuild()
|
||||
) {
|
||||
console.warn(
|
||||
"[groupOrderShop] Build mismatch (page=" +
|
||||
self._buildVersion +
|
||||
", server=" +
|
||||
data.client_version +
|
||||
"), reloading"
|
||||
);
|
||||
self._markReloadAttempt();
|
||||
window.location.reload();
|
||||
return;
|
||||
}
|
||||
// Capture current cycle marker so _loadCart can evict a
|
||||
// localStorage cart that belongs to a previous cycle.
|
||||
self._currentCutoffDate = data && data.cutoff_date ? data.cutoff_date : null;
|
||||
|
|
|
|||
|
|
@ -337,7 +337,7 @@
|
|||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body cart-body-lg" id="cart-items-container" t-attf-data-order-id="{{ group_order.id }}" aria-labelledby="cart-title" aria-live="polite" aria-relevant="additions removals">
|
||||
<div class="card-body cart-body-lg" id="cart-items-container" t-attf-data-order-id="{{ group_order.id }}" t-attf-data-build-version="{{ module_version }}" aria-labelledby="cart-title" aria-live="polite" aria-relevant="additions removals">
|
||||
<p class="text-muted">This order's cart is empty</p>
|
||||
</div>
|
||||
<div class="card-footer bg-white d-flex justify-content-between align-items-center gap-2">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue