Problem/Motivation

On a resource with a venue (a seatmap), booking by quantity produces bookings that reference no place at all, so nothing limits them to the seats that exist.

The core composer resolves an individual booking's units through the yoyaku.placement_provider seam: BookingComposer tries each provider in priority order and the first whose applies() returns TRUE emits one line per unit. The comment on yoyaku.anonymous_placement_provider states the intent: "yoyaku_placement registers higher-priority providers that assign a place or zone." No such provider exists. PlacementProviderInterface has exactly one implementation, core's own AnonymousPlacementProvider, which emits lines carrying only the slot and tier.

yoyaku_placement does have a PlacementProvider class, but it is the map-flow booker: it takes an explicit place selection, implements no interface and carries no service tag, so the composer never sees it.

The consequences on a seatmapped slot: the calendar's quantity fields sell tickets that reference no seat; PlaceBookable, the constraint that protects a seat, returns no error as soon as the place value is empty, so nothing is enforced; and two tiers pricing the same grade can each be filled to that grade's size. A venue can be sold well beyond its seat count, and those bookings cannot be seated at all. This is the blocker underneath #3613137: Category free must be the min of the quota remaining and the free seats in its placement grade(s), which only corrects the displayed numbers.

Proposed resolution

Register the provider the seam expects, in yoyaku_placement. applies() returns TRUE when the resource's venue offers placement: at least one place today, and once sections carry a mode, a pooled section with capacity. place() assigns seats through the existing PlaceAssignment::proposeForCategory(), which already picks the best adjacent free places for the category's grades, and emits one quantity-1 line per unit carrying its place in values. Tag it above the core anonymous fallback.

With every line carrying a place, PlaceBookable enforces one booking per seat under the slot lock, so a placed resource can no longer be sold beyond its seats and no new constraint is needed.

When the assignment cannot supply the requested quantity the hold must fail with a clear "not enough places" message rather than fall back to a seat-less line: proposeForCategory() currently returns fewer places than asked, and the provider has to turn that into an exception.

Gating on "the venue offers placement" rather than "a venue is referenced" keeps a venue with no places behaving exactly as today, instead of refusing bookings it accepts now. Selling a section as a pool (free seating or standing) is a separate feature, filed alongside this one; the term for that axis is placed against pooled, not seated against standing, since an unreserved seated section and a standing hall behave identically for booking.

Remaining tasks

  • Add the provider and tag it above the anonymous fallback.
  • Fail cleanly when the seats cannot be supplied.
  • Kernel coverage: a placed resource seats a quantity booking; a shortfall is refused; a resource whose venue offers no placement still uses the anonymous provider.
  • Functional coverage of the calendar quantity path on a placed slot.
  • Document that on a placed resource the seatmap is the capacity.

User interface changes

A quantity booking on a placed resource comes with seats assigned, and a request that cannot be seated is refused with an explanatory message.

API changes

None. The seam and its interface already exist.

Issue fork yoyaku-3613506

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

Status: Active » Needs review

  • mably committed 4efaa253 on 1.x
    fix: #3613506 A quantity booking on a placed resource creates seat-less...
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.