Pin named places to an allotment, so a hall holds the same seats every time it runs

A hall can now hold particular places for an allotment, not just a number of them: the front row for the schools, the box for the guests.

A pinning is a named set of held places for a venue, holding places for every allotment at once. It is chosen per session, then per production, then from the venue default. That bottom rung is why it is a thing of its own rather than something on the placement configuration: a layout belongs to a production, so it cannot say that the matinee holds the front row for the schools and the evening holds it for the guests. The two stay independent, because a layout decides whether a place is offered by name at all and a pinning decides who holds it.

The held places carry a weight, and each session takes as many of them as it holds. One pinning naming thirty VIP places therefore serves a production holding ten and one holding thirty: the lightest win and the rest go to everybody else. The hold shrinks as the allotment is taken up, so the places bound never drifts from the count bound in core and the general capacity is never charged twice for one place.

A booker is told nothing. A held place reads as unavailable and cannot be chosen, and the payload a public map is sent carries no allotment at all. An operator holding choose booking allotment is sent the colors, and the map rings each held place in its allotment's.

A party may sit in an allotment's places only when every one of its lines draws on that allotment, so a place held for the schools does not seat a full-price companion who came in the same booking.

Deleting a pinning is refused only while a session still to come runs it. A past session never blocks it, which is what makes a pinning made for one evening disposable.

Cost. Nothing at all to a venue that holds no place by name. A venue that does pays two reads per page rather than per session: one for the pinning, one for what each allotment has taken. PinnedPlaceQueryBudgetTest asserts both, and the open place counts stay free of any allotment on purpose so the memo a calendar depends on is not fragmented.

Verification. Four new kernel classes; the whole yoyaku kernel suite (155 classes) passes locally. The counting tests were seen to fail against the unfixed code first (10 is identical to 8 on the headline case). phpcs, phpstan and cspell clean on the changed files; docs and fr.po updated in the same commit.

One drive-by kept: an 81-character line in yoyaku_placement_io/README.md that phpcs was already flagging.

Merge request reports

Loading
Loading