Allotments hold a number of places, from #3615101: Add allotments, named slices of a session's capacity that given tariffs draw from. This adds the other half: naming which places, so a house can hold the front row for the schools rather than twenty seats somewhere.
A pinning (yoyaku_pinning, in yoyaku_placement) is a named set of held places for a venue, holding places for every allotment at once: some rows for the schools, a box for the guests. Its places are yoyaku_pinned_place, one row per place, (tenant, pinning, allotment, place, weight). The invariant worth enforcing is that a place belongs to at most one allotment within a pinning, and only a unique key on (pinning, place) can state it; a multi-value place field would push that onto a validator scanning every record.
Why not the placement configuration
A configuration belongs to a resource, so it cannot tell the matinee from the evening. Holding the front row for the schools at three o'clock and for the guests at eight is exactly what this is asked to do, so a pinning is chosen one rung lower: the session's, else the resource's, else the venue's default_pinning, else no place is held by name.
The two stay independent, and answer different questions. A configuration decides whether an area is offered place by place, as a pool, or not at all. A pinning decides who holds the places that are offered by name. So one pinning is used under several layouts, and a place in an area a layout pools or closes is simply dropped when the pinning is read rather than refused when it is saved.
Named places are a subset of the number, not an alternative to it
The number always comes from the resource or the session, so the pinned record carries no capacity of its own. A pinning names candidates in held order, and each session takes as many as it holds: thirty named and ten held holds the ten lightest, and the other twenty are ordinary places. Thirty named and nothing declared holds all thirty, because naming them is then the whole declaration. Thirty named and zero held tonight holds none.
One pinning therefore serves a resource holding ten and one holding thirty, so there is no VIP 10 and VIP 20 to keep in step and nothing for a tariff to be re-pointed at. Naming more places than a session holds is ordinary rather than an error, which is why nothing validates the pinning against the number.
The hold also shrinks as the allotment is taken up: ten held with three schools booked holds seven places, whether those three sat in named places or anywhere else. That is what keeps the place bound and the count bound from drifting apart, so the general capacity is never charged twice for one place.
What this settles
- A line drawing on an allotment is not kept out of its own places. That is the point of holding them: the places kept for the schools are the ones a school tariff can still take once the hall is otherwise full.
- A party may sit in an allotment's places only when every one of its lines draws on that allotment. A party mixing allotments, or mixing one with the general capacity, sits in the open house: a place held for the schools must not seat a full-price companion who came in the same booking.
- The refusal lives under the slot lock, in
PlaceBookable. A read cannot hold anything, and a place can be named directly on a map or by an operator. - Deleting a pinning is refused only while a session still to come runs it, by its own choice, its resource's or its venue's default. A past session never blocks it: a booking carries its own place and its own allotment, so deleting the pinning cannot change a figure already reported. A pinning made for one evening is meant to be thrown away afterwards.
Two payloads, one geometry
The public map never carries an allotment, not even as a hidden key: a place another allotment holds reads exactly like a taken one, so a booker cannot tell a held place from a booked one. An operator holding choose booking allotment is sent the colors, and the map rings each held place in its allotment's. VenueMapBuilder already splits cached venue geometry from per-slot state, so this is a second state layer rather than a fork of it.
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. The open place counts stay free of any allotment on purpose, since that memo is what lets a calendar be answered in a handful of queries and taking an allotment into it would compute a variant per allotment on the page.
Issue fork yoyaku-3615568
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
Comment #2
mably commentedComment #4
mably commentedComment #6
mably commented