Issue #3615159: Present a slot's offers as configured: class panels that fill, and an order the operator chose
Two faults in how a slot's offers are presented, both visible on one booking page. This MR lands the first; the second follows in the same branch.
1. The class panels rendered empty (in this MR)
A slot whose tariffs name a tariff class rendered its panels and then rendered every quantity field flat above them: seven offers in a list, followed by two empty accordions.
#group only relocates an element whose type carries core's processGroup(), which is details and its relatives. A number has neither processGroup() in #process nor preRenderGroup() in #pre_render, so it never registered with the panel and the panel never absorbed it. The property was set and did nothing. It never worked, and the test asserted that #group held the expected string, which stayed true the whole time the page was wrong.
Fixed by rendering the field inside its panel with #parents pinned to quantity][<tariff>, so reconcile() reads the tree it always did. The AJAX refresh walked the children of quantity to build one entry per offer, which would have gone silent on every panelled offer, so both places are now read through one accessor rather than at each call site.
Measured on a real production before and after: 0 of 7 inputs inside the panels, then 7 of 7.
Coverage
The assertion the old test lacked: the rendered markup puts each input inside a details exactly once. It is built through the form builder rather than buildForm(), because buildForm() returns an unprocessed array with no #name on anything and so renders no inputs at all, which would have made the assertion vacuously true. Reverting the fix turns these red:
- the field is inside its panel and no longer under
quantity - its
#parentsstill point at the value tree - two panels render, both inputs inside one, neither rendered twice
- the refresh command carries both a panelled and a non-panelled offer
2. The order was a tie-break, and a different one per page
The Tariffs tab sorted weight then label; the engine's read, which the booking page walks, sorted weight alone, so the database returned tariffs of equal weight however it liked. Every tariff of a freshly priced production has weight 0, so the same seven tariffs came out in one order for an operator and another for a booker, and neither was anyone's choice: the weight field existed and no screen offered it.
- The Tariffs tab is dragged. It becomes a form, and the weights are rewritten as a sequence from zero on save rather than typed, so the next tariff added lands after the last one instead of inside an arrangement someone chose.
- The engine ties on the label, as the tab always did, so the two reads agree.
BookingResourceTariffgainedgetWeight(), which both its siblings already had and which only mattered once the weight did.
Verified on a real production: the tab and the availability map now return the same sequence, 14, 15, 16, 18, 19, 20, 17, where the engine previously put Visibilité réduite fourth.
One consequence worth reviewing
A row is now a form element, because a weight is one. The two columns submodules add through yoyaku_ui_entity_table therefore reach two shapes: elements keyed by entity id on this table, strings in #rows on the preset and slot tables. Both branches are covered, including yoyaku_placement's Place grades column, which had no test at all before this. Removing either branch turns the new tests red.