diff --git a/website_sale_aplicoop/controllers/website_sale_validators.py b/website_sale_aplicoop/controllers/website_sale_validators.py index 8f14309..7e6e7ff 100644 --- a/website_sale_aplicoop/controllers/website_sale_validators.py +++ b/website_sale_aplicoop/controllers/website_sale_validators.py @@ -65,21 +65,33 @@ def _get_salesperson_for_order(self, partner): def _find_recent_draft_order(self, partner_id, group_order, request_obj=None): """Return the active-cycle draft sale.order for the partner, or empty. - The active ordering period ends at group_order.cutoff_date and its length - depends on the order period: 7 days (weekly), 14 days (biweekly), one - month (monthly). One-time orders have a single cycle starting at - start_date (or unbounded when start_date is empty). Drafts created - outside this window belong to a previous cycle and must not be reused — - otherwise stale carts come back when the user re-enters the order page. + A draft only counts as "current cycle" when it satisfies BOTH of these + (neither is sufficient on its own — see the regression each one guards): + + 1) create_date falls within the active window — derived from + group_order.cutoff_date and the order period (7 days weekly, 14 + biweekly, one month monthly; one-time orders use a single cycle + starting at start_date). Without this, a draft whose pickup_date + happens to match the current one only because pickup_date froze + across cycles (observed in production) would be wrongly reused. + 2) When group_order.pickup_date is set, the draft's pickup_date matches + it exactly. Without this, a draft created "now" for a stale/previous + pickup_date — but still inside the current create_date window — + would be wrongly reused instead of starting a fresh cart. + + Drafts failing either check belong to a previous cycle and must not be + reused — otherwise stale carts come back when the user re-enters the + order page. """ req = request_obj or request - from datetime import timedelta - - from dateutil.relativedelta import relativedelta if not group_order or not group_order.cutoff_date: return req.env["sale.order"] + from datetime import timedelta + + from dateutil.relativedelta import relativedelta + period_end = group_order.cutoff_date if group_order.period == "weekly": period_start = period_end - timedelta(days=6) @@ -98,6 +110,8 @@ def _find_recent_draft_order(self, partner_id, group_order, request_obj=None): ] if period_start: domain.append(("create_date", ">=", f"{period_start} 00:00:00")) + if group_order.pickup_date: + domain.append(("pickup_date", "=", group_order.pickup_date)) return ( req.env["sale.order"].sudo().search(domain, order="create_date desc", limit=1) diff --git a/website_sale_aplicoop/models/group_order.py b/website_sale_aplicoop/models/group_order.py index 8c716e0..95ae054 100644 --- a/website_sale_aplicoop/models/group_order.py +++ b/website_sale_aplicoop/models/group_order.py @@ -420,6 +420,7 @@ class GroupOrder(models.Model): taken = set() new_vals_list = [] for vals in vals_list: + vals = {k: v for k, v in vals.items() if k != "home_delivery"} slug = self._normalize_slug(vals.get("slug")) or self._generate_unique_slug( vals.get("name"), taken=taken ) @@ -428,7 +429,16 @@ class GroupOrder(models.Model): return super().create(new_vals_list) def write(self, vals): - """Normalize the slug, regenerating it from the name when emptied.""" + """Normalize the slug, regenerating it from the name when emptied. + + `home_delivery` is derived from `delivery_product_id` and must never + be settable directly — dropped here so a stray direct write can't + make it stick outside `_compute_home_delivery`. + """ + if "home_delivery" in vals: + vals = {k: v for k, v in vals.items() if k != "home_delivery"} + if not vals: + return True if "slug" not in vals: return super().write(vals) slug = self._normalize_slug(vals["slug"])