Aller au contenu

Story CP-011 : Changement de Tenant Backend + Interface Sélecteur de Tenant

Module : control-plane Slice : Slice 11 from architecture Brand context : Papillon HR Suite Priority : 11 Depends on : CP-003 Estimated complexity : M


Objectif

Permettre le flux de travail du cabinet comptable (comptable externe) : un utilisateur appartenant à plusieurs tenants peut basculer entre eux sans se réauthentifier. Ce slice ajoute le composant UI de sélection de tenant, le mécanisme d'échange de token et le flux de changement principal. Les miniatures du tableau de bord et l'optimisation des performances sont dans CP-028.


Périmètre Backend

Entités & Records

Aucune nouvelle entité — utilise UserTenantMembership de CP-003.

DTOs (extension de CP-003) :

public record SwitchTenantRequest(UUID targetTenantId) {}

public record SwitchTenantResponse(
    String accessToken, String refreshToken,
    UUID tenantId, String tenantName
) {}

Couche Service

TenantSwitchService.java : - switchTenant(UUID userId, UUID targetTenantId) → SwitchTenantResponse : 1. Valider que l'utilisateur a une adhésion active dans le tenant cible 2. Demander un nouveau JWT à Keycloak avec le claim tenant_id cible 3. Invalider l'ancien JWT (si Keycloak supporte la révocation de token) 4. Retourner les nouveaux tokens access + refresh 5. Le changement se complète en < 2 secondes

Endpoints API

Propriété de cette story (CP-011) :

Méthode Chemin Corps de requête Réponse Auth
GET /api/v1/auth/my-tenants MyTenantsResponse Tout utilisateur authentifié
POST /api/v1/auth/switch-tenant SwitchTenantRequest SwitchTenantResponse Tout utilisateur authentifié

Considérations multi-tenant

  • L'échange de token remplace un contexte tenant par un autre
  • Tous les caches en mémoire (permissions, statut des modules) doivent être invalidés au changement
  • Les données IndexedDB de l'ancien tenant doivent être effacées au changement
  • Les données du nouveau tenant sont préchargées après le changement

Périmètre Frontend

Pages & Routes

  • Pas de page dédiée. Le sélecteur de tenant est accessible depuis :
  • Le menu avatar utilisateur dans la barre supérieure → "Mes entreprises"

Composants

  • TenantSwitcher.tsx — Bottom sheet plein écran (mobile) / dropdown (bureau)
  • TenantSwitcherCard.tsx — Carte tenant avec logo, nom, rôle, badge courant
  • TenantSwitchOverlay.tsx — Chargement plein écran pendant le changement : "Chargement de {name}…"

États UI (TOUS REQUIS)

État Comportement Texte en Français
Loading Spinner pendant le chargement de la liste
Tenant unique Sélecteur non affiché (uniquement pour les utilisateurs multi-tenant)
Multi-tenant Liste des tenants avec le tenant courant mis en évidence "Mes entreprises"
Changement en cours Overlay plein écran avec indicateur pulsé "Chargement de {tenantName}…"
Erreur de changement Toast "Impossible de changer d'entreprise. Veuillez réessayer."
Succès Redirection vers le tableau de bord du nouveau tenant

Comportement responsive

  • 360px (mobile) : Bottom sheet plein écran affichant la liste des tenants. Chaque carte : emplacement logo, nom d'entreprise, rôle de l'utilisateur, coche sur le tenant courant.
  • 1024px+ (bureau) : Dropdown depuis le menu avatar utilisateur. Même contenu, disposition compacte.

Interactions

  1. L'utilisateur appuie sur l'avatar → le menu affiche l'option "Mes entreprises"
  2. Appui → la liste des tenants apparaît
  3. Appui sur un tenant différent → overlay de chargement plein écran : "Chargement de {name}…"
  4. L'échange de token s'effectue
  5. Toutes les données en cache sont effacées → nouvelles données tenant préchargées → redirection vers le Tableau de bord
  6. Le contexte du tenant précédent est entièrement effacé de la mémoire

