---
suivi: 1213
date: 2026-08-05
sujet: Diag état parcelle × promesse signée (avant transition auto)
chantier: foncier / parcelles / promesses
type: diagnostic
statut: poussé
hash: 7190d32c
fichiers:
    - tools/diag/diag_promesse_etat_parcelle_1213.php
    - docs/suivi/SUIVI_1213_diag_promesse_etat_parcelle.md
---

# SUIVI #1213 — PHASE 1 DIAGNOSTIC — état parcelle × promesse signée

## PROMPT ENVOYÉ

Constat Robin : parcelle en négociation / repérée alors qu’une promesse signée
existe. Option B retenue (avancer auto à `sous_compromis` à la SIGNATURE, en avant
seulement) + alerte de cohérence (D27 interdit la régression auto). PHASE 1 =
cartographie lecture seule. Aucun code métier.

## Fiches lues

- #1089 promesses brique 1 ; #1090 CS ; #1111 porte propriétaires ; #1125 alertes ;
  #1128 / R5 acte ; #1155 division/réunion ; #1194 / #1198 offres transitions ;
  #1208 prospection ; #1210 pattern diag.

## Enjeu (nommé)

L’indicateur de **maîtrise foncière** (Phase 2, surface sécurisée ÷ assiette D36)
sera **dérivé de `etat_foncier`**. Un état tenu à la main dérive → risque scénario
faux sans signal. D’où Option B + alerte A.

---

## OBJET 1 — Qui écrit `etat_foncier` aujourd’hui

### Schéma
| | |
|--|--|
| Table / colonne | `parcelles.etat_foncier` |
| Type | `string(32)` |
| Null | NON |
| Default | `'reperee'` |
| Migration | `2026_07_30_140100_create_parcelles_table.php` |

### Valeurs stockées exactes (`FoncierReferentiels`)
| Code | Libellé |
|------|---------|
| `reperee` | Repérée |
| `en_negociation` | En négociation |
| `sous_compromis` | Sous compromis |
| `acquise` | Acquise |
| `affectee` | Affectée |
| `pas_d_action` | Pas d’action |
| `projet_abandonne` | Projet abandonné |

Source : constantes + map `ETATS_FONCIERS` dans `app/Support/Foncier/FoncierReferentiels.php`.
**Pas d’enum SQL**, pas de table référentielle.

### Points d’écriture (exhaustif prod)

