Issue #3620544: Give a new row the tenant it belongs to instead of the default one, and let an allotment be shared across tenants

This is the whole of the tenant rule, on both sides.

A row belonging to another row takes that row's tenant. TenantHooks settles it over the parent_fields chain each entity type already declares, so a submodule's type is covered by declaring what it belongs to. The thirteen per-screen copies of the rule are removed: an add form names the parent, and the tenant follows from that once.

A row belonging to nothing but its tenant takes the tenant being served. TenantField reads its default from the tenant context. The booking channel form opens on the same answer, and the venue importer stamps the venue alone, its inventory following from it.

The parent's tenant is read as one column, not by loading the entity. The first version used EntityParents::getNearest(); the full kernel suite went red in eleven placement classes, availability answering differently, while the hook rewrote no tenant at all. A load in the middle of a write primes what a later read reuses. Reading one indexed column does not, and is cheaper on a path every created row goes down.

The booking pays nothing. It declares tenant_from_parent: FALSE, because a hold agrees every ask on one tenant before anything is written and refuses the ones that span two. HoldCostTest and PlaceHoldCostTest are back at their budgets.

An allotment may be shared with every tenant. Empty means shared, the shape a resource type already has. Only the name and the color are shared: the units held live on the resource and the slot, so counting, release and reporting stay inside a realm. The seven operator-picked allotment references are scoped through a new AllotmentSelection; the booking's stamp deliberately stays on the generic handler, since the engine also runs where there is no realm to ask. Renaming or deleting a shared name needs administer shared allotments.

A shared row's tenant is stored as NULL rather than the empty string, so the selection asks for that explicitly. TenantStampTest records it, because a condition listing '' matches no shared allotment.

Tests. yoyaku_tenant_test gives every test a switchable served tenant, the shape yoyaku_channel_test already uses for the front. TenantStampTest and AllotmentScopeTest cover the writes and the offer; both were seen to fail against the unfixed code, each half independently. TenantAddFormTest covers the browser path.

Kernel suite 256/256, phpcs and phpstan clean, French complete.

Single-tenant sites are unaffected throughout.

Merge request reports

Loading
Loading