From 965c5c24951560c51d25f14d2c8d2a9e2de61903 Mon Sep 17 00:00:00 2001 From: GitHub Copilot Date: Thu, 4 Jun 2026 15:14:04 +0200 Subject: [PATCH 1/2] [FIX] website_sale_aplicoop: stamp localStorage cart with cycle and evict on rollover MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The cart was reappearing on stage with ghost items that had never been saved as drafts on the server. Root cause: the localStorage cart had no cycle awareness. Users who added items but never clicked Save left the items in localStorage forever, and the only existing eviction path (_clearCurrentOrderCartSilently via _checkGroupOrderStatus) only fires when cutoff_passed flips true — which it rarely does, because the stored cutoff_date is normally in the future for an active cycle. On the next visit, _loadCart happily rehydrated the stale items and _autoLoadDraftOnInit short-circuited because the cart was no longer empty. Stamp every cart written to localStorage with the cutoff_date it belongs to and reject on cycle mismatch in _loadCart. The cutoff_date is captured from /eskaera/check-status (which already returned it). Legacy unstamped values and corrupt JSON are also dropped, so existing browsers self-heal on first reload after the deploy. Co-Authored-By: Claude Opus 4.7 --- .../static/src/js/website_sale.js | 78 +++++++++++++++++-- 1 file changed, 70 insertions(+), 8 deletions(-) diff --git a/website_sale_aplicoop/static/src/js/website_sale.js b/website_sale_aplicoop/static/src/js/website_sale.js index bae18ef..15076c0 100644 --- a/website_sale_aplicoop/static/src/js/website_sale.js +++ b/website_sale_aplicoop/static/src/js/website_sale.js @@ -436,18 +436,74 @@ _loadCart: function () { var cartKey = "eskaera_" + this.orderId + "_cart"; - var savedCart = localStorage.getItem(cartKey); - this.cart = savedCart ? JSON.parse(savedCart) : {}; - console.log("Cart loaded from localStorage[" + cartKey + "]:", this.cart); - console.log("Raw localStorage value:", savedCart); + var raw = localStorage.getItem(cartKey); + this.cart = {}; + if (!raw) { + console.log("Cart loaded: localStorage[" + cartKey + "] is empty"); + return; + } + var parsed; + try { + parsed = JSON.parse(raw); + } catch (e) { + console.warn("Cart load: invalid JSON in localStorage, dropping", e); + localStorage.removeItem(cartKey); + return; + } + // 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); + return; + } + // Cycle mismatch (or no current cycle known) → drop. This is the + // primary defence against stale carts surviving cycle rollover. + if (!this._currentCutoffDate || parsed.cutoff_date !== this._currentCutoffDate) { + console.log( + "Cart load: cycle mismatch (stored=" + + parsed.cutoff_date + + ", current=" + + this._currentCutoffDate + + "), dropping localStorage[" + + cartKey + + "]" + ); + localStorage.removeItem(cartKey); + return; + } + this.cart = parsed.items; + console.log( + "Cart loaded from localStorage[" + + cartKey + + "] (cycle " + + parsed.cutoff_date + + "):", + this.cart + ); }, _saveCart: function () { var cartKey = "eskaera_" + this.orderId + "_cart"; - var cartJson = JSON.stringify(this.cart); - localStorage.setItem(cartKey, cartJson); - console.log("Cart saved to localStorage[" + cartKey + "]:", this.cart); - console.log("Verification - immediately read back:", localStorage.getItem(cartKey)); + var payload = JSON.stringify({ + cutoff_date: this._currentCutoffDate || null, + items: this.cart, + }); + localStorage.setItem(cartKey, payload); + console.log( + "Cart saved to localStorage[" + + cartKey + + "] (cycle " + + (this._currentCutoffDate || "unknown") + + "):", + this.cart + ); }, _clearCurrentOrderCartSilently: function () { @@ -493,11 +549,15 @@ xhr.onload = function () { if (xhr.status !== 200) { + self._currentCutoffDate = null; done(); return; } try { var data = JSON.parse(xhr.responseText || "{}"); + // 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; // Clear cart if order is closed, action requests clearance, or cutoff already passed var shouldClear = false; if (data) { @@ -527,12 +587,14 @@ } } } catch (e) { + self._currentCutoffDate = null; console.warn("[groupOrderShop] check-status parse error", e); } done(); }; xhr.onerror = function () { + self._currentCutoffDate = null; done(); }; From 48597ae8c74b3bbacea05c9edfd1cdd4e5d3b73c Mon Sep 17 00:00:00 2001 From: GitHub Copilot Date: Thu, 4 Jun 2026 15:14:53 +0200 Subject: [PATCH 2/2] add l10n_es_partner dependency for commercial name field. --- website_sale_aplicoop/__manifest__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/website_sale_aplicoop/__manifest__.py b/website_sale_aplicoop/__manifest__.py index e820bd8..35b7831 100644 --- a/website_sale_aplicoop/__manifest__.py +++ b/website_sale_aplicoop/__manifest__.py @@ -19,6 +19,7 @@ "stock_picking_batch", "account", "product_get_price_helper", + "l10n_es_partner", ], "data": [ # Datos: Grupos propios