Aller au contenu

ADR-0017 — Mobilité groupe (étage 2) : Entreprise, ConventionGroupe, mutation dérivée

Statut Accepté — 2026-06-17
Portée Modèle de domaine (Entreprise/Convention), reconstruction des trajectoires, attribution du passif
Décision L'entreprise est une dimension de la photo (pas un flux d'événements) ; la mutation est dérivée par comparaison de photos ; ConventionGroupe ne porte en v1 que la portabilité d'ancienneté ; l'attribution du passif v1 est un breakdown par entreprise
Réf. Vision — étage 2 ; ADR-0005 — Tenant = groupe ; ADR-0016 — Corpus

🎯 À retenir

L'Entreprise est une dimension de la photo (Salarie.entreprise_id), pas un flux d'événements : la mutation inter-filiales est dérivée par comparaison de deux photos (la primitive de l'étage 1, garde-fou 2 honoré). ConventionGroupe ne porte en v1 que la portabilité d'ancienneté ; l'attribution du passif v1 est un simple breakdown par entreprise.

Contexte

ADR-0005 a tranché l'infrastructure : tenant = groupe, l'Entreprise est une entité interne, donc une mutation inter-filiales est un événement intra-tenant (l'isolation 404 reste intacte). La vision (étage 2) décrit pourtant la mutation comme « une classe d'événements de plus »mutation(source → cible, date). Or l'étage 1 a délibérément refusé l'event-sourcing (garde-fou 2) : on reconstruit depuis des photos datées à matricule stable. Il faut réconcilier les deux.

Décision

Quatre partis pris, ratifiés :

  • D-MOB-1 — Entreprise = dimension sur la photo (option α, pas de table d'événements). L'entreprise d'un salarié est portée par l'observation (Salarie.entreprise_id), pas par un flux d'événements. Une mutation est dérivée : matricule en entreprise X au t₀, Y au t₁ → mutation reconstruite — exactement la primitive de l'étage 1 (transition entre deux observations). On honore le garde-fou 2 ; la physique « force pulsée » reste dans la doc, pas dans le schéma.
  • D-MOB-2 — ConventionGroupe ne porte qu'anciennete_portable en v1. La seule règle actuariellement matérielle aujourd'hui : l'ancienneté se cumule-t-elle au niveau groupe à travers une mutation ? Le reste (reset partiel, conditions de vesting) est différé (YAGNI).
  • D-MOB-3 — Attribution du passif v1 = breakdown par entreprise. Somme des DBO par-salarié regroupées par entreprise_id (réutilise la sortie moteur existante). La méthode profonde (prorata des périodes de service inter-entités, transfert du passif à la mutation) est différée, avec son propre oracle le jour venu.
  • D-MOB-4 — Confidentialité inter-filiales différée (YAGNI). Conforme à ADR-0005 : on ne construit pas de RBAC scopé entreprise tant qu'un client ne l'exige pas. On préserve l'isolation 404 cross-tenant ; l'entreprise reste une dimension interne au tenant.
  • D-MOB-5 — Surface de gestion staff, sans DELETE. L'écran /groupes (junior + senior, périmètre = celui des trajectoires) crée/édite les conventions et rattache les filiales. Sans lui, anciennete_portable reste à son défaut prudent (portable) et la branche « non portable → reset attendu, silencieux » est inatteignable en prod : cet écran la rend pilotable. Aucun archivage en v1 — une filiale portée par des photos est un fait historique (non supprimable), une convention non rattachée est inerte ; une erreur de saisie se corrige par édition (on évite ainsi d'exposer ces entités dans la corbeille). Les filiales naissent de l'ingestion (get-or-create) ; l'écran ne les crée pas.

Modèle

  • Entreprise : id, tenant_schema (le groupe, indexé), nom, convention_id?, archived_at, created_at. NULL de convention_id = pas de convention déclarée.
  • ConventionGroupe : id, tenant_schema, nom, anciennete_portable: bool, archived_at, created_at.
  • Salarie.entreprise_id : int | None. NULL = cas dégénéré (groupe mono-entreprise, ou données pré-étage-2) → strictement rétrocompatible.
  • La photo (Dossier) reste l'effectif du groupe à une date ; chaque ligne porte son entreprise_id. Pas d'entreprise sur le Dossier.

Conséquences

  • Reconstruction enrichie (réutilise l'étage 1) : la mutation devient une transition dérivée ; nouvelle anomalie convention-aware — ancienneté ré-initialisée à une mutation alors que la convention la dit portable.
  • Le jeu de test multi-entités naît dans le corpus (validation/datasets/multi-entites/, ADR-0016) avec Green (bouclage / breakdown par entité) et Red (reset d'ancienneté sous convention portable).
  • Piège prod (db.py) : Salarie.entreprise_id est une colonne sur une table existante → DOIT entrer dans _COLONNES_AJOUTEES["salarie"], sinon 500 prod. Entreprise/ConventionGroupe sont des tables neuves → créées par create_all (déjà importées via models.py).

🛑 Irréversible

Salarie.entreprise_id s'ajoute à une table existante : sans entrée dans _COLONNES_AJOUTEES["salarie"] (db.py), la prod (SQLite déployée) renvoie 500. Les tables neuves Entreprise / ConventionGroupe passent par create_all — pas elles.

Déclencheur de réexamen (pré-enregistré)

Si un commissaire aux comptes exige l'attribution fine du passif (prorata des périodes de service par entité, ou transfert à la mutation), ouvrir un ADR dédié avec son oracle — la v1 (breakdown) ne le couvre pas et ne le prétend pas.