Compare commits
No commits in common. "9484f8c3496e0377032fe8546ec3e15e4baf37da" and "bd8d87060f419d27d841960d3e8811c9f761b33e" have entirely different histories.
9484f8c349
...
bd8d87060f
4 changed files with 23 additions and 116 deletions
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
{ # noqa: B018
|
{ # noqa: B018
|
||||||
"name": "Website Sale - Aplicoop",
|
"name": "Website Sale - Aplicoop",
|
||||||
"version": "18.0.1.10.2",
|
"version": "18.0.1.10.0",
|
||||||
"category": "Website/Sale",
|
"category": "Website/Sale",
|
||||||
"summary": "Modern replacement of legacy Aplicoop - Collaborative consumption group orders",
|
"summary": "Modern replacement of legacy Aplicoop - Collaborative consumption group orders",
|
||||||
"author": "Odoo Community Association (OCA), Criptomart",
|
"author": "Odoo Community Association (OCA), Criptomart",
|
||||||
|
|
|
||||||
|
|
@ -641,16 +641,6 @@ class AplicoopWebsiteSale(WebsiteSale):
|
||||||
def _decode_json_body(self):
|
def _decode_json_body(self):
|
||||||
return _utils._decode_json_body(self, request)
|
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):
|
def _build_group_order_unavailable_response(self, group_order, status=403):
|
||||||
"""Delegate building of unavailable response to utils helper."""
|
"""Delegate building of unavailable response to utils helper."""
|
||||||
self._request = request
|
self._request = request
|
||||||
|
|
@ -837,7 +827,6 @@ class AplicoopWebsiteSale(WebsiteSale):
|
||||||
"current_page": page,
|
"current_page": page,
|
||||||
"has_next": has_next,
|
"has_next": has_next,
|
||||||
"total_products": total_products,
|
"total_products": total_products,
|
||||||
"module_version": self._get_installed_module_version(),
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -1381,7 +1370,6 @@ class AplicoopWebsiteSale(WebsiteSale):
|
||||||
),
|
),
|
||||||
"cutoff_passed": cutoff_passed,
|
"cutoff_passed": cutoff_passed,
|
||||||
"cutoff_date": cutoff_date_str,
|
"cutoff_date": cutoff_date_str,
|
||||||
"client_version": self._get_installed_module_version(),
|
|
||||||
}
|
}
|
||||||
return request.make_response(
|
return request.make_response(
|
||||||
json.dumps(response_data),
|
json.dumps(response_data),
|
||||||
|
|
|
||||||
|
|
@ -45,13 +45,6 @@
|
||||||
|
|
||||||
console.log("Initializing cart for order:", this.orderId);
|
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)
|
// Attach event listeners FIRST (doesn't depend on translations)
|
||||||
this._attachEventListeners();
|
this._attachEventListeners();
|
||||||
console.log("[groupOrderShop] Event listeners attached");
|
console.log("[groupOrderShop] Event listeners attached");
|
||||||
|
|
@ -442,18 +435,7 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
_loadCart: function () {
|
_loadCart: function () {
|
||||||
// Storage layout:
|
|
||||||
// eskaera_<id>_cart → items as plain {productId: {...}}
|
|
||||||
// (keeps the contract used by
|
|
||||||
// checkout_labels.js, home_delivery.js
|
|
||||||
// and _saveOrderDraft).
|
|
||||||
// eskaera_<id>_cart_cycle → "YYYY-MM-DD" the cart was saved at.
|
|
||||||
// Eviction only fires when we KNOW the current cycle and it
|
|
||||||
// disagrees with the stored one. If either side is missing
|
|
||||||
// (e.g. checkout page that doesn't call check-status, or XHR
|
|
||||||
// failure), we preserve the user's cart.
|
|
||||||
var cartKey = "eskaera_" + this.orderId + "_cart";
|
var cartKey = "eskaera_" + this.orderId + "_cart";
|
||||||
var cycleKey = "eskaera_" + this.orderId + "_cart_cycle";
|
|
||||||
var raw = localStorage.getItem(cartKey);
|
var raw = localStorage.getItem(cartKey);
|
||||||
this.cart = {};
|
this.cart = {};
|
||||||
if (!raw) {
|
if (!raw) {
|
||||||
|
|
@ -466,32 +448,27 @@
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn("Cart load: invalid JSON in localStorage, dropping", e);
|
console.warn("Cart load: invalid JSON in localStorage, dropping", e);
|
||||||
localStorage.removeItem(cartKey);
|
localStorage.removeItem(cartKey);
|
||||||
localStorage.removeItem(cycleKey);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!parsed || typeof parsed !== "object") {
|
// Legacy / corrupt shape (raw items object without cycle stamp) → drop.
|
||||||
|
if (
|
||||||
|
!parsed ||
|
||||||
|
typeof parsed !== "object" ||
|
||||||
|
!parsed.items ||
|
||||||
|
!("cutoff_date" in parsed)
|
||||||
|
) {
|
||||||
|
console.log(
|
||||||
|
"Cart load: legacy/unstamped value, dropping localStorage[" + cartKey + "]"
|
||||||
|
);
|
||||||
localStorage.removeItem(cartKey);
|
localStorage.removeItem(cartKey);
|
||||||
localStorage.removeItem(cycleKey);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Migration: v18.0.1.10.0 wrapped the cart as {cutoff_date, items}
|
// Cycle mismatch (or no current cycle known) → drop. This is the
|
||||||
// in the same key, which broke checkout_labels.js / home_delivery.js
|
// primary defence against stale carts surviving cycle rollover.
|
||||||
// / _saveOrderDraft (they iterated Object.keys treating them as
|
if (!this._currentCutoffDate || parsed.cutoff_date !== this._currentCutoffDate) {
|
||||||
// productIds). Unwrap once; subsequent saves use the canonical
|
|
||||||
// split-key layout below.
|
|
||||||
var items;
|
|
||||||
var storedCycle;
|
|
||||||
if (parsed.items && "cutoff_date" in parsed) {
|
|
||||||
items = parsed.items;
|
|
||||||
storedCycle = parsed.cutoff_date;
|
|
||||||
} else {
|
|
||||||
items = parsed;
|
|
||||||
storedCycle = localStorage.getItem(cycleKey);
|
|
||||||
}
|
|
||||||
if (this._currentCutoffDate && storedCycle && storedCycle !== this._currentCutoffDate) {
|
|
||||||
console.log(
|
console.log(
|
||||||
"Cart load: cycle mismatch (stored=" +
|
"Cart load: cycle mismatch (stored=" +
|
||||||
storedCycle +
|
parsed.cutoff_date +
|
||||||
", current=" +
|
", current=" +
|
||||||
this._currentCutoffDate +
|
this._currentCutoffDate +
|
||||||
"), dropping localStorage[" +
|
"), dropping localStorage[" +
|
||||||
|
|
@ -499,20 +476,14 @@
|
||||||
"]"
|
"]"
|
||||||
);
|
);
|
||||||
localStorage.removeItem(cartKey);
|
localStorage.removeItem(cartKey);
|
||||||
localStorage.removeItem(cycleKey);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.cart = items;
|
this.cart = parsed.items;
|
||||||
// Re-canonicalize storage so other readers see the plain shape.
|
|
||||||
localStorage.setItem(cartKey, JSON.stringify(items));
|
|
||||||
if (storedCycle) {
|
|
||||||
localStorage.setItem(cycleKey, storedCycle);
|
|
||||||
}
|
|
||||||
console.log(
|
console.log(
|
||||||
"Cart loaded from localStorage[" +
|
"Cart loaded from localStorage[" +
|
||||||
cartKey +
|
cartKey +
|
||||||
"] (cycle " +
|
"] (cycle " +
|
||||||
(storedCycle || "unknown") +
|
parsed.cutoff_date +
|
||||||
"):",
|
"):",
|
||||||
this.cart
|
this.cart
|
||||||
);
|
);
|
||||||
|
|
@ -520,11 +491,11 @@
|
||||||
|
|
||||||
_saveCart: function () {
|
_saveCart: function () {
|
||||||
var cartKey = "eskaera_" + this.orderId + "_cart";
|
var cartKey = "eskaera_" + this.orderId + "_cart";
|
||||||
var cycleKey = "eskaera_" + this.orderId + "_cart_cycle";
|
var payload = JSON.stringify({
|
||||||
localStorage.setItem(cartKey, JSON.stringify(this.cart));
|
cutoff_date: this._currentCutoffDate || null,
|
||||||
if (this._currentCutoffDate) {
|
items: this.cart,
|
||||||
localStorage.setItem(cycleKey, this._currentCutoffDate);
|
});
|
||||||
}
|
localStorage.setItem(cartKey, payload);
|
||||||
console.log(
|
console.log(
|
||||||
"Cart saved to localStorage[" +
|
"Cart saved to localStorage[" +
|
||||||
cartKey +
|
cartKey +
|
||||||
|
|
@ -537,42 +508,11 @@
|
||||||
|
|
||||||
_clearCurrentOrderCartSilently: function () {
|
_clearCurrentOrderCartSilently: function () {
|
||||||
var cartKey = "eskaera_" + this.orderId + "_cart";
|
var cartKey = "eskaera_" + this.orderId + "_cart";
|
||||||
var cycleKey = "eskaera_" + this.orderId + "_cart_cycle";
|
|
||||||
localStorage.removeItem(cartKey);
|
localStorage.removeItem(cartKey);
|
||||||
localStorage.removeItem(cycleKey);
|
|
||||||
this.cart = {};
|
this.cart = {};
|
||||||
console.log("[groupOrderShop] Cart cleared silently for order:", this.orderId);
|
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) {
|
_isClosedOrderResponse: function (xhr) {
|
||||||
if (!xhr) {
|
if (!xhr) {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -615,27 +555,6 @@
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
var data = JSON.parse(xhr.responseText || "{}");
|
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
|
// Capture current cycle marker so _loadCart can evict a
|
||||||
// localStorage cart that belongs to a previous cycle.
|
// localStorage cart that belongs to a previous cycle.
|
||||||
self._currentCutoffDate = data && data.cutoff_date ? data.cutoff_date : null;
|
self._currentCutoffDate = data && data.cutoff_date ? data.cutoff_date : null;
|
||||||
|
|
|
||||||
|
|
@ -337,7 +337,7 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<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">
|
<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">
|
||||||
<p class="text-muted">This order's cart is empty</p>
|
<p class="text-muted">This order's cart is empty</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-footer bg-white d-flex justify-content-between align-items-center gap-2">
|
<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