---
suivi: 1753
date: 2026-09-30
sujet: Liens entre fiche programme et dossier foncier
chantier: foncier
type: evolution
statut: poussé
hash:
fichiers:
  - app/Support/Foncier/DossierProgrammeLink.php
  - app/Support/Foncier/DossierFoncierListeAvancement.php
  - app/Http/Middleware/HandleInertiaRequests.php
  - app/Http/Controllers/Foncier/DossierFoncierController.php
  - resources/js/Layouts/AppShell.vue
  - resources/js/Components/ObjectSubNavBar.vue
  - resources/js/Pages/Foncier/Dossiers/Index.vue
  - database/data/journal_mises_a_jour_post_2026_06_12.php
  - tests/Unit/LiensProgrammeDossierFoncier1753SourceTest.php
  - tests/Feature/Foncier/LiensProgrammeDossierFoncier1753Test.php
  - tests/Unit/DossierFoncier1442SourceTest.php
  - public/build/
  - docs/suivi/SUIVI_1753_liens_programme_dossier_foncier.md
---

# SUIVI #1753 — Liens programme et dossier foncier

## PROMPT ENVOYÉ

Raccourci dans les deux sens entre un programme et le dossier foncier lié, plus le lien depuis la liste des dossiers. Uniquement si la fiche cible est déjà visible. Pas de nouvelle règle d’accès.

## FICHES LUES

- #1555 LEÇON : deux habilitations distinctes. On ne déduit pas la visibilité du dossier depuis celle du programme, ni l’inverse. L’entrée se calcule côté serveur. Ne pas afficher la fiche dossier à l’intérieur de la barre programme.
- #1730 : la colonne Programme de la liste vient de `dossiers_fonciers.programme_id`.

## CONSTAT

- Un dossier a au plus un programme : `DossierFoncier::programme()` (`app/Models/DossierFoncier.php` ligne 154), colonne `programme_id`.
- L’inverse n’est pas une relation Eloquent. L’unicité « un programme, un dossier vivant » est applicative (`DossierProgrammeLink`, hook `saving` lignes 100-114). L’index unique MySQL a été retiré (#1597) : plusieurs dossiers vivants restent possibles si ce contrôle est contourné.
- Barre programme : `AppShell.vue` `programmeNavItems`. L’onglet FONCIER déjà présent (avant Commercialisation) ouvre le hub `/programmes/{id}/foncier`. Il reste en place. Documents est la dernière entrée.
- En-tête dossier : barre `showHeaderDossierBar`, identité référence et libellé. La fiche est `Foncier/Dossiers/Show.vue`.
- Dossier : `RecordHabilitationScope` + `DossierFoncierPolicy::view` → `userCanViewRecord`. Programme : `ProgrammeAccessService::accessibleProgrammeIds`. `Gate::before` laisse passer un admin ERP sur les policies ; le scope dossier, lui, lit les groupes bruts.

## SYNTHÈSE

Entrée FONCIER après Documents : un dossier visible → lien direct vers sa fiche ; plusieurs → menu référence et libellé. Absente si aucun dossier vivant visible (corbeille exclue par SoftDeletes, pas par `DB::table()`). Sur le dossier, bouton « Programme nom · référence » si le programme est dans le périmètre déjà utilisé par le switcher. Dans la liste, la cellule n’est un lien que dans ce cas ; sinon le texte reste. L’onglet hub, l’ordre des autres entrées et l’association depuis le dossier ne changent pas.

## DÉPLOIEMENT-TEST

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

1. Programme HORIZON : après Documents, FONCIER ouvre DF-2026-0004 ZEN.
2. Dossier ZEN : « Programme HORIZON · HEC34-033 » ouvre la fiche programme.
3. Liste des dossiers : « HORIZON · HEC34-033 » est un lien.
4. Programme sans dossier : pas de cette entrée.
5. Compte non habilité au dossier (pas un admin ERP) : pas d’entrée, ou dossier absent du menu.

## LEÇON

Le hub FONCIER (avant Commercialisation) et le raccourci vers la fiche dossier (après Documents) sont deux entrées. Ne pas déplacer le hub : il ne rend pas `Foncier/Dossiers/Show`. La liste avait déjà un lien sans contrôle du périmètre programme : le lien ne part que si l’identifiant est dans `accessibleProgrammeIds`, calculé une fois pour toute la liste.
