[FIX] website_sale_aplicoop: scope draft lookup to active cycle window

The /eskaera/load-draft endpoint was returning previous-cycle drafts when
group_order.cutoff_date was still in the future but group_order.pickup_date
had not yet been recomputed (its compute only depends on pickup_day and
start_date). Both the stale group_order.pickup_date and the old draft's
pickup_date held the same past value, so the exact-pickup-date filter in
_find_recent_draft_order matched the stale draft and the cart was
repopulated with old products immediately after being cleared.

Replace the pickup_date exact-match + current-week fallback with a single
cutoff-anchored window: create_date in [cutoff_date - 6 days, cutoff_date].
Drafts created outside that window belong to a previous cycle and must
not be reused. The change applies to all four callers (load-draft,
clear-cart, save-order, confirm) so the merge/confirm paths also stop
attaching to stale drafts.

Covered by a new regression test that mirrors the production setup
(matching pickup_date, draft create_date backdated 10 days).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
GitHub Copilot 2026-05-29 17:40:48 +02:00
parent 6125515772
commit 3b8cb7582b
2 changed files with 101 additions and 13 deletions

View file

@ -134,6 +134,93 @@ class TestGroupOrderStatusEndpoint(TransactionCase):
self.assertEqual(data.get("action"), "clear_cart")
self.assertNotIn("items", data)
def test_find_recent_draft_excludes_previous_cycle(self):
"""_find_recent_draft_order must only return drafts whose create_date
falls in [cutoff_date - 6 days, cutoff_date].
Regression (observed on stage.elikabilbo.eus): when the previous
cycle's draft still matched group_order.pickup_date (because both
were the same stale value), the helper returned it and the cart
was repopulated with old products on the next page load.
"""
partner = self.env.user.partner_id
# Ensure cutoff_date is in the (near) future so the active window is
# well-defined and the cutoff-passed guard is NOT what's being tested.
future_cutoff = fields.Date.today() + timedelta(days=2)
self.group_order.sudo().write({"cutoff_date": future_cutoff})
# Mirror production conditions where pickup_date matches between
# group_order and the stale draft — this is what made the old
# exact-pickup-date filter return the previous cycle's draft.
matching_pickup_date = self.group_order.pickup_date
previous_cycle_create = fields.Datetime.now() - timedelta(days=10)
stale_draft = self.env["sale.order"].create(
{
"partner_id": partner.id,
"group_order_id": self.group_order.id,
"pickup_date": matching_pickup_date,
"order_line": [
(
0,
0,
{
"product_id": self.product.id,
"product_uom_qty": 3,
},
)
],
}
)
# create_date is auto-stamped; backdate it directly to simulate a draft
# left over from a previous weekly cycle.
self.env.cr.execute(
"UPDATE sale_order SET create_date = %s WHERE id = %s",
(previous_cycle_create, stale_draft.id),
)
stale_draft.invalidate_recordset(["create_date"])
request_mock = self._build_request_mock({})
with patch(
"odoo.addons.website_sale_aplicoop.controllers.website_sale.request",
request_mock,
):
found = self.controller._find_recent_draft_order(
partner.id, self.group_order
)
self.assertFalse(
found,
"Previous-cycle draft must not be returned (create_date is outside "
"[cutoff-6, cutoff] window) even when pickup_date matches",
)
# Now create a current-cycle draft and confirm it IS returned.
current_draft = self.env["sale.order"].create(
{
"partner_id": partner.id,
"group_order_id": self.group_order.id,
"pickup_date": matching_pickup_date,
"order_line": [
(
0,
0,
{
"product_id": self.product.id,
"product_uom_qty": 1,
},
)
],
}
)
with patch(
"odoo.addons.website_sale_aplicoop.controllers.website_sale.request",
request_mock,
):
found = self.controller._find_recent_draft_order(
partner.id, self.group_order
)
self.assertEqual(found.id, current_draft.id)
def test_save_order_endpoint_rejects_closed_group_order(self):
"""/eskaera/save-order must reject closed group order with clear_cart action."""
self.group_order.action_close()