---
suivi: 1075
date: 2026-07-30
sujet: Module Foncier Lot 3 — ACL par enregistrement + dossiers + fil de suivi
chantier: module-foncier
type: feature
statut: poussé
hash: 267d2cb1
fichiers:
  - database/migrations/2026_07_30_150000_create_record_habilitations_table.php
  - database/migrations/2026_07_30_150100_create_motifs_veille_table.php
  - database/migrations/2026_07_30_150200_create_dossiers_fonciers_table.php
  - database/migrations/2026_07_30_150300_create_dossier_foncier_pivots_table.php
  - app/Support/Acl/RecordAcl.php
  - app/Models/Scopes/RecordHabilitationScope.php
  - app/Models/Concerns/HasRecordHabilitations.php
  - app/Models/RecordHabilitation.php
  - app/Models/DossierFoncier.php
  - app/Models/MotifVeille.php
  - app/Services/Acl/RecordHabilitationService.php
  - app/Policies/DossierFoncierPolicy.php
  - app/Http/Controllers/Foncier/DossierFoncierController.php
  - app/Http/Controllers/Foncier/FoncierCommentaireController.php
  - resources/js/Pages/Foncier/Dossiers/
  - tests/Feature/Foncier/FoncierDossiersAclTest.php
  - docs/suivi/SUIVI_1075_foncier_acl_dossiers.md
---

## PROMPT ENVOYÉ

SUIVI #1075 — MODULE FONCIER LOT 3 : ACL PAR ENREGISTREMENT + DOSSIERS
FONCIERS + FIL DE SUIVI. Brique ACL générique (liste blanche), dossiers,
motifs veille, pivots parcelle/responsable, fil entite_commentaires, écrans,
tests sécurité 5 chemins, non-régression #1074. Un commit, push.

## SYNTHÈSE

### Tranche ACL : morph `record_habilitations`
Une table morph réutilisable (pas `dossier_foncier_habilitations` dédiée) :
évite N tables clones ; le trait `HasRecordHabilitations` s’applique à tout
modèle cloisonné futur. Équivalent fonctionnel pour les dossiers.

### Chemins d’accès (liste blanche → défaut invisible)

| # | Chemin | Condition SQL / logique | Test |
|---|--------|-------------------------|------|
| 1 | Créateur | `createur_id = user.id` | create + liste A |
| 2 | Habilitation | EXISTS record_habilitations (lecture\|gestion) | lecture sans update |
| 3 | DG | m365GroupNames contient GS_DIRECTION_GENERALE | dg_voit_sans_habilitation |
| 4 | DSI | idem GS_DSI (orphelins R3) | dsi_modifie_habilitations |
| 5 | Admin ERP | idem GS_ADMIN_ERP | (bypass liste) |
| — | Aucun | invisible (scope + 404 URL) | b_ne_voit_pas_cinq_chemins |

Ordre d’évaluation scope : bypass groupes → sinon (créateur OR habilitation).
Policy view/update/manageHabilitations réutilise les méthodes du trait.
Compteurs direction : `queryWithoutRecordAcl()` (R8).

### Mapping UI ↔ DB

| UI | Valeur | Colonne |
|----|--------|---------|
| En cours / En veille / Terminée / Annulée | en_cours…annulee | dossiers_fonciers.statut |
| Apporteur…Autre | apporteur…autre | origine |
| Lecture / Gestion | lecture / gestion | record_habilitations.droit |
| Motifs veille seed | libellés FR | motifs_veille.libelle |

### Non-régression #1074
Parcelle / Proprietaire SANS HasRecordHabilitations. Test dédié.

### Fil de suivi
HasEntiteCommentaires sur DossierFoncier, Parcelle, Proprietaire.
GET `/foncier/entite-commentaires/{id}` : charge parent hors scope puis ACL → 403.

## DÉPLOIEMENT / TEST

1. git pull && php artisan migrate --force
2. view:clear && journal:sync && route:clear
3. Créer dossier (créateur seul) ; collègue 404 ; habiliter lecture ; DG voit
4. Veille sans motif → erreur ; avec motif+date → OK
5. Parcelle dans dossier inaccessible → message minimal sans divulgation

## LEÇON

ACL avant écrans ; filtrage SQL global + liste blanche de groupes (jamais
early-return permissif). Compteurs direction hors scope dès le jour 1.
