Issue #3613506: A quantity booking on a placed resource creates seat-less lines: the placement provider the core seam expects is never registered

Change

  • New VenuePlacementProvider, the provider this module was always meant to register on the yoyaku.placement_provider seam. applies() is TRUE when the resource's venue offers placement; place() takes seats from the existing PlaceAssignment::proposeForCategory() and returns one quantity-1 line per unit carrying values['place']. Tagged at priority 0, above the core anonymous fallback at -100.
  • A shortfall throws CapacityExceededException rather than falling back to a place-less line: a booking that cannot be seated must fail, not be sold. SelectionBooker already catches BookingException, so the calendar reports it as a form error.
  • PlaceAvailability gains resourceVenueId() (the gate asks about a resource, not a slot) and venueOffersPlacement(), a per-request cached count that today means "the venue holds at least one place". That method is the single extension point for pooled sections: a venue of pool-only sections will answer TRUE there without holding a place.
  • PlaceAssignment::proposeForCategory() now accepts a NULL category, so an untiered booking on a placed slot is seated from any free place instead of escaping placement.

Why the gate is "offers placement" and not "has a venue"

A venue with no place is not a sold-out venue, it is a venue that does not place. Refusing its bookings would break a setup that works today, so those units keep falling through to the anonymous provider unchanged.

Naming

The old yoyaku_placement.placement_provider service (the map booker) is renamed to yoyaku_placement.place_selection_booker, class PlaceSelectionBooker, with its test renamed to match. It books a selection the caller already made and reports a shortfall as an error string, while the seam places the units of one request and throws. Its old name read like the seam and helped hide that the seam had no implementation at all, so the rename belongs with the fix rather than in a follow-up.

Not in this change

The displayed availability numbers are [#3613137], and selling a section as a pool is [#3613507].

Testing

New VenuePlacementProviderTest, five cases: a quantity booking comes out seated with distinct places belonging to the venue; a shortfall is refused; an untiered booking is seated; a venue with no places falls back to anonymous; a resource with no venue is untouched. Verified it fails on unfixed code (three failures: the missing place key, and the exception not thrown) and passes with the fix. Whole yoyaku kernel suite green, 19 directories and 258 test methods. phpcs and phpcbf clean at --warning-severity=1, cspell clean.

No Functional test is included: the calendar path goes through BookingComposer, which the kernel test covers directly, so the Functional case would only add the HTTP-level rendering of the error message.

Edited by Frank Mably

Merge request reports

Loading