The calendar paints a day blue and keeps it selectable when the visitor already holds it, so their own basket does not read as taken by somebody else. The overlay comes from the yoyaku_held cookie, which HeldCookie::value() builds from the cart's held lines, so at the moment it is written it is right.

Nothing reliably retires it, and it then describes a basket that no longer exists.

Observed: a one-place refuge slot whose only place is taken by the visitor's own completed booking is drawn blue and selectable. Selecting it and submitting gets "Slot 364: 0 of 1 place(s) left, 1 requested". The day is genuinely full, and full because of them, but the page invites the booking anyway and answers with an engine message.

Why it survives

Both mechanisms that should end the overlay fail together, and for the same reason: the booking finished away from the visitor's browser.

  • The server clear needs their own request. HeldCookieSubscriber does listen for the completion and would clear the cookie, but onBookingChange() flags the current request, and its own comment records the hole: a mutation off an HTTP request has none to flag. A workflow-driven booking confirms, completes and settles in workflow, cron and operator requests, never in the visitor's.
  • The client cannot expire it either. The overlay is dropped only when the current time has passed the earliest hold deadline the cookie carries, and only when that deadline is truthy. A workflow suspends the holds so it owns the windows, which clears hold_expires, so value() leaves e at 0, so the guard is falsy and the overlay never lapses on its own.

With the cookie stale, applyHeld() does exactly what it promises for a live basket: adds the places back to the availability, lifts the day out of full, tags it mine and leaves it selectable.

Fix

Smaller than it first looks, because the rule is already written and already right. HeldCookie::value() starts from BookingCart::current(), which returns the order only while it is empty or pending and deliberately withholds one that has moved into checkout. So the overlay is only ever written for an editable basket, and for the booking above value() would answer NULL today if anything asked it.

Nothing asks it. The browser is holding a snapshot taken while the order was pending, and the only thing that would refresh it is a mutation landing in the visitor's own request, which a workflow-driven booking never produces.

So this needs no new field and no client-side notion of pending. It needs the existing rule re-evaluated whenever a surface draws the overlay: BookingCalendar flags the refresh as it builds, and the response subscriber then calls value(), gets NULL, and clears the cookie. The cart lookup is one query and these pages are uncacheable already.

The case worth having on its own falls out of that: after a refusal the server has just proved the overlay wrong, and the re-rendered page is such a draw, so it corrects itself instead of showing the same misleading day again.

Coverage

The overlay is only ever tested while it is true. The assertion that matters is not that the client ignores a stale overlay, but that the overlay does not outlive the basket, which is a server-side test and far cheaper than a JS one.

  • drawing a booking surface clears the cookie once the cart no longer holds the lines, without the mutation having happened in that request;
  • a day whose only place is the visitor's own settled booking renders as full rather than as theirs, and cannot be selected;
  • a refused hold leaves the response carrying a corrected overlay.

Issue fork yoyaku-3614505

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

mably created an issue. See original summary.

mably’s picture

Issue summary: View changes
mably’s picture

Issue summary: View changes

mably’s picture

Status: Active » Needs review

  • mably committed f8cddd10 on 1.x
    fix: #3614505 The held overlay outlives the basket, so a day full of the...
mably’s picture

Status: Needs review » Fixed

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.