---
suivi: 1286
date: 2026-08-12
sujet: retrait_parcelle_dossier
chantier: module-foncier
type: diagnostic
statut: poussé
hash: c614422c
fichiers:
  - tools/diag/verif_retrait_parcelle_dossier_1286.php
  - docs/suivi/SUIVI_1286_retrait_parcelle_dossier.md
---

## PROMPT ENVOYÉ

SUIVI #1286 — Ajout fonctionnel « Retirer une parcelle du périmètre d’un
dossier » sans toucher à la détention. Option A Robin (retirer ≠ supprimer).
OBJET 1 lecture seule ; clause d’arrêt si parcelles purement dérivées.
Serialisation après #1285. Pas de migration sans arbitrage.

## SYNTHÈSE

### CLAUSE D’ARRÊT OBJET 1b — APPLIQUÉE

Les parcelles d’un dossier sont **DÉRIVÉES à la lecture** des détentions
courantes (`proprietaires.parcellesActuelles` / `parcelle_proprietaire` où
`date_fin_detention IS NULL`) des propriétaires rattachés au dossier
(`dossier_foncier_proprietaire`).

`dossier_foncier_parcelle` est un **miroir technique déprécié** (#1111),
resyncé par `syncParcellesMiroirDepuisProprietaires()` via **`sync()`**.

→ Un retrait unitaire de périmètre **sans** marqueur d’exclusion est
**impossible**. Aucune modification `app/` / `resources/` / `routes/` /
migration. Feature reportée après arbitrage Robin.

### Distinction périmètre / détention

| Action | Effet |
|--------|-------|
| Retirer propriétaire du dossier (existant) | Detach pivot proprio ; détention intacte |
| « Ajouter / Choisir des parcelles » | **Écrit la détention** puis resync miroir |
| Retrait parcelle demandé (#1286) | Sortir du périmètre dossier **sans** clôturer détention — **nécessite exclusion** |

### Option retenue (NON CODÉE — arbitrage)

Table dédiée `dossier_foncier_parcelle_exclusions`
`(dossier_foncier_id, parcelle_id, exclu_par_id, exclu_at)` unique.

**Pas** de colonne sur `dossier_foncier_parcelle` : le `sync($ids)` du miroir
détruirait les métadonnées (piège sync du projet).

Filtrage futur dans `parcellesCandidats()` + `serializeParcellesDerivees`.
Ré-ajout = DELETE exclusion (détention déjà présente).

### Engagements bloquants (pour le futur lot)

Liste à refuser côté serveur : offre (`offre_parcelle`), promesse
(`promesse_parcelle`), avenant (`promesse_avenants` via promesse), CS
(`conditions_suspensives` via promesse), acte (`acte_parcelle`), scénario
(`scenario_parcelle`), charge de portage (`charges_portage`).

Aujourd’hui `engagementsParParcelle` ne couvre que offre / promesse / acte
(badges UI).

### Trace

`AuditsFieldChanges` sur dossier = champs scalaires seulement. Le detach
propriétaire actuel **n’écrit pas** d’historique pivot. À prévoir dans le
lot feature (commentaire système ou FieldAudit dédié) — signalé, pas créé ici.

### Droits (#1285)

Alignement : `authorize('update', $dossierFoncier)` =
`userCanManageRecord` (créateur / droit gestion `record_habilitations` /
bypass DG·Admin). Pas l’ACL référentiel #1116 (parcelles/proprios).

### Fiches lues

#1285 (ACL dossiers = record_habilitations bloquant), #1111 (D51 dérivées +
miroir), #1194 (onglet FONCIER, attach écrit détention), #1101 (ancien
attach direct retiré).

## DÉPLOIEMENT-TEST

```bash
git pull origin main
php -d display_errors=1 tools/diag/verif_retrait_parcelle_dossier_1286.php
```

Pas de migrate / journal / rebuild (aucun code applicatif).

## LEÇON

Quand l’UI dit « parcelles dérivées des détentions », un bouton « retirer
du dossier » au niveau parcelle **implique** un marqueur d’exclusion (ou
de changer le modèle). Toucher au miroir `sync()` sans table d’exclusion
indépendante efface l’intention au prochain resync. Diagnostiquer le
modèle **avant** de coder le bouton.