| # | Fichier | Déclencheur | Valeur |
|---|---------|-------------|--------|
| 1 | `ParcelleCreationService::applyServerDefaults` | Création (fiche, prospection, batch proprio, filles #1155) | **forcé** `reperee` |
| 2 | `ParcelleController::update` | PUT fiche `Form.vue` | toute clé `ETATS_FONCIERS` |
| 3 | `ParcelleController::updateChamp` | PATCH inline `Show.vue` | idem |
| 4 | `ActeAcquisitionService::creer` | Création acte (R5 + étape suivante) | mass-update `acquise` |

Non-écritures : Prospection (lecture/filtre), scénarios (sérialisation `niveau_maitrise`
= copie d’affichage de `etat_foncier`), géométrie IGN, seeds métier, artisan.

#1155 : mères → `statut_cadastral=remplacee` seulement ; **`etat_foncier` inchangé**.
Filles → créées via service → `reperee`.

### Audit
- `Parcelle` utilise `AuditsFieldChanges` ; `etat_foncier` est dans `auditedFields()`.
- Couvert : update fiche + patch inline (Eloquent `save` → `recordDirty`).
- **Non couvert** : mass-update R5 (`query()->update`) — bypass events Eloquent.
  → Toute transition auto PHASE 2 **doit** passer par Eloquent `save` (ou
  `FieldAuditService::recordChange` explicite), sinon indéfendable.

### Machine de transitions ?
**Absente** pour la parcelle. Contrairement à `OffreStatutTransitions` (#1198/#1202) :
validation `in(ETATS_FONCIERS)` seulement, y compris régression manuelle après `acquise`
(test socle acte).

---

## OBJET 2 — Cycle de vie de la promesse

### Table
`promesses` (modèle `PromesseFonciere`). Statuts :
`en_cours` | `signe` | `caduc` | `acte` | `abandonne`.

### Ce qui matérialise la SIGNATURE — POINT DÉCISIF

| Signal | Rôle actuel |
|--------|-------------|
| `date_signature` | **Obligatoire à la création** (`validateCreation`). Remplie dès `en_cours`. |
| `statut = signe` | Workflow **manuel** (Select fiche promesse). Défaut création = `en_cours`. |

**Aucun code ne couple** `date_signature` → `statut=signe`.

**Conclusion pour Option B :** le déclencheur « SIGNATURE » non ambigu est
**`statut` passant à `signe`** (chemin UI `PromesseFonciereController::update` →
`mettreAJour`). `date_signature` **seule** est insuffisante et trompeuse (présent
dès la rédaction).

Prérequis éventuel (arbitrage Robin, hors PHASE 2 auto) : si le métier veut que
« date de signature » = vrai jour de signature, séparer la date de rédaction de la
date de signature — **aujourd’hui ce n’est pas le cas**. Pour PHASE 2, s’accrocher
à `statut=signe` (et idéalement exiger `date_signature` non nulle en garde).

### Chemins statut
1. Création → `en_cours` (+ date_signature requise).
2. Update fiche → tout statut manuel (y compris `signe`, `caduc`, `abandonne`).
3. Rattachement acte → auto `acte` + verrou (`verrouillerApresActe`).
4. Déverrouillage → ne change pas le statut.

### Caducité (D27)
- Statut `caduc` **présent** dans le référentiel + Select UI.
- **Pas de bascule auto** (test `pas_de_bascule_auto_caduc_sur_expiration` ;
  `PromesseExpirationType` : « Pas de passage automatique à caduc »).
- Livré : alerte `promesse_expiration` + tâche décisionnelle (#1125) sur
  `en_cours`/`signe` non verrouillés.

### Conditions suspensives
Aucun impact sur `promesses.statut`. CS ont leurs propres statuts
(`a_lever`, `levee`, …).

---

## OBJET 3 — Lien promesse → parcelles

### Chemin univoque
```
promesses ──< promesse_parcelle >── parcelles
              (+ surface_promise_m2 nullable)
```
Relation `PromesseFonciere::parcelles()`. Sync `PromesseFonciereService::syncParcelles`.
≥1 parcelle obligatoire à la création.

**Univoque** pour le périmètre promis. Indépendant de `dossier_foncier_parcelle`
et de `acte_parcelle`.

### D51 — clarification vs prompt
Le prompt évoque la suppression de `dossier_foncier_proprietaire`. **En code actuel
(#1111 / #1194) la table EXISTE** : le dossier porte ses propriétaires. Elle ne
matérialise **pas** le lien promesse↔parcelles. Pas de second chemin concurrent pour
les parcelles d’une promesse.

Contexte : `promesses.proprietaire_id` = **un** propriétaire (validé ∈ dossier).
Une promesse = un vendeur, N parcelles via pivot.

### Surface partielle
Colonne `promesse_parcelle.surface_promise_m2` prête ; **UI non branchée** sur
`Promesses/Show.vue`. Pas de « division envisagée » sur l’objet promesse.

---

## OBJET 4 — Cas limites (à arbitrer, PAS tranchés)

### Ordre d’avancement (issu du référentiel + usages, **non codé** comme rang)
Axe : `reperee` < `en_negociation` < `sous_compromis` < `acquise` < `affectee`.
Hors axe : `pas_d_action`, `projet_abandonne`.
Aucun `OffreStatutTransitions`-like ni constante d’ordre sur la parcelle.
« En avant seulement » exige d’écrire cet ordre en PHASE 2 (ex. classe
`ParcelleEtatTransitions` miroir offres) — **pas dans ce diag**.

### 1. Indivision partiellement sécurisée
Promesse = 1 `proprietaire_id` ; parcelle peut avoir N détenteurs courants
(`parcelle_proprietaire.date_fin_detention IS NULL`) avec `quote_part` (decimal:6).
**Question Robin :** (a) avancer quand même ; (b) seulement si tous les détenteurs
courants ont une promesse `signe` ; (c) avancer + afficher couverture partielle.
Mesure : section 5 du script diag.

### 2. Parcelle en sortie (`pas_d_action` / `projet_abandonne`)
Hors axe. Signer dessus contredit la sortie.
**Question Robin :** avancer (le fait de signer gagne) ou laisser + alerter ?

### 3. Ordre — confirmation
Aucun code métier ne suppose un autre ordre. Scénarios exposent `niveau_maitrise`
= alias d’affichage de `etat_foncier`, sans comparaison ordinale.

---

## OBJET 5 — Alerte de cohérence

### Mécanisme existant
- Middleware `DeclencheFoncierAlertes` (bootstrap web) — pattern digest :
  verrou `foncier_alerte_runs` → `terminate()` → `FoncierAlerteEvaluationService`.
- Dédup `foncier_alertes_emises`.
- **Déjà PHP-FPM web** (pas CLI) → Teams OK sur OVH.

### Greffon
Nouveau type d’alerte dans le **même** évaluateur (pas un second pipeline).

### Incohérences à détecter
| Code | Condition |
|------|-----------|
| (a) | ≥1 promesse `statut=signe` liée via `promesse_parcelle` ET `etat_foncier` ∈ {`reperee`,`en_negociation`} (+ hors axe si arbitrage) |
| (b) | `etat_foncier=sous_compromis` ET **aucune** promesse `signe` liée (ex. caducité manuelle) |

### Affichage recommandé (sans coder)
1. **Bandeau fiche parcelle** (signal fort, action de correction manuelle).
2. Badge / pastille dans **grille Prospection** (#1208 — sérialiser).
3. Bloc optionnel synthèse **dossier** (agrégat des parcelles incohérentes).
Priorité : fiche parcelle + prospection (là où Robin voit le bug).

---

## OBJET 6 — Mesure prod

**Volumes = sortie du script sur OVH uniquement.** Ne pas inventer de chiffres locaux.

Le script liste :
- (a) signe + état < sous_compromis
- (a2) volume piège en_cours+date (informatif)
- (b) sous_compromis orphelines
- (c) acquise sans `acte_parcelle`
- répartition états + indivisions

**Ne pas corriger** — reprise = décision Robin (leçon #1191).

---

## OBJET 7 — Découpage PHASE 2 (proposition)

| Lot | Contenu | Fichiers typiques | Risque | Dépendance |
|-----|---------|-------------------|--------|------------|
| **1213b** | Transition auto : sur passage `statut→signe`, avancer parcelle(s) du pivot si rang < `sous_compromis` (et règles indivision/sortie arbitrées) ; audit Eloquent ; jamais régression | `PromesseFonciereService`, évent. `ParcelleEtatTransitions` (nouveau), **pas** de Vue si purement service | moyen (métier + audit) | Arbitrages objet 4 ; déclencheur = `signe` |
| **1213c** | Alerte cohérence (a)+(b) dans `FoncierAlerteEvaluationService` + affichage | Service alertes ; puis **série** `Parcelles/Show.vue` puis `Prospection` (#1208 zone) | moyen front sérialisé | 1213b utile mais alerte peut vivre seule |
| **1213d** | Reprise données (si volume diag > 0) — script one-shot ou commande **web** validée Robin | script tools / endpoint admin | élevé data | Sortie diag OVH + arbitrage |

### Migration ?
- **Pas nécessaire** pour stocker l’état (colonne existe).
- **Pas de nouvelle colonne** obligatoire si déclencheur = `statut=signe`.
- Migration éventuelle **seulement** si Robin veut séparer « date rédaction » /
  « date signature réelle » (prérequis UX) — hors Option B minimale.

### Sérialisation front
Ne pas paralléliser lots sur `Parcelles/Show.vue`, `Prospection/Index.vue`,
`manifest.json`.

### Ne pas toucher
R5 acte, #1155, #1182 détention, #1198 offres, #1209 tâches, moteur étude Fi.

---

## DÉPLOIEMENT-TEST

Script uniquement :
```
cd ~/.../erp-immo   # racine
php tools/diag/diag_promesse_etat_parcelle_1213.php
```
Coller la sortie brute dans le fil / la fiche après run OVH.

## LEÇON

`date_signature` obligatoire à la création ≠ signature workflow. Sans ce diagnostic,
Option B se serait accrochée au mauvais champ et aurait avancé dès la rédaction.
R5 mass-update contourne l’audit : ne pas reproduire pour `sous_compromis`.
