ADR-0020 — Identité client, annuaire et anti-doublon à l'instigation¶
| Statut | ✅ Accepté — 2026-06-19 (archivage d'un client vide livré ; fusion : ouverte) |
| Portée | Modèle Tenant (+ nom), instigation POST /api/demandes/pour-compte, nouvel GET /api/clients, écran Cabinet (senior) |
| Décision | Un client reste un tenant (clé tenant_schema) ; on lui ajoute un nom d'affichage, un annuaire senior, et on rend la création d'un nouveau client explicite (fin du get-or-create silencieux qui fabriquait des doublons par typo) |
| Réf. | ADR-0005 — Tenant = groupe ; ADR-0006 — Personas staff cross-tenant ; ADR-0007 — Vues par persona & instigation |
🎯 À retenir
Un client = un tenant (tenant_schema, clé immuable) ; il n'y a pas d'entité Client.
On garde ce modèle minimal mais on comble trois manques repérés en QA senior :
(1) un nom lisible (Tenant.nom, raison sociale) en plus de l'identifiant technique ;
(2) un annuaire clients côté senior (nom · tenant · e-mails de contact · nb de
demandes), dérivé des données existantes ; (3) l'instigation n'invente plus un
client : une typo du tenant donnait un client fantôme sans projet ; désormais on
choisit un client existant, et créer un nouveau est un acte confirmé.
Contexte¶
Repéré en QA (persona senior). Le senior dispose d'une table « Équipe du cabinet »
(staff : nom · e-mail · persona) mais d'aucune vue équivalente pour les clients. Pire :
le formulaire « Nouvelle demande pour un client » (instigation, ADR-0007) prenait un champ
tenant libre, et le backend faisait un get-or-create — un tenant inconnu était créé
en silence. Une faute de frappe (client-tst au lieu de client-test) fabriquait donc un
client en double, sans projet, indistinguable à l'œil de l'original.
Cause de fond : un client est modélisé de façon minimale — Tenant.tenant_schema
(clé unique), sans raison sociale ni e-mail attaché. Les e-mails clients ne vivent que
figés dans chaque demande (Demande.created_by_email, ADR-0013). On ne pouvait donc
ni lister les clients, ni les retrouver, ni se prémunir des doublons.
Décision¶
- D-CLI-1 — Un client = un tenant. On garde le modèle minimal : la clé d'identité est
tenant_schema(string unique, immuable). Pas d'entité Client séparée. - D-CLI-2 — Nom d'affichage.
Tenant.nom(raison sociale, optionnel) est ajouté comme label —tenant_schemareste la clé. NULL = clients d'avant (on affiche alors le tenant). Colonne déclarée dans_COLONNES_AJOUTEES(sinon 500 prod). - D-CLI-3 — Instigation explicite (anti-doublon). Le formulaire propose en premier de
choisir un client existant (liste). Créer un nouveau client est un acte
confirmé :
pour-compteexigeconfirmer_nouveau=true(+client_nom) pour un tenant inconnu, sinon 409 (« choisissez un client existant, ou confirmez la création »). Fin du get-or-create silencieux. - D-CLI-4 — Annuaire clients (senior).
GET /api/clients(réservéactuaire_senior, commeGET /api/equipe) : par tenant → nom, e-mails de contact (distincts, dérivés decreated_by_email), nb de demandes, dernière activité. Données dérivées — aucune table nouvelle hormisTenant.nom. Affiché sous le Cabinet, jumeau d'« Équipe du cabinet ».
Conséquences¶
- Positif. Le senior voit ses clients et les choisit au lieu de retaper un identifiant : le doublon par typo disparaît. Le nom rend l'annuaire lisible sans casser la clé technique. Coût modèle minimal (une colonne).
- Limite assumée. Un client fraîchement instigué (avant toute soumission client) peut
n'avoir aucun e-mail de contact (l'instigation ne connaît pas l'e-mail) — l'annuaire
l'affiche alors sans contact. Et
tenant_schemareste non renommable (c'est la clé) ; seul lenomse corrige.
Déclencheur de réexamen (pré-enregistré) — le nettoyage reste ouvert¶
- Archivage d'un client VIDE — livré (slice 2).
POST /api/clients/{tenant}/archiver(réversible, senior, refusé si le client porte des demandes) masque le doublon par typo de l'annuaire actif et du sélecteur, sans rien perdre. - Fusion d'un client portant des demandes — OUVERTE. Merger = déplacer les demandes (et leurs dossiers/justifications/journal) d'un tenant vers un autre en préservant l'audit immuable (ADR-0008) — non trivial, et rare depuis la prévention. À traiter au cas par cas / one-off (script journalisé) plutôt qu'en fonctionnalité, sauf si le volume le justifie.
- Raison sociale comme donnée Quantis. Si l'identité client doit venir de l'IdP
(YODICORE/SSO) plutôt que d'une saisie cabinet, re-statuer (le
nomdeviendrait un miroir de la fiche Quantis).