---
suivi: 1209
date: 2026-08-05
sujet: Tâches de suivi — morph dossier foncier + panneau Synthèse
chantier: foncier / taches
type: feature
statut: poussé
hash: 41a2b57b
fichiers:
    - database/migrations/2026_08_05_130000_add_attachable_to_taches_suivi_table.php
    - app/Models/TacheSuivi.php
    - app/Models/DossierFoncier.php
    - app/Services/TacheSuiviService.php
    - app/Http/Requests/TacheSuivi/StoreTacheSuiviRequest.php
    - app/Http/Controllers/Foncier/DossierFoncierController.php
    - app/Support/TacheSuiviPresenter.php
    - resources/js/Pages/Foncier/Dossiers/Show.vue
    - resources/js/Components/TachesSuivi/TacheSuiviFormDialog.vue
    - resources/js/Components/TachesSuivi/TacheSuiviProgrammeTag.vue
    - resources/css/mes-taches.css
    - tests/Unit/TacheSuiviVisibilityScopeTest.php
    - tests/Unit/TacheSuiviDossierAssignationTest.php
    - docs/dictionnaire/taches.md
    - database/data/journal_mises_a_jour_post_2026_06_12.php
    - public/build/
    - docs/suivi/SUIVI_1209_taches_dossier_foncier_morph.md
---

# SUIVI #1209 — Tâches dossier foncier (morph)

## PROMPT ENVOYÉ

Morph `attachable_*` sur `taches_suivi` pour dossier foncier uniquement ;
visibilité D62 ; panneau Synthèse ; backfill = ZÉRO.
Mes tâches / digest / retard = #1211 (hors scope de #1209).

## Fiches lues

- **#1206** (diag) — Q6=B polymorphe, Q8=A ACL dossier, Q9=A Synthèse ;
  `programme_id` absent ; rattachement programme = étiquette.
- Aucune autre fiche sur les mêmes chemins front (Show.vue / FormDialog)
  récente concurrente hors #1201 bandeau (non touché ici).

## SYNTHÈSE

### Schéma
- Colonnes nullable `attachable_type` / `attachable_id` + index
  `taches_suivi_attachable_idx` — **pas de FK**, aucune UPDATE de données.
- `DossierFoncier::tachesSuivi()` MorphMany ; pas de `Programme::tachesSuivi()`.

### Visibilité (`scopeVisiblePourUtilisateur`)
1. attachable NULL / non-dossier → règles historiques inchangées.
2. attachable = `DossierFoncier` → ACL seule (bypass M365 RecordAcl, sinon
   créateur dossier ∪ `record_habilitations` lecture|gestion).
   **Pas d’exemption** créateur/assigné de la tâche.

### Assignation
- `assertPersonnesHabiliteesSurDossier` dans sync/attacher → 422 nommé
  (`Impossible d’assigner X : non habilité(e) sur le dossier REF.`).
- UI création dossier : MultiSelect sur `users_habilites_taches` (pas Graph).

### UI
- Panneau « Tâches du dossier » sur Synthèse (après grille Responsables),
  miroir programme (`ProgrammeTacheSuiviLigne` + FormDialog + DetailModal).
- Tag virtuel `Dossier {reference}` (type `dossier`).

### Mapping UI ↔ back (personnes mode dossier)

| Libellé affiché | Posté | Colonne / cible | Back |
|-----------------|-------|-----------------|------|
| Nom utilisateur | `{ user_id }` | `tache_suivi_personnes.user_id` | `users.id` habilité |
| — | `dossier_foncier_id` | `attachable_*` | `DossierFoncier` |

Statuts / importance / validation_mode : inchangés (`TacheSuivi` + `tacheSuiviLabels.js`).

### Hors scope (#1211)
Mes tâches, bandeau retard, digest 9h pour attachable dossier.

## DÉPLOIEMENT-TEST

```
git pull origin main
php artisan migrate --force
php artisan view:clear
php artisan journal:sync
```

Tests prod :
1. Ouvrir un dossier habilité → Synthèse → panneau « Tâches du dossier ».
2. Créer une tâche, assigner uniquement un habilité → OK.
3. (API) assigner un non-habilité → 422 avec référence dossier.
4. Utilisateur non habilité sur le dossier : ne voit pas la tâche (même créateur).
5. Dashboard programme : compteur tâches inchangé (pas de morph programme).
6. Fuite : GET tâche dossier sans ACL → 403 ; Mes tâches ne liste pas encore
   ces tâches (#1211).

## LEÇON

- Morph nullable + index sans FK pour ancrage objet métier sans casser
  l’historique étiquette-programme.
- Visibilité dossier = ACL stricte y compris créateur tâche (D62) —
  le sélecteur personnes doit être filtré côté UI **et** validé serveur.
- Ne pas brancher Mes tâches dans le même lot que le panneau dossier
  (sérialisation front + scope digest).
