---
suivi: 1526
date: 2026-09-11
sujet: Arrêt — bouton Restreindre encore effectif via factures.lecture
chantier: factures / visibilite
type: diagnostic
statut: poussé
hash: f069cd93
fichiers:
  - docs/suivi/SUIVI_1526_masquer_bouton_restreindre.md
---

# SUIVI #1526 — ARRÊT : ne pas masquer le bouton « Restreindre l’accès »

## PROMPT ENVOYÉ

Masquer le bouton « Restreindre l’accès à cette facture » (fiche + saisie
dépôt). Rien d’autre : garder colonne, routes, contrôleur, FactureVisibility,
factures déjà marquées. Avant de masquer : vérifier que l’indicateur n’est
plus lu avec un effet réel sur la visibilité. Si une facture restreinte est
invisible à quelqu’un qui la verrait sinon → signaler et NE PAS masquer.

## BASE GIT

HEAD = `origin/main` = `4b8e6379` (chore hash fiche #1525).
Aucun rebase. Aucun fichier applicatif modifié.

## FICHES LUES

- **#1517 LEÇON** — cran enseigne retiré de `applyScope` / `canView` /
  `canViewFacture`. `applyNonVerrouilleConstraint` retiré avec ce cran.
  `FactureVerrou` **conservé** pour poser/retirer **et** pour
  `userCanViewFacturePage` (lecture + allowlist). Impact : c’est
  exactement le chemin encore actif.
- **#1410 LEÇON** — le verrou ne couvre pas le panel programme
  (`FactureController::indexProps`), marché, CIE, foncier. La porte
  `factures.lecture` sur la fiche a été posée pour que la permission
  catalogue ne contourne pas un verrou.
- **#1412** — cadenas sur `Factures/Show.vue`, routes dépôt inchangées.

## VÉRIFICATION (effet réel encore présent)

### Ce qui ne lit plus le verrou (après #1517)

`FactureVisibility::applyScope` L153-164 : socle → no-op ; sinon
`user_id` OU valideur désigné. **Aucun test `verrouille`.**

`canView` L98-113 et `canViewFacture` L115-148 : socle ∪ déposant ∪
valideur. **Aucun test `verrouille`.**

Conséquence boîte de dépôt / listes FV : un collègue d’enseigne hors
déposant/valideur/socle ne voit **déjà plus** les factures d’autrui,
verrouillées ou non. Sur **ce** périmètre le bouton ne change plus
la liste.

### Ce qui lit encore le verrou (effet fiche / PDF)

`FactureVisibility::userCanViewFacturePage` L64-79 :

1. Si l’utilisateur a la permission catalogue `factures.lecture` :
   - facture **non** verrouillée → `return true` (voit la fiche, même
     s’il n’est ni déposant ni valideur ni socle) ;
   - facture **verrouillée** et hors allowlist → `return false`.
2. Sinon → `canViewFacture` (déposant ∪ valideur ∪ socle).

Allowlist `FactureVerrou::userPassesAllowlistForFacture` L84-104 =
socle ∪ déposant ∪ valideur — **le même ensemble** que `canView`.

La différence se joue uniquement pour un titulaire de
`factures.lecture` **hors** cet ensemble.

Porte d’accès réelle : `FactureController::abortUnlessCanViewFacture`
L622-630 appelle `userCanViewFacturePage`. Consommateurs :
`show` L466-469, `showFiche` L474-477, `viewPdf` L1525-1528
(et d’autres actions fiche : virement, statut Sage, règlement).

### Qui a `factures.lecture` sans être socle

Runtime `PermissionService` L56-60 : groupes M365 → table
`feature_permissions` (pas le config à chaud).

Mapping par défaut `config/feature_permissions.php` :

- `GS_ENVOL` L270-271 : `'*.lecture'` → inclut `factures.lecture`.
  **Pas** dans `SOCLE_TOTAL_GROUPS` (L26-30).
- `GS_HECTARE` L299-303 : foncier seulement → **pas** `factures.lecture`.
- Socle (`GS_ADMIN_ERP`, `GS_DSI`, `GS_DIRECTION_GENERALE`,
  `GS_COMPTABILITE`) : allowlist toujours vraie → le verrou ne les
  exclut pas.

`FeaturesPermissionsSeeder` résout `*.lecture` via
`PermissionPatternResolver` (`*.lecture` = clés finissant par
`.lecture`, donc `factures.lecture`) puis `firstOrCreate` le lien
groupe ↔ permission.

Donc un collègue **GS_ENVOL** (ni déposant, ni valideur, ni socle) :

| Surface | Facture non restreinte | Facture restreinte |
|---|---|---|
| Boîte dépôt (`applyScope`) | invisible | invisible |
| Panel factures programme (`indexProps` L99-114, **sans** FV) | ligne visible | ligne visible |
| Fiche / PDF (`userCanViewFacturePage`) | **ouverte** | **403** |

C’est le critère d’arrêt : une facture restreinte est aujourd’hui
invisible (fiche + PDF) à quelqu’un qui la verrait sinon.

`FactureGlobaleController` applique `applyScope` : la liste globale
ne montre pas les factures d’autrui à GS_ENVOL. L’effet restant est
la **fiche** (URL directe, clic depuis le panel programme, PDF).

## DÉCISION

**Ne pas masquer le bouton.** Masquer laisserait le marquage en base
sans moyen de l’ôter, tout en continuant à 403 les fiches GS_ENVOL
sur les factures déjà restreintes — et empêcherait d’en restreindre
de nouvelles alors que le cran fiche fonctionne encore.

Décider des factures déjà marquées (laisser / lever en masse / ne
restreindre plus que le socle) = **autre lot**.

Aucun Vue, aucun PHP applicatif, aucune migration, aucun test
restriction modifié, aucun rebuild Vite.

## SQL (à exécuter en prod — pas d’accès MySQL ici)

Source de vérité : `factures.verrouille` dès qu’une facture existe ;
miroir `depot_factures.verrouille` avant création (#1410).

```
SELECT
  (SELECT COUNT(*) FROM factures WHERE verrouille = 1)
    AS factures_restreintes,
  (SELECT COUNT(*) FROM depot_factures
    WHERE facture_id IS NULL AND verrouille = 1)
    AS depots_restreints_sans_facture,
  (SELECT COUNT(*) FROM depot_factures df
    INNER JOIN factures f ON f.id = df.facture_id
    WHERE f.verrouille = 1)
    AS depots_lies_a_facture_restreinte;
```

## DÉPLOIEMENT-TEST

```
git pull origin main
```

Pas de migrate. Pas de `optimize:clear` nécessaire (doc seule).
Pas de `journal:sync`. Pas de npm.

## LEÇON

Après #1517 le verrou n’est plus un cran de **liste** FV. Il reste un
cran de **fiche** : `factures.lecture` (GS_ENVOL via `*.lecture`) ouvre
toutes les fiches non verrouillées, et `userCanViewFacturePage` refuse
les verrouillées hors allowlist. Ne pas traiter « plus visible par
enseigne dans applyScope » comme « le bouton ne fait plus rien ».
Le panel programme (`indexProps`) n’applique toujours pas FV (#1410) :
la ligne reste listée, la fiche 403 si verrouillée.
