---
suivi: 1059
date: 2026-07-29
sujet: Filtre enseigne HECTARE / ENVOL / Tous sur la liste Programmes
chantier: programmes
type: feature
statut: poussé
hash: b665105c
fichiers:
  - app/Http/Controllers/ProgrammeController.php
  - app/Models/User.php
  - resources/js/Pages/Programmes/Index.vue
  - database/data/journal_mises_a_jour_post_2026_06_12.php
  - public/build/
---

## PROMPT ENVOYÉ

SUIVI #1059 — filtre d'affichage enseigne (HECTARE / ENVOL / Tous) sur la liste Programmes,
à côté du filtre Actifs / Inactifs. Défaut selon GS_ENVOL / GS_HECTARE (appartenance prime
sur DG / DSI / ADMIN_ERP). Filtre purement visuel : ne pas toucher ProgrammeAccessService
ni aucune policy. Mémorisation conditionnelle : réutiliser users.preferences (#1058) si
disponible sur main, sinon pas de mémorisation.

## SYNTHÈSE

### Source enseigne
Table `enseignes` (codes `ENVOL`, `HECTARE`, `GEMME`) ; FK `programmes.enseigne_id` NOT NULL.
Filtre SQL via `whereHas('enseigne', code = ENVOL|HECTARE)` — **jamais** sur préfixe
`code_envol`. GEMME hors périmètre du sélecteur (pas de 3e bouton) ; programmes GEMME
n'apparaîtraient qu'avec « Tous » s'il en existait.

### Groupes M365
Défaut calculé via `$user->m365GroupNames()` (liste dynamique runtime). Pas
`hasM365Group()` : ce helper short-circuit pour `isAdminERP()` et aurait forcé « Tous »
pour tout admin, contredisant la décision Robin. Aucune exception DG / DSI / ADMIN_ERP.

### Mémorisation — cas (a)
#1058 déjà sur main (`users.preferences` + `User::preference` / `setPreference`).
Réutilisation avec clé `programmes_filtre_enseigne`. Query `enseigne` présente → applique
+ persiste ; absente → préférence stockée si valide, sinon défaut M365. Pas de nouvelle
migration.

### Droits
Aucun changement : `ProgrammeAccessService`, policies, détail programme intacts. Un
utilisateur GS_ENVOL peut basculer sur HECTARE / Tous et ouvrir ces fiches.

### Comptage prod
MySQL local DOWN chez Cursor. Observation écran Robin (actifs) : ~10 ENVOL + 1 HECTARE
(EQUILIBRE / HEC34-001). À confirmer en prod par SELECT groupé enseigne × statut (y compris
inactifs et éventuels GEMME).

### Front
Boutons segmentés « Enseigne : » à côté d'« Affichage : » ; les deux filtres se combinent
en query `statut` + `enseigne`. Filtrage côté back (déjà le pattern du filtre statut).

## DÉPLOIEMENT-TEST

```bash
git pull origin main
php artisan journal:sync
php artisan view:clear
find app/ -name "*.php" -exec touch {} +
```

Pas de migration (colonne `users.preferences` déjà créée par #1058 — s'assurer que
`migrate --force` #1058 a bien tourné). Pas de route:clear. Pas de npm/composer/optimize
sur OVH.

## LEÇON
Distinguer un défaut d'AFFICHAGE d'une restriction de DROITS : l'appartenance à un groupe
choisit ce qu'on montre en premier, sans jamais retirer un accès. Un filtre « enseigne »
qui passerait par ProgrammeAccessService transformerait une préférence visuelle en
censure — ici le défaut guide, le basculement manuel reste libre.
