Issue #3616821: Stop the plan posting a click a limit has already refused
The plan bounded a booker by what the hall had left and by nothing else, so a per-order rule was invisible on it: the booker pressed, the click was posted, the engine refused it under the slot lock, and the answer came back as a message.
The split
The allowance is configuration and the same for everybody, so it travels in the shared availability document, per tariff, beside the prices. How much of it one booker has left cannot travel there at all: that document is kept for seconds and served to every visitor, and a remaining count in it would hand one booker's basket to the next. So the browser subtracts what it already holds, which it knows exactly - its own seats plus its own block units - and the bound is exact with no extra request.
Every gesture, not one
- A block's stepper stops at the lower of what the area has left and what the allowance leaves, and states which of the two stopped it, in the words the booking page uses for the same rule (
Limited to @count per order.). - An area's outline inherits it by calling that stepper, and a press it cannot honor is answered rather than swallowed.
- A seat click gets the same guard. It was the one gesture with none: a stepper reads its allowance off its own maximum, but a click on a seat checked nothing.
- Repricing a held seat asks it of the tariff the seat is moving to.
The engine stays the authority
The guard is advisory and deliberately looser than the engine where the two can disagree: an attachment on a resource caps every tariff it offers together while the plan subtracts per tariff, and a policy reaching wider than one session cannot be computed from one map at all. Looser is the only safe direction - a guard stricter than the engine would refuse a click the engine would have taken. The refusal under the slot lock is what enforces the rule, and the map shows it the way it shows any other.
Two found while in there
A run past a limit gave an assigned area's booker nothing. Such an area is asked for as a party, so the house composes the whole ask at once, and its refusal refused all of it whatever the reason. That is right when the reason is the room - four together or none - and wrong when the reason is a rule about how many one booking may take, since such a rule states the number that would be allowed. The ask is remade at that number. A refusal naming no number still refuses whole. A run of seat clicks and a run on a pooled block already kept what the rule covered; this makes the third gesture agree, and both are now pinned by tests.
The settled half was under-tagged. It is held until something invalidates it, and what invalidates it is the list of things an availability figure is made of. A ceiling is not a figure - it is read from four hosts, two of which (the resource and its type) are outside that list - and neither are the prices the payment layer fills through the same alter. So an operator lowering a limit, or changing a price on the resource, was answered with the old entry for ever.
Coverage
VenueMapCeilingTest(kernel): the allowance travels per tariff, an uncapped tariff publishes none, a session pricing nothing publishes its own, and lowering a limit on the resource reaches a document already built.VenueMapPickerTest: the stepper stops and posts nothing; a seat past the allowance is not posted; a run past the limit keeps what it allows, for seats and for units.AssignedAreaPickerTest: a run past the limit keeps what it allows.- The two tests that covered the engine's own refusal now attach the rule after the page has drawn, which is the case the client-side guard cannot cover and the one that path exists for.
Every new test was seen to fail against the unfixed code first.
ScriptBytes re-measured at 60226 (was 57897 locally) and recorded rather than nudged.