Issue #3613507: Sell a section as a pool: a per-event section mode (placed or pooled) for free seating and standing

MR1 of two on this issue: the model and the booking path. The picker and the venue map are the follow-up, so a pooled area is currently absent from the map rather than drawn as a block to take a quantity from.

What it does

A configuration stores its deviations as section settings (yoyaku_configuration_section): one decision per area, addressed at a whole section or at one subsection of it, saying placed, pooled or closed, plus a pool capacity and a pool grade.

That absorbs the two list fields the configuration used to carry. closed_subsections encoded "section id:number" strings only because there was no per-area record to hang the number on, and that reason is gone. Section gains an optional default_mode, so a normally standing hall states it once instead of in every configuration.

Resolution, most specific first: the setting for the place's own subsection, else the section-wide setting, else the section default, else placed. SectionSettings is the single home of that rule, so the picker, the map, the provider, the availability bound and the constraints all read the same answer.

Capacity and price

A pool holds the capacity its setting declares, else the number of places its area holds, so a section of numbered places sold unreserved needs no number typed and a standing floor that was never drawn as places needs one.

A tariff prices grades, so a pool needs one: the grade its setting declares, else the grades of its places. Declaring it is the only way to price an area holding no places, and the form requires it when a declared capacity covers places of several grades, since the capacity could not otherwise be attributed to a grade.

Enforcement

A pooled unit becomes a line naming the area (section, and subsection when the pool is one slice) rather than a place. That pair is the pool's identity, recorded on the booking rather than a pointer to the setting, because a setting is a per-event decision that gets edited and deleted while a ticket has to keep meaning something.

PlaceBookable ignores such a line, since there is no place to protect. SectionPoolBookable bounds it by the pool under the slot lock, counting every consuming booking in the area: the pooled lines naming it, and the placed lines whose places sit inside it. Without the second, an area pooled after some of its places were sold assigned would sell those seats again, oversold by exactly the number already gone. A test proves it: with that half removed the pool reports 4 free where 2 remain.

A pooled area's places leave the place market, so they leave the picker, the map, the group proposal and the place counts, and its capacity comes back through the availability bound instead. Nothing is counted twice.

Admin UI

A configuration's areas are managed the way the rest of the inventory is: the configuration has an Areas tab listing only what it decides about, one row per area with Edit and Delete, and Add area under them. InventoryListTrait holds the shape those tabs share, and the delete form (renamed InventoryDeleteForm) returns each row to the tab it was managed from.

The add form asks for the area as one choice, listing the sections of the venue and the subsections its places carry, so nobody types a slice number, and an area the configuration already decides about is not offered: one setting per area cannot be broken from the form rather than being reported after the fact. The mode is radios that say what each does, and the pool capacity and grade appear only for a pooled area. The Areas list names each area exactly as the picker does, section and slice in one column.

Verified

  • New: SectionPoolBookableTest, PooledSectionProviderTest, ConfigurationSectionTest, PoolAvailabilityBoundTest. Updated: ConfigurationTest, PlacementExampleTest, PlacementAdminUiTest (the configuration form round trip and its validation).
  • Whole yoyaku kernel sweep green: 19 test directories, including the root suite (115 tests) and placement (63 tests). Placement Functional green (3 tests).
  • phpcs (with warnings) and cspell clean on src and tests; phpstan two errors below the branch point.
  • Exercised on a real venue: the existing reduced-capacity configuration of a 1422-seat hall resolves to exactly the same 1131 open places as before, its 9 closures having become 9 settings; pooling its parterre through the form moves 184 places out of the place count and into a pool, and putting the line back to Default deletes the setting.

Data model

New yoyaku_configuration_section. yoyaku_section: default_mode. yoyaku_booking: section, subsection, plus a (slot, section, state) index. yoyaku_configuration: closed_sections and closed_subsections removed, and with them getClosedSectionIds(), getClosedSubsections(), isSectionOpen() and isSubsectionOpen().

Edited by Frank Mably

Merge request reports

Loading