---
suivi: 1475
date: 2026-09-07
sujet: Diagnostic — GS_DIRECTION_GENERALE = accès total module juridique
chantier: juridique / droits
type: diagnostic
statut: poussé
hash: 670832ed
fichiers:
  - docs/suivi/SUIVI_1475_diag_droits_dg_juridique.md
---

# SUIVI #1475 — PHASE 1 DIAGNOSTIC — droits DG = JURIDIQUE

## PROMPT ENVOYÉ

Diagnostic uniquement : cartographier tous les points de contrôle du module
juridique avant d’aligner GS_DIRECTION_GENERALE sur GS_JURIDIQUE. Aucun code
applicatif, aucune migration, aucun .vue, aucun rebuild.

## BASE GIT

```
git log --oneline -1 origin/main
57901b98 chore(suivi): hash fiche #1474
```

(Attendu prompt : `0d217701` #1473 — main a avancé via #1474 UI pièces
demandées. Travail sur tip à jour ; fiches #1472/#1474 lues.)

## FICHES LUES

- **#1468** — relevé GS_JURIDIQUE (point de départ) ; KPI bandeau déjà
  visible DG via `m365GroupNames()` brut ∩ GROUPES_BANDEAU ; périmètre =
  ListQuery.
- **#1472** — `DemandeJuridiquePieceDemandeeResolver` GROUPES_JURISTE
  JURIDIQUE∪DSI∪ADMIN_ERP via `m365GroupNames()` brut ; FormRequests store /
  echeance / destinataire.
- **#1474** — UI consomme les flags Presenter ; n’ajoute pas de nouveau
  contrôle métier (drapeaux serveur inchangés hors `echeance_depassee`).
- **#1465 / #1464** — modèle permission `feature.*` + piège middleware amont
  (`foncier.lecture` bloquait avant le contrôleur).
- **#1451** — suiveurs : `m365GroupNames()` brut.
- **#1442** — ne jamais réassigner `$groups` / props avec `m365GroupNames()`.

---

## A. CARTOGRAPHIE EXHAUSTIVE

### A1 — Tableau des points de contrôle

Légende nature : **(i)** liste groupes en dur · **(ii)** `features_permissions`
· **(iii)** participation (demandeur / suiveur / juriste affecté) · **(iv)**
annuaire / notification (pas un droit d’action UI) · **(v)** menu front.

| Fichier:ligne | Action protégée | Groupes / condition actuelle | DG présent | Nature |
|---|---|---|---|---|
| `routes/web.php:1318-1361` | Toutes routes demandes (liste, fiche, CRUD champs, PJ, commentaires, pièces demandées) | Auth SSO seulement — **pas** de `m365.groups` ni `feature.permission` | n/a (porte ouverte) | middleware auth |
| `routes/web.php:1363-1401` | Registres contentieux / pré-contentieux / non-conformités | `m365.groups:GS_JURIDIQUE,GS_DSI,GS_ADMIN_ERP` | **non** | (i) middleware |
| `AppShell.vue:638` | Ligne menu « Juridique » | `peutVoirLigneJuridique = true` (tout authentifié) | oui (tous) | (v) |
| `AppShell.vue:616-620` | Sous-liens registres | `hasM365Groupe(GS_JURIDIQUE\|DSI\|ADMIN_ERP)` | **non** | (i)+(v) helper short-circuit |
| `DemandeJuridiqueListQuery.php:28-33` | Liste complète + filtre « Mes dossiers » | `hasM365Group('GS_JURIDIQUE')` → toutes demandes | **non** | (i) helper short-circuit |
| `DemandeJuridiqueListQuery.php:36-41` | Liste hors JURIDIQUE | `demandeur_id` OU suiveur (iii) | n/a | (iii) |
| `DemandeJuridiqueListQuery.php:46-48` | Normalisation filtre | hors JURIDIQUE → force `toutes` (sans bascule) | **non** | (i) |
| `DemandeJuridiqueController.php:123-125` | Prop `est_juriste` (bascule Mes dossiers) | `hasM365Group('GS_JURIDIQUE')` | **non** | (i) |
| `DemandeJuridiqueController.php:144-148` | Props bandeau KPI | `m365GroupNames()` ∩ `{JURIDIQUE, DIRECTION_GENERALE, ADMIN_ERP}` | **oui** | (i) brut |
| `DemandeJuridiqueKpiService.php:20-24,39-40` | Droit voir bandeau | idem GROUPES_BANDEAU | **oui** | (i) brut |
| `DemandeJuridiqueKpiService.php:58` | Données KPI | `ListQuery::forUser` (même filtre page) | oui mais **périmètre partiel** | (i)+(iii) |
| `DemandeJuridiqueParticipantResolver.php:46-50` | Ouvrir une fiche (`peutAcceder`) | JURIDIQUE via `hasM365Group` **OU** demandeur/suiveur | **non** | (i)+(iii) |
| `DemandeJuridiqueParticipantResolver.php:53-60` | Modifier champs / ajouter PJ (`peutModifier`) | JURIDIQUE **OU** (demandeur + statut `a_qualifier`) | **non** | (i)+(iii) |
| `DemandeJuridiqueParticipantResolver.php:67-72` | Changer le demandeur | `m365GroupNames()` ∈ {JURIDIQUE, ADMIN_ERP} — **pas DSI** | **non** | (i) brut |
| `DemandeJuridiqueParticipantResolver.php:30-36` | Pool mentions (participants) | tous users `m365_groups` JSON contient JURIDIQUE | **non** (DG non inclus comme « juriste ») | (i) requête |
| `DemandeJuridiquePresenter.php:54,147` | `$estJuriste` → `peut_affecter` | `hasM365Group('GS_JURIDIQUE')` | **non** | (i) |
| `DemandeJuridiquePresenter.php:157-158` | Options sélecteur juriste | idem `hasM365Group` | **non** | (i) |
| `DemandeJuridiquePresenter.php:135` (#1474) | `peut_demander_piece` | `PieceDemandeeResolver::peutCreer` | **non** | (i) brut via resolver |
| `DemandeJuridiquePresenter.php:141-143` | `peut_gerer_suiveurs` / `peut_changer_statut` | SuiveurResolver / StatutResolver | **non** | (i) |
| `DemandeJuridiquePresenter.php:161` | `peut_supprimer` demande | ADMIN_ERP brut seul | **non** | (i) brut |
| `DemandeJuridiquePresenter.php:447-450` | Supprimer une PJ | `peutModifier` + (JURIDIQUE **ou** visibilité demandeur) | **non** (sauf si `peutModifier` élargi) | (i)+(iii) |
| `DemandeJuridiqueStatutResolver.php:14,27-29` | Changer statut | `hasAnyM365Group(JURIDIQUE,DSI,ADMIN_ERP)` | **non** | (i) helper short-circuit |
| `UpdateDemandeJuridiqueStatutRequest.php:11-15` | FormRequest statut | délègue StatutResolver | **non** | FormRequest |
| `DemandeJuridiqueSuiveurResolver.php:13,15-25` | Gérer suiveurs | `m365GroupNames()` ∈ {JURIDIQUE,DSI,ADMIN_ERP} | **non** | (i) brut |
| `StoreSuiveurDemandeJuridiqueRequest.php:11-15` | FormRequest ajout suiveur | SuiveurResolver | **non** | FormRequest |
| `DemandeJuridiqueDemandeurResolver.php:14,16-18` | Choisir demandeur au dépôt | `hasAnyM365Group(JURIDIQUE,DSI,ADMIN_ERP)` | **non** | (i) helper |
| `AffecterJuristeDemandeJuridiqueRequest.php:15` | Affecter juriste | `hasM365Group('GS_JURIDIQUE')` seul (pas DSI) | **non** | (i) FormRequest |
| `JuristeResolver.php:15-16` | Qui est **choisissable** comme juriste | `whereJsonContains(m365_groups, GS_JURIDIQUE)` | **non** (voulu) | (iv) annuaire |
| `DemandeJuridiquePieceDemandeeResolver.php:19,21-31` (#1472) | Créer / modifier / supprimer pièce demandée | `m365GroupNames()` ∈ {JURIDIQUE,DSI,ADMIN_ERP} | **non** | (i) brut |
| `…PieceDemandeeResolver.php:52-58` | Valider (cocher) | juriste-gestion **OU** destinataire ligne (iii) | **non** (sauf destinataire) | (i)+(iii) |
| `Store/Update…PieceDemandee*Request.php` | FormRequests #1472 | délèguent Resolver | **non** | FormRequest |
| `DemandeJuridiquePieceDemandeeController.php` | Endpoints pièces demandées | `peutAcceder` + Resolver | **non** | Controller |
| `DemandeJuridiqueSuppressionResolver.php:12-14` | Soft-delete demande | ADMIN_ERP brut **seul** | **non** | (i) brut |
| `JuridiqueRegistreParticipantResolver.php:15,17-19` | Accès registre (contrôleur) | `hasAnyM365Group(JURIDIQUE,DSI,ADMIN_ERP)` | **non** | (i) helper |
| `JuridiqueRegistreSuppressionResolver.php` | Suppr. ligne registre | ADMIN_ERP brut | **non** | (i) brut |
| `StoreJuridiqueRegistreRequest.php:12` | authorize registre | `return true` (garde = middleware route) | **non** | middleware amont |
| `DemandeJuridiqueProfilDemandeur.php:30-32` | Libellé profil demandeur | map inclut déjà DIRECTION_GENERALE | oui (libellé) | (iv) |
| `NotifierDepotDemandeJuridique.php:69-71` | Destinataires Teams dépôt → juristes | `m365GroupNames()` contient JURIDIQUE | **non** | (iv) notif |
| `AlerteService.php:115` | Cloche ERP nouvelle demande | `groupe_cible => 'GS_JURIDIQUE'` littéral | **non** | (iv) alerte |
| `HandleInertiaRequests.php:225-226` | Compteur validations factures (hors fiche juridique) | `hasM365Group` JURIDIQUE / DIRECTION_GENERALE | DG déjà pour NV2 | hors module fiche |
| `ValidationController.php:141-142` | Idem rôles validations factures | idem | DG = `est_pdg` | hors module fiche |

**#1474** : aucun nouveau point de contrôle serveur ; le front ne fait que
consommer `peut_valider` / `peut_demander_piece` / `peut_modifier` /
`peut_supprimer` déjà calculés.

### A2 — Trois familles

| Famille | Exemples module juridique | Correction typique |
|---|---|---|
| **(i) Listes en dur** | ListQuery, StatutResolver, SuiveurResolver, PieceDemandeeResolver, middleware registres, AffecterJuriste | Ajouter DG (ou constante partagée) **en lecture `m365GroupNames()` brut** |
| **(ii) `feature.*`** | **Aucune** permission `juridique.*` trouvée dans `database/` ni routes | Créer serait optionnel (voie #1465) — pas de garde amont aujourd’hui sur les demandes |
| **(iii) Participation** | `peutAcceder` hors JURIDIQUE ; `peutModifier` demandeur `a_qualifier` ; `peutValider` destinataire ligne | **Ne pas remplacer** : élargir le court-circuit « service juridique » en amont, laisser la branche participant intacte |

Piège (iii) : si on ajoute DG seulement dans ListQuery sans `peutAcceder`, la
liste montrerait des dossiers ouvrant en 403. Si on ajoute dans `peutAcceder`
sans ListQuery, la fiche directe marcherait mais la liste resterait partielle.
Les deux doivent bouger ensemble.

### A3 — Permission `juridique.*` et garde de route

**Constat code :** aucune entrée `juridique.` dans `database/` (grep) ; routes
demandes = `auth` seul (`routes/web.php:186` groupe auth, `1318` prefix
juridique sans `feature.permission`).

**Différence #1465 :** avis d’imposition avait un middleware `foncier.lecture`
**avant** le contrôleur. Ici, **pas de garde équivalente** sur les demandes :
un membre DG atteint déjà les URLs ; ce qui bloque, ce sont les resolvers /
ListQuery / FormRequests.

Registres : seule sous-branche avec middleware `m365.groups` — DG **ne
franchit pas** cette porte aujourd’hui (`routes/web.php:1363`).

#### Requête permissions (Robin)

```sql
SELECT fp.id, fp.feature_key, fp.label, fpg.groupe_m365
FROM features_permissions fp
LEFT JOIN feature_permission_groups fpg ON fpg.feature_permission_id = fp.id
WHERE fp.feature_key LIKE 'juridique.%'
   OR fp.feature_key LIKE '%juridique%'
ORDER BY fp.feature_key, fpg.groupe_m365;
```

Attendu probable : **0 ligne** `juridique.*` (à confirmer).

---

## B. CE QUE VOIT GS_DIRECTION_GENERALE AUJOURD’HUI

### B1 — Parcours réel (code, sans chiffre)

1. **Menu** : voit « Juridique » (toujours) + Déposer / Demandes ; **pas** les
   registres (`AppShell.vue:616-620`).
2. **Liste** `/juridique/mes-demandes` : uniquement demandes où il est
   **demandeur ou suiveur** (`ListQuery.php:36-41`). Pas de bascule « Mes
   dossiers » juriste (`Controller.php:125` + `normalizeFiltre`).
3. **Bandeau KPI** : **affiché** (`GROUPES_BANDEAU` contient DG) mais calculé
   sur ce périmètre partiel → compteurs **trompeurs** (confirmé #1468 + code
   `KpiService::calculer(ListQuery::forUser)`).
4. **Fiche** hors participation : **403** (`peutAcceder` L46-50).
5. **Statut / affectation juriste / suiveurs / pièces demandées (créer) /
   registres / supprimer demande** : refusés (FormRequests / resolvers /
   middleware).
6. **Commenter / cocher une pièce demandée** : seulement s’il est participant
   (accès fiche) et, pour la coche, destinataire de la ligne.

### B2 — ListQuery + KPI — VERDICT EXPLICITE

**Confirmé :** pour un membre DG hors GS_JURIDIQUE, ListQuery = dossiers
personnels seulement ; le bandeau KPI s’affiche quand même → **KPI partiels,
pire qu’aucun bandeau**. À corriger en même temps que l’élargissement
ListQuery (ou masquer le bandeau tant que ListQuery n’est pas total — arbitrage).

### B3 — Confidentialité / arbitrage

- Pièces jointes : `ChronologieBuilder::peutVoirPiece` (L95-103) = **accès
  fiche** ; plus de filtre « Interne vs Demandeur » à la lecture (commentaire
  L91-93 : aligné liste/téléchargement). Le flag `visibilite` sert encore à
  **qui peut supprimer** une PJ (`Presenter.php:447-450`).
- Historique / commentaires : visibles dès `peutAcceder`.
- **Arbitrage Robin :** certaines demandes (contentieux sensibles) ne
  devraient-elles **pas** être ouvertes à toute la DG ? Le code actuel n’a
  **pas** de classification de confidentialité par demande — élargir DG =
  voir **toutes** les demandes si on aligne ListQuery + `peutAcceder`.

---

## C. IMPACT DE L’ÉLARGISSEMENT

### C1 — Effets de bord si DG ajouté « partout où JURIDIQUE »

| Zone | Effet | Probablement voulu ? |
|---|---|---|
| ListQuery + peutAcceder + peutModifier | Voir / éditer toutes les demandes | **oui** (demande Robin) |
| Statut / suiveurs / pièces demandées / choix demandeur | Actions métier service | **oui** (à confirmer) |
| Registres middleware + menu | Accès contentieux etc. | **à arbitrer** |
| `JuristeResolver` (annuaire) | DG devient **choisissable** comme juriste responsable | **probablement NON** |
| `participants()` mentions | DG listés comme « juristes » pour @mention | **à arbitrer** |
| `AffecterJuriste` authorize | DG peut affecter | **oui** sans élargir le pool |
| `AlerteService` `groupe_cible` | 3 DG reçoivent toutes les cloches « nouvelle demande » | **probablement NON** |
| `NotifierDepotDemandeJuridique` | Teams à toute la DG à chaque dépôt | **probablement NON** |
| Suppression demande (ADMIN seul) | Si ajouté « partout » → DG soft-delete | **probablement NON** |
| Suppression registre (ADMIN) | idem | **probablement NON** |

### C2 — Actions où l’élargissement est INDÉSIRABLE (candidats)

1. **Annuaire juriste** (`JuristeResolver.php:16`) — ne pas y mettre DG.
2. **Alertes** (`AlerteService.php:115`) — garder `GS_JURIDIQUE`.
3. **Notif Teams dépôt** (`NotifierDepotDemandeJuridique.php:71`) — garder JURIDIQUE.
4. **Suppression demande / ligne registre** — aujourd’hui ADMIN_ERP seul ;
   hors « tout modifier comme JURIDIQUE » (les juristes ne peuvent déjà pas
   supprimer une demande).
5. **HandleInertiaRequests / ValidationController** — hors périmètre fiche ;
   DG a déjà un rôle NV2 distinct (`est_pdg`).

### C3 — Aucun groupe ne doit perdre d’accès

Toute correction doit être **additive** (union DG). Préserver DSI et ADMIN_ERP
là où ils figurent déjà (Statut, Suiveurs, Pièces demandées, Registres).
Ne pas retirer JURIDIQUE. `peutModifierDemandeur` n’inclut pas DSI aujourd’hui —
ne pas « corriger » DSI dans ce lot sauf arbitrage séparé.

### C4 — GS_DSI ≠ GS_ADMIN_ERP

Confirmé : listés séparément partout (ex. `GROUPES_JURISTE` #1472,
middleware registres). Ne pas fusionner.

---

## D. PIÈGE MAJEUR

### D1 — Helpers short-circuit ADMIN

`User::hasM365Group` / `hasAnyM365Group` (`User.php:134-152`) : si
`isAdminERP()` → **true**. `Gate::before` (`AppServiceProvider.php:69-75`) :
toutes abilities true pour ADMIN_ERP. Front `hasM365Groupe`
(`useAuthM365.js:19-20`) : court-circuit `auth.is_admin_erp`.

**Usages fautifs (module juridique) pour tests de refus :**

| Fichier | Helper |
|---|---|
| `DemandeJuridiqueListQuery.php:28,46` | `hasM365Group` |
| `DemandeJuridiqueParticipantResolver.php:46,55` | `hasM365Group` |
| `DemandeJuridiquePresenter.php:54,157,447` | `hasM365Group` |
| `DemandeJuridiqueStatutResolver.php:29` | `hasAnyM365Group` |
| `DemandeJuridiqueDemandeurResolver.php:18` | `hasAnyM365Group` |
| `AffecterJuristeDemandeJuridiqueRequest.php:15` | `hasM365Group` |
| `JuridiqueRegistreParticipantResolver.php:19` | `hasAnyM365Group` |
| `DemandeJuridiqueController.php:125` | `hasM365Group` |
| `AppShell.vue:617-619` | `hasM365Groupe` |
| Middleware registres | `EnsureM365GroupMembership` → `hasAnyM365Group` après short-circuit `isAdminERP` L30-31 |

**Modèles corrects déjà présents :** `PieceDemandeeResolver`,
`SuiveurResolver`, `SuppressionResolver`, `KpiService`,
`peutModifierDemandeur`, `NotifierDepot` (brut).

### D2 — Peuplement — requêtes Robin

```sql
-- Membres GS_DIRECTION_GENERALE
SELECT id, name, email, m365_groups
FROM users
WHERE JSON_CONTAINS(m365_groups, '"GS_DIRECTION_GENERALE"')
ORDER BY name;

-- Contrôle peuplement groupes clés
SELECT g.groupe, COUNT(*) AS n
FROM (
  SELECT 'GS_DIRECTION_GENERALE' AS groupe UNION ALL
  SELECT 'GS_JURIDIQUE' UNION ALL
  SELECT 'GS_ADMIN_ERP' UNION ALL
  SELECT 'GS_DSI'
) g
LEFT JOIN users u ON JSON_CONTAINS(u.m365_groups, CONCAT('"', g.groupe, '"'))
GROUP BY g.groupe;
```

Peuplement annoncé (à confirmer) : DG 3 · JURIDIQUE 2 (Christelle 19, Manon 29)
· ADMIN_ERP 1 (Robin).

### D3 — Comptes de test APRÈS correction

| Rôle | Compte | Attendu |
|---|---|---|
| DG | un des 3 membres GS_DIRECTION_GENERALE (pas Robin) | liste totale, fiche, statut, suiveurs, pièces demandées ; **pas** juriste dans le select ; **pas** cloche/Teams dépôt |
| Juriste | Christelle 19 ou Manon 29 | inchangé (non-régression) |
| Tiers refus | collab GS_COMMERCIAL hors demande | liste personnelle / 403 fiche étrangère |
| Jamais | compte Robin (GS_ADMIN_ERP) | pour un test de **refus** |

### D4 — Propagation M365

Après changement de groupe Graph : délai Graph + cache ACL (~5 min). Un 403
immédiat n’est pas un bug — rappeler dans le lot d’implémentation.

---

## E. REMÉDIATION — PROPOSITION (PAS D’EXÉCUTION)

### E1 / E2 — Stratégie recommandée

**Avis :** introduire une constante / helper unique du type

`DemandeJuridiqueAcces::GROUPES_SERVICE = ['GS_JURIDIQUE','GS_DIRECTION_GENERALE','GS_DSI','GS_ADMIN_ERP']`

avec méthode `estService(User): bool` basée sur **`m365GroupNames()` brut**
(modèle `FactureVisibility` / `PieceDemandeeResolver`), puis remplacer les
`hasM365Group('GS_JURIDIQUE')` / `hasAnyM365Group([...])` des **portes
d’accès métier**.

**Pourquoi pas quinze copies DG à la main :** hétérogénéité actuelle (parfois
sans DSI, parfois `hasAny`, parfois brut) → risque d’oubli. **Pourquoi pas
`feature.juridique.*` tout de suite :** aucune permission existante, pas de
garde middleware demandes, et Robin a refusé l’usine à gaz — la constante
partagée suffit ; une permission adminable peut venir plus tard (#1465) si
besoin de /admin/droits.

**Hors constante (ne pas y coller DG) :**

- `JuristeResolver` (pool affectation)
- `AlerteService::groupe_cible`
- `NotifierDepotDemandeJuridique::appartientGsJuridique`
- `DemandeJuridiqueSuppressionResolver` / `JuridiqueRegistreSuppressionResolver`
- Validations factures (`HandleInertiaRequests` / `ValidationController`)

### E3 — Migration

**Non requise** pour la voie constante. Si voie permission `feature.*` :
migration idempotente `updateOrInsert` (modèle #1465), sans `->after()`,
groupes attachés en `feature_permission_groups`.

### E4 — Découpage lot d’implémentation

| Étape | Contenu | Risque |
|---|---|---|
| **1 — Lecture totale + KPI** | ListQuery + `peutAcceder` + helper brut ; KPI redevient cohérent | Moyen : ouvre toutes les fiches à la DG |
| **2 — Actions métier fiche** | Statut, Suiveurs, PieceDemandee, `peutModifier`, Presenter flags, AffecterJuriste (authorize seulement), DemandeurResolver | Moyen : écriture large |
| **3 — Registres** | Middleware + menu AppShell + RegistreParticipantResolver | Moyen : périmètre contentieux |
| **4 — Nettoyage helpers** | Remplacer `hasM365Group` restants du module par brut (sauf hors-scope) | Faible si tests |
| **Hors scope / arbitrage** | Alertes, Teams dépôt, pool juristes, suppressions ADMIN | — |

Clause d’arrêt : points hétérogènes → **découpage ci-dessus plutôt qu’un
big-bang** ; alertes/notifs/pool juristes hors « partout ».

### E5 — Tests de refus à écrire (comptes non-admin)

1. Tiers GS_COMMERCIAL : ListQuery ne voit pas une demande étrangère ; show 403.
2. Même tiers : POST pièces-demandees / PATCH statut / affecter juriste → 403.
3. DG (après correction) : ListQuery voit demande étrangère ; show 200 ; KPI
   aligné sur total ouvertures visibles.
4. DG : peut changer statut / ajouter suiveur / créer pièce demandée.
5. DG : **absent** de `JuristeResolver::optionsPourSelect`.
6. DG : **ne reçoit pas** alerte `groupe_cible=GS_JURIDIQUE` ni Teams dépôt
   (assertion source / destinataires).
7. Juriste Christelle : non-régression accès + actions.
8. Suiveur destinataire : `peut_valider` sa ligne seulement (#1472/#1474).
9. Source : aucun nouveau `hasAnyM365Group` pour les portes corrigées ; lecture
   `m365GroupNames()`.
10. Jamais de test de refus avec GS_ADMIN_ERP.

---

## ARBITRAGES ROBIN (choix A/B/C — sans recommandation imposée)

### Arb 1 — Périmètre « tout voir / tout modifier »

- **A** Demandes seulement (liste + fiche + actions fiche + pièces demandées)
- **B** A + registres (contentieux / pré-contentieux / non-conformités)
- **C** B + suppressions aujourd’hui réservées ADMIN_ERP

### Arb 2 — Sélecteur juriste responsable

- **A** Pool reste strictement GS_JURIDIQUE (DG peut affecter mais n’est pas
  choisissable)
- **B** DG ajouté au pool choisissable
- **C** Pool = JURIDIQUE ∪ DSI (sans DG)

### Arb 3 — Alertes cloche nouvelle demande

- **A** `groupe_cible` reste `GS_JURIDIQUE` uniquement
- **B** `groupe_cible` = JURIDIQUE ∪ DIRECTION_GENERALE
- **C** Alerte individuelle aux juristes affectables (changement de modèle)

### Arb 4 — Notification Teams dépôt

- **A** Destinataires = membres GS_JURIDIQUE seulement (état actuel)
- **B** Inclure GS_DIRECTION_GENERALE
- **C** Uniquement le juriste déjà affecté (si présent) + fallback JURIDIQUE

### Arb 5 — KPI bandeau avant élargissement ListQuery

- **A** Élargir ListQuery et KPI ensemble (même lot)
- **B** Masquer temporairement le bandeau pour DG tant que ListQuery partiel
- **C** Calculer KPI sur un query « service » distinct de la liste personnelle
  (deux périmètres)

### Arb 6 — Confidentialité

- **A** DG voit toutes les demandes (pas de classification)
- **B** Introduire plus tard un flag confidentialité (hors ce lot)
- **C** DG lecture seule (voir tout, ne rien modifier) — contredit la demande
  initiale « tout modifier »

---

## DÉPLOIEMENT

Aucun (diagnostic). Fiche seule.

## LEÇON

Le bandeau KPI #1468 a déjà mis DG dans la **visibilité UI** sans élargir
ListQuery → KPI partiels. Toute porte « comme JURIDIQUE » doit coupler liste +
`peutAcceder` + actions, tout en **excluant** explicitement annuaire juriste,
alertes et Teams dépôt sauf arbitrage. Préférer `m365GroupNames()` brut +
constante partagée aux helpers `hasM365Group` / `hasAnyM365Group`.
