---
suivi: 1751
date: 2026-09-30
sujet: Visibilité des factures de marché pour le responsable programme
chantier: factures
type: evolution
statut: poussé
hash:
fichiers:
  - app/Support/FactureVisibility.php
  - app/Support/DepotFactureDocumentAccess.php
  - app/Http/Controllers/DocumentFactureController.php
  - tests/Unit/FactureVisibility1751SourceTest.php
  - tests/Feature/FactureVisibility1751Test.php
  - database/data/journal_mises_a_jour_post_2026_06_12.php
  - docs/suivi/SUIVI_1751_visibilite_factures_responsable_programme.md
---

# SUIVI #1751 — Visibilité factures, responsable programme

## PROMPT ENVOYÉ

Une facture liée à un marché est visible par le responsable programme / conducteur de travaux actuel du programme de ce marché, comme un déposant ou un valideur : listes, fiche, PDF, documents, recherche. Visibilité seulement. Facture sans marché : inchangé. Une seule définition.

## FICHES LUES

- #1508 LEÇON : « partout » n’est pas seulement `applyScope`. `userCanViewFacturePage`, les listes et les écrans compta (porte de groupe) sont trois familles. Ce lot n’ajoute le droit que dans `FactureVisibility` (famille lecture).
- #1517 LEÇON : un droit de visibilité est un OR. Ne pas fusionner la recherche charges de portage ni `factures.lecture` ici. Le cran enseigne reste retiré.

## CONSTAT

- Visibilité : `app/Support/FactureVisibility.php`. Socle `hasSocleTotalAccess` ligne 253 (groupes admin, DSI, direction, comptabilité). Déposant : `depot_factures.user_id` lignes 112 et 173. Valideurs : `isDesignatedValidator` ligne 258 et `applyDesignatedValidatorScope` (workflow NV1/NV2 ou saisie). `canView` ligne 102, `canViewFacture` ligne 123, `applyScope` ligne 165.
- Responsables : table `programme_responsables`, colonnes `programme_id`, `role`, `user_id`. Unique (`programme_id`, `role`). Rôle carte « Responsable programme / Conducteur de travaux » = `responsable_programme` (`ProgrammeController` constante `ROLES_RESPONSABLES`, `Edit.vue` ligne 164). Ils servaient déjà à la validation NV1 si `factures.programme_id` est renseigné (`FactureValidationPermissions::canValidateNv1`), pas à la lecture.
- Lien : `factures.marche_id` → `marches.programme_id`. Sans facture : `depot_factures.marche_id`. `DepotFacture::sqlMarcheEffectifId` ligne 423.

## SYNTHÈSE

`estResponsableProgrammeDuMarche` est le seul prédicat. `canView`, `canViewFacture` (facture sans dépôt) et `applyScope` l’appellent. Le titulaire est relu à chaque fois. Lecture des pièces : `canView`. Ajout et suppression de pièces : `canManage`, sans ce nouveau droit.

La validation NV1 déjà ouverte au responsable du `facture.programme_id` n’est pas modifiée. Une facture de marché avec `programme_id` vide n’ouvre pas la validation.

## DÉPLOIEMENT-TEST

```
git pull origin main
php artisan optimize:clear
php artisan view:clear
php artisan journal:sync
```

Pas de migrate. Pas de route:clear. Pas de build.

1. Compte non admin, responsable programme d’ARBOREA : une facture d’un marché ARBOREA qu’il n’a pas déposée apparaît dans Toutes les factures, la boîte, la fiche, le PDF. Il ne modifie pas le dépôt. S’il n’est pas le valideur désigné et que la facture n’a pas son programme en `programme_id`, il ne valide pas. S’il l’a déjà, le bouton NV1 reste celui d’avant.
2. Un autre utilisateur hors socle ne la voit pas.
3. Facture sans marché : inchangé.
4. Changer le responsable : l’ancien ne la voit plus, sauf s’il est déposant ou valideur. Le nouveau la voit.

## LEÇON

`DepotFactureDocumentAccess::canManage` était égal à `canView`. Élargir `canView` aurait permis d’ajouter et de supprimer des pièces. La lecture et la gestion sont deux méthodes. Le responsable programme de la validation NV1 est `factures.programme_id`, pas `marches.programme_id` : ne pas les confondre.
