[FIX] website_sale_aplicoop: two draft-reuse and compute-field bugs
- group.order.home_delivery is a stored compute field with no inverse; Odoo still lets write() set it directly, so a stray direct write stuck instead of always being re-derived from delivery_product_id. create() and write() now strip it from vals, same pattern already used for slug. - _find_recent_draft_order only bounded drafts by create_date, so a freshly created draft for a stale/previous pickup_date (but created "now") was wrongly reused. Fix requires both create_date to fall in the active window AND pickup_date to match when set — the latter alone isn't enough either, per the regression already covered by test_find_recent_draft_excludes_previous_cycle (observed in production at stage.elikabilbo.eus).
This commit is contained in:
parent
e59c706ef6
commit
158933e96e
2 changed files with 34 additions and 10 deletions
|
|
@ -65,21 +65,33 @@ def _get_salesperson_for_order(self, partner):
|
||||||
def _find_recent_draft_order(self, partner_id, group_order, request_obj=None):
|
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.
|
"""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
|
A draft only counts as "current cycle" when it satisfies BOTH of these
|
||||||
depends on the order period: 7 days (weekly), 14 days (biweekly), one
|
(neither is sufficient on its own — see the regression each one guards):
|
||||||
month (monthly). One-time orders have a single cycle starting at
|
|
||||||
start_date (or unbounded when start_date is empty). Drafts created
|
1) create_date falls within the active window — derived from
|
||||||
outside this window belong to a previous cycle and must not be reused —
|
group_order.cutoff_date and the order period (7 days weekly, 14
|
||||||
otherwise stale carts come back when the user re-enters the order page.
|
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
|
req = request_obj or request
|
||||||
from datetime import timedelta
|
|
||||||
|
|
||||||
from dateutil.relativedelta import relativedelta
|
|
||||||
|
|
||||||
if not group_order or not group_order.cutoff_date:
|
if not group_order or not group_order.cutoff_date:
|
||||||
return req.env["sale.order"]
|
return req.env["sale.order"]
|
||||||
|
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
|
from dateutil.relativedelta import relativedelta
|
||||||
|
|
||||||
period_end = group_order.cutoff_date
|
period_end = group_order.cutoff_date
|
||||||
if group_order.period == "weekly":
|
if group_order.period == "weekly":
|
||||||
period_start = period_end - timedelta(days=6)
|
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:
|
if period_start:
|
||||||
domain.append(("create_date", ">=", f"{period_start} 00:00:00"))
|
domain.append(("create_date", ">=", f"{period_start} 00:00:00"))
|
||||||
|
if group_order.pickup_date:
|
||||||
|
domain.append(("pickup_date", "=", group_order.pickup_date))
|
||||||
|
|
||||||
return (
|
return (
|
||||||
req.env["sale.order"].sudo().search(domain, order="create_date desc", limit=1)
|
req.env["sale.order"].sudo().search(domain, order="create_date desc", limit=1)
|
||||||
|
|
|
||||||
|
|
@ -420,6 +420,7 @@ class GroupOrder(models.Model):
|
||||||
taken = set()
|
taken = set()
|
||||||
new_vals_list = []
|
new_vals_list = []
|
||||||
for vals in 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(
|
slug = self._normalize_slug(vals.get("slug")) or self._generate_unique_slug(
|
||||||
vals.get("name"), taken=taken
|
vals.get("name"), taken=taken
|
||||||
)
|
)
|
||||||
|
|
@ -428,7 +429,16 @@ class GroupOrder(models.Model):
|
||||||
return super().create(new_vals_list)
|
return super().create(new_vals_list)
|
||||||
|
|
||||||
def write(self, vals):
|
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:
|
if "slug" not in vals:
|
||||||
return super().write(vals)
|
return super().write(vals)
|
||||||
slug = self._normalize_slug(vals["slug"])
|
slug = self._normalize_slug(vals["slug"])
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue