Issue #3611662: Venue sections (first-class), seat geometry and party-adjacency auto-assignment
First slice of the venues-as-sections model (#3611662): the seated part, on
top of the merged seating foundation (#3611637). It delivers the seat map and
automatic party-adjacency assignment; general admission, section mode,
per-slot enable/disable and GA capacity pools are a follow-up on the same
entity.
What this adds
yoyaku_section— a first-class, translatable venue section (orchestre, corbeille, balcon, paradis). The public name lives once and localizes, instead of a string copied onto every seat.- Seat geometry —
seat.sectionbecomes a reference toyoyaku_section; seats gainrowandposition, which is what makes "adjacent" definable. SeatAssignment::proposeAdjacent()— proposes the best free seats for a party: a run of consecutive positions in one row when possible, otherwise a best-effort scattered set flagged so the caller can warn. Seats are held as a group, so each is re-checked bySeatBookableunder the slot lock and nothing is oversold.- Picker — a per-section seat map and a "seat a party together" flow.
Capacity
Seated capacity stays the seat inventory itself (each seat sold once per slot,
enforced by SeatBookable); this slice adds no numeric capacity field. A GA
pool SUM (and the precomputed reserved counter that would optimize it) come
with the general-admission follow-up.
Deferred (follow-up on this issue)
- General-admission sections and mixed venues, section
mode, per-slot enable/disable, GA capacity pools. - A graphical, click-on-a-map seat map (the server-rendered per-section/row layout here is the first step).
Testing
Kernel: section CRUD + translatable label, adjacency (contiguous, skip-taken, scattered fallback, allowed-category, group-hold authoritative under the lock). Functional: pick + book a seat, auto-assign a party of adjacent seats. phpcs (Drupal, DrupalPractice), phpstan and cspell clean locally.