Aller au contenu

Le multi-société

Un mot sur un modèle, et tout change : une colonne apparaît, chaque lecture est filtrée, chaque création est marquée, et certaines écritures deviennent impossibles.

registry.register_model(
"books_invoice",
module="books",
fields=[...],
multi_company=True,
)

Ce que ça fait, exactement

Sur une base réelle, avec deux sociétés et une facture dans chacune :

colonnes de books_invoice : ['id', 'number', 'company_id', 'created_at', ...]
créé sans company_id -> company_id rempli : 1
société 2 : ce qu'elle voit -> ['TG-001']
société 1 : ce qu'elle voit -> ['FR-001']
vue consolidée (deux sociétés actives) -> ['FR-001', 'TG-001']
écriture vers une société interdite -> CrossCompanyWriteRefusedError

Quatre comportements, aucun à écrire :

  1. La colonne company_id est ajoutée au modèle. Vous ne la déclarez pas.
  2. Elle se remplit toute seule à la création, avec la société courante de l’utilisateur.
  3. Toute lecture est filtrée sur les sociétés actives, dans l’ORM, donc partout : l’API, un travail de fond, un export. Ce n’est pas un filtre d’écran.
  4. Écrire vers une société à laquelle l’utilisateur n’a pas accès est refusé, même par un chemin indirect. C’est l’interdiction n°35 du projet, tenue par le noyau.

La vue consolidée (plusieurs sociétés actives à la fois) montre les deux, ce qui est le comportement attendu d’un sélecteur « toutes les sociétés ».

Quand le poser

Là où il y a une frontière légale ou comptable, et nulle part ailleurs.

C’est la décision D1 du projet, et elle est délibérément restrictive. Une facture, une écriture comptable, une commande, une paie appartiennent à une entité juridique : deux sociétés ne partagent pas leur comptabilité, et une facture appartient à celle qui l’émet.

Un produit du catalogue, un partenaire, une étiquette, un modèle de courriel : non. Ce sont des référentiels partagés, et les dupliquer par société est une charge de saisie sans contrepartie.

Ce que ça implique pour vos écrans

Rien à faire. Le sélecteur de société de l’en-tête, le filtrage, le marquage à la création : le moteur générique s’en occupe parce que la déclaration est au niveau du modèle. C’est la raison pour laquelle la sécurité multi-société est dans l’ORM et pas dans les endpoints : un module qui oublierait un contrôle ne peut pas fuiter, il n’a rien à contrôler.

Et si vous vous trompez ?

Passer un modèle de mono à multi est une migration : la colonne s’ajoute, et il faut décider à quelle société appartiennent les lignes existantes. C’est une reprise de données, pas un drapeau qu’on bascule un après-midi. L’inverse, retirer le drapeau, laisse une colonne orpheline et des données qui redeviennent visibles de partout d’un coup.

C’est pour ça que la question mérite d’être posée avant la première ligne du modèle, et que la réponse par défaut est non.