Micro-textes français :

Clé Texte Français
tenant_switcher.title "Mes entreprises"
tenant_switcher.current "Entreprise active"
tenant_switcher.switching "Chargement de {name}…"
tenant_switcher.hint "Tapez sur une entreprise pour changer de contexte."

Critères d'acceptation

AC-089: Tenant switch with token exchange
Given I belong to tenant A (current) and tenant B
When I select tenant B from the switcher
Then a new JWT is issued for tenant B
And I land on tenant B's dashboard
And all tenant A data is cleared from cache/IndexedDB

AC-090: Switcher only for multi-tenant users
Given I belong to only 1 tenant
When I open the user menu
Then "Mes entreprises" option does not appear

AC-091: Switcher shows current tenant
Given I belong to 3 tenants and am currently in "Acme SARL"
When I open the tenant switcher
Then "Acme SARL" has a "✓ Entreprise active" badge

Règles OHADA & Réglementaires

  • Le cabinet comptable est une structure commerciale courante dans les zones OHADA — les comptables externes gèrent plusieurs entreprises clientes. La fonctionnalité de changement de tenant multi-tenant sert directement ce profil utilisateur.
  • L'échange de token ne doit pas fuiter de données entre les tenants — tous les caches sont effacés au changement.

Standards & Conventions

  • docs/standards/react-typescript-guidelines.md — §Bottom sheet, §États overlay/chargement, §Gestion du cache
  • docs/standards/offline-sync-guidelines.md — §Effacement IndexedDB lors du changement de contexte

Exigences de test

Tests unitaires

  • TenantSwitchService : changement valide, adhésion invalide → rejeté
  • TenantSwitcher : s'affiche uniquement pour les utilisateurs multi-tenant
  • TenantSwitcherCard : rendu du badge de tenant courant

Tests d'intégration

  • Échange de token : le nouveau JWT contient le tenant_id correct
  • Changement refusé : l'utilisateur n'est pas membre du tenant cible → 403
  • Cache/IndexedDB effacé après le changement

Ce que le QA validera

  • Le sélecteur s'affiche uniquement pour les utilisateurs multi-tenant
  • Le tenant courant est mis en évidence
  • L'overlay de changement s'affiche et se ferme correctement
  • Toutes les données reflètent le nouveau tenant après le changement (liste d'employés, paramètres, etc.)

Hors périmètre

  • Miniatures de tableau de bord tenant — CP-028
  • Affichage du rôle par tenant dans le sélecteur — CP-028
  • Optimisation des performances de changement (< 2s) — CP-028
  • Configuration du tenant principal (changer quel tenant est le "home") — V2
  • Recherche dans la liste de tenants (pour les utilisateurs avec de nombreux tenants) — V2
  • Reporting inter-tenant (vue agrégée entre tenants) — V2
  • Changement de tenant hors ligne — Impossible (nécessite un échange de token)

Définition de Done

  • [ ] Tous les endpoints backend implémentés et retournant les réponses correctes
  • [ ] Tous les composants frontend s'affichent correctement à 360px, 768px, 1024px
  • [ ] Les 6 états UI implémentés (loading, tenant unique, multi-tenant, changement en cours, erreur, succès)
  • [ ] Les micro-textes français correspondent exactement à la spec design
  • [ ] Tous les critères d'acceptation passent manuellement
  • [ ] Tests unitaires écrits et passants
  • [ ] Tests d'intégration écrits et passants
  • [ ] Isolation multi-tenant vérifiée (aucune fuite de données inter-tenant)
  • [ ] Pas de any TypeScript — tous les types explicites
  • [ ] Pas de types Java bruts — tous les génériques explicites
  • [ ] Les réponses API respectent le format d'enveloppe standard
  • [ ] Entrées d'audit créées pour les événements de changement de tenant