---
suivi: 1789
date: 2026-10-05
sujet: Diagnostic valorisation parcelle (achat, estimé, cible)
chantier: foncier / parcelles
type: diagnostic
statut: poussé
hash:
fichiers:
  - docs/suivi/SUIVI_1789_diag_valorisation_parcelle.md
---

# SUIVI #1789 — diagnostic valorisation parcelle

Phase 1. Aucun code modifié. Aucune migration. Aucune écriture SQL. Les comptes de production ne sont pas exécutés depuis cet environnement : la commande tinker est en section 4.

## FICHES LUES

- **#1131** (citée par #1734) — `parcelle_valorisations` empile des réévaluations. `valeur` est un montant total, pas un prix au mètre carré.
- **#1734 LEÇON** — ne pas typer une ligne « cible » dans `parcelle_valorisations` : la courante est la dernière date, sans filtre de méthode. La surface du gain est `surface_cadastrale_m2`. `COALESCE(arpentée, cadastrale)` ne sert qu’au filtre et au tri Surface.
- **#1737 LEÇON** — les compteurs « à compléter » ne réutilisent ni le périmètre des cartes de décision, ni le SQL de la qualité « Détenue sans valorisation ».
- **#1770 LEÇON** — ne pas caster `sans_valorisation` en boolean (null deviendrait false, la Rétrocession ne serait plus automatique). Ne pas additionner la colonne €/m² pour le total cible.

Ces quatre points sont encore vrais dans le code lu ci-dessous. Ils conditionnent toute reprise : une nouvelle colonne « estimé » ne peut pas être une ligne de plus dans `parcelle_valorisations`.

## PROMPT ENVOYÉ

Carte Valorisations jugée trop compliquée. Modèle visé, par parcelle, tout en €/m² : prix d’achat déduit de l’acte (prix ÷ somme des surfaces des parcelles de l’acte), lecture seule ; prix estimé à ce jour = une valeur + un motif ; prix cible = une valeur + un motif. Diagnostic de l’existant uniquement.

## 1. Modèle actuel

### 1.a Tables et colonnes — unité stockée

**`parcelle_valorisations`** — `database/migrations/2026_07_31_180000_create_parcelle_valorisations_table.php:28-44`. Pas de SoftDeletes. Pas d’alter ultérieur (aucun `Schema::table('parcelle_valorisations'`).

| Colonne | Type | Unité |
|---|---|---|
| `parcelle_id` | FK `parcelles` cascadeOnDelete (hard delete seulement) | — |
| `date_valorisation` | date, obligatoire | date |
| `valeur` | decimal(15,2), obligatoire | **total €**, pas €/m² |
| `methode` | string(40), obligatoire | code : `expertise`, `comparaison`, `charge_fonciere`, `interne`, `autre` (`FoncierReferentiels.php:677-695`) |
| `auteur_id` | FK `users` nullable | — |
| `commentaire` | text nullable | texte libre, pas un motif obligatoire |
| `created_at`, `updated_at` | timestamps | — |

Index `(parcelle_id, date_valorisation)` nommé `parcelle_valo_parcelle_date_idx`. Modèle : `app/Models/ParcelleValorisation.php:19`. `valeur` cast `decimal:2` (l.74). Champs audités : date, valeur, methode, commentaire ; seul `valeur` est sensible (`:26-39`).

Le €/m² « actuel » n’est pas stocké. Il est calculé à la lecture : `valeur` ÷ `surface_cadastrale_m2` si la surface est > 0 (`ParcelleValorisationService.php:185-198`).

**`parcelles.valorisation_cible_eur_m2`** — decimal(12,2) nullable. **€/m².** Migration `2026_09_28_173400_add_valorisation_cible_to_parcelles.php:29`.

**`parcelles.valorisation_cible_explication`** — text nullable. Obligatoire dès qu’un prix est saisi (`ParcelleValorisationService.php:255-264`).

**`parcelles.sans_valorisation`** — boolean nullable. Pas un montant. Trois états (`2026_10_01_177000_add_sans_valorisation_to_parcelles.php:7-9` et `:28`) :

- `NULL` = automatique
- `1` = sans valorisation forcé
- `0` = valorisation exigée

**`parcelles.sans_valorisation_motif`** — string(200) nullable. Facultatif (`ParcelleValorisationController.php:136-138`, placeholder « Motif (facultatif) » dans `Show.vue:2283`).

Pas de cast boolean sur `sans_valorisation` (`Parcelle.php:165-182`). `sansValorisationStockee()` lit l’attribut brut (`:673-690`).

**Historique des changements** — table `entity_field_audits` (`EntityFieldAudit.php:21`), morph sur la ligne de valorisation et sur la parcelle (cible, explication, sans valorisation, motif). L’empilement des lignes de `parcelle_valorisations` est l’historique métier des montants. Une suppression est un DELETE physique, avec copie d’audit vers la fiche (`ParcelleValorisationService.php:105-120`).

Aucune colonne « prix estimé », « motif de l’estimé », ni « prix d’achat » sur `parcelles`. Recherche des migrations `*parcelle*` : aucun `prix_`.

### 1.b Comment la valorisation actuelle est choisie

Il n’existe pas d’état « validée ». La courante est **la ligne de `date_valorisation` la plus récente, puis `id` le plus grand**. Même règle à quatre endroits :

- Fiche : `orderByDesc('date_valorisation')->orderByDesc('id')`, la courante est `$rows->first()` (`ParcelleValorisationService.php:135-152`).
- Liste : sous-requête `LIMIT 1` même tri (`ParcelleProspectionQuery.php:890-905`).
- Supervision : copie de ce SQL (`ParcelleSupervisionQuery.php:467-472`).
- Patrimoine : `dernieresPourParcelles()` parcourt le même tri et garde la première ligne par parcelle (`ParcelleValorisationService.php:307-318`).

`created_at` n’entre pas dans le tri. La méthode (`expertise`, `interne`…) n’entre pas non plus. Deux lignes le même jour : le plus grand `id` gagne. Les autres lignes restent affichées comme historique (`Show.vue:2469-2470`, `.slice(1)`).

La liste signale le surplus : `valorisations_autres` = nombre de lignes − 1 (`ProspectionController.php:405`), affiché « +N autre(s) » (`Index.vue:1710-1713`).

### 1.c Règle « Sans valorisation »

Décision unique, PHP : `Parcelle::sansValorisationEffectif()` (`Parcelle.php:697-707`).

```
effectif = (stocké = 1) OU (stocké IS NULL ET destination = 'retrocession')
```

`stocké = 0` reste exigeant même en Rétrocession. SQL jumeau : `sqlSansValorisationEffectif()` (`:723-727`). `sansValorisationAutomatique()` (`:713-716`) = stocké null ET effectif (mention « automatique : Rétrocession »).

La case à cocher de la fiche (`Show.vue:522-536`) : cocher une Rétrocession reposte `''` (null, automatique) ; décocher une Rétrocession reposte `'0'` (exigée) ; cocher une autre destination reposte `'1'` (forcé).

**Où l’effectif est lu**

| Endroit | Fichier:ligne | Effet |
|---|---|---|
| Carte fiche | `Show.vue:2294-2329` et résumé `:609-616` | libellé « Sans valo » ; le total € stocké reste en petit si une ligne existe |
| Colonnes liste Valo. / Cible | `Index.vue:1700-1726` | « Sans valo » à la place du montant et de « À compléter » |
| Totaux liste (Valorisation, Cible, Gain) | `ParcelleProspectionQuery.php:867-883` | la parcelle compte **0** même si une ligne `valeur` existe |
| Info-bulle des totaux | `Index.vue:673-677` | « Les parcelles sans valorisation comptent pour 0. » |
| Export, colonne « Sans valorisation » | `ProspectionListeExportService.php:161` | `oui` / `non` |
| Ligne Total de l’export | `:182-186` | totaux qui appliquent le 0 ; compteur « N oui » |
| « Valorisation à compléter » | `Parcelle.php:734-748` et `:773-777` | effectif ⇒ pas à compléter. Filtre : statut `detenue`, aucune ligne, `NOT` effectif |
| « Cible à compléter » | `:755-767` et `:783-787` | effectif ⇒ pas à compléter. Filtre : statut `detenue`, `valorisation_cible_eur_m2` NULL, `NOT` effectif |
| Pastilles de comptage liste | `ParcelleProspectionQuery.php:170-183` | mêmes `restreindre*` |
| Cartes Supervision « À compléter » | `ParcelleSupervisionQuery.php:397-410` | appellent ces `restreindre*`. Périmètre : cadastre actif + filtres barre. **Pas** `restreindrePerimetre` (`:416-418` vs `:321-324`) |
| Libellé d’historique | `FieldAuditService.php:443-451` | null → « Automatique », 1 → « Sans valorisation », 0 → « Valorisation exigée » |

**Où l’effectif n’est pas lu**

- Carte qualité « Détenue sans valorisation » : `statut = detenue` ET la sous-requête de valeur récente `IS NULL` (`ParcelleSupervisionQuery.php:156-158`, drill `:266-268`, libellé `:783`). Une Rétrocession sans ligne est donc comptée ici, alors qu’elle est exclue de « À compléter ». Une ligne à `0,00` n’est pas NULL : elle n’est pas « sans valorisation » pour cette carte.
- Sous-liste commune : `ProspectionParcellesSublist.vue:208-222` ne teste pas `sans_valorisation`. Une Rétrocession sans ligne affiche « — », pas « Sans valo ».
- Patrimoine : ne lit ni `sans_valorisation`, ni la cible.
- Les sommes Supervision (`:96`, `:846`) additionnent la dernière `valeur` en €. Elles ne forcent pas 0 quand l’effectif est vrai.

« À compléter » (liste et fiche) exige en plus le statut calculé `detenue` (`Parcelle.php:740-742` et `:760-762`). Une parcelle en négociation sans ligne n’est pas signalée.

Les lignes de `parcelle_valorisations` ne sont pas effacées quand on coche « Sans valorisation » (`ParcelleValorisationService.php:275-277`).

## 2. Actes

### 2.a Table, prix, lien, surface

**`actes_fonciers`** — création `2026_07_30_170200_create_actes_fonciers_table.php:18-40`. Pas de SoftDeletes sur `ActeFoncier`.

Champ de prix sur l’acte : **`prix_numeraire` decimal(14,2), NOT NULL**. C’est le seul montant. Validation saisie : obligatoire, numérique, `min:0` (`ActeFoncierController.php:169` et `:194`). `0.00` est donc une valeur stockée. NULL n’est pas prévu par la migration.

Libellés écran :

- Liste des actes, colonne « Prix » : `ActesFonciers/Index.vue:44` et `:58-60`, `Number(prix_numeraire).toLocaleString(..., currency EUR)`.
- Fiche acte : « Prix d’achat » si `sens` n’est pas `vente`, sinon « Prix de vente » (`ActesFonciers/Show.vue:80-84`).
- Référence affichée : `reference_acte`, sinon `#` + `id` (`Index.vue:52`, `Show.vue:49`).

Absent de `actes_fonciers` : prix déclaré, frais d’acte, ODF, prix au mètre carré. Ces champs sont ailleurs :

- Promesse `promesses` (`2026_07_30_180000_create_promesses_table.php:24-31`) : `prix_numeraire`, `prix_valorise`, `prix_declare`, `mode_prix`, `prix_m2`, `surface_reference_m2`, `frais_acte_verses`.
- Écart promesse → acte, informatif : `ActeFoncierController.php:961-968` compare le prix effectif de la promesse à `actes_fonciers.prix_numeraire`. Rien n’est recopié sur la parcelle.
- ODF : `scenario_fiche_lignes.prix_achat_odf_ttc` (`ScenarioFicheLigne.php:35`), pas l’acte.

Autres colonnes d’acte utiles : `sens` (`achat` défaut / `vente`, migration `2026_09_24_170000`), `reference_acte` nullable, `date_acte`, `promesse_id` nullable, `dossier_foncier_id` nullable, `societe_acquereur_id` nullable depuis #1698, `societe_vendeuse_id`, `notaire_fournisseur_id`.

**Lien acte ↔ parcelles** — pivot `acte_parcelle` (`2026_07_30_170300:19-29`). Unique `(acte_foncier_id, parcelle_id)` seulement. **Pas d’unicité sur `parcelle_id` : une parcelle peut être dans plusieurs actes** (achat et vente compris). Relation : `Parcelle.php:286-289`, `ActeFoncier.php:116-121`.

**Surfaces**

- `parcelles.surface_cadastrale_m2` — utilisée pour le €/m² actuel, le total cible et le gain (`ParcelleValorisationService.php:192-204`).
- `parcelles.surface_arpentee_m2` — affichée, et utilisée seulement par le filtre / tri Surface via `COALESCE(arpentée, cadastrale)` (`ParcelleProspectionQuery.php:311` et `:685`). Pas dans le calcul de valorisation.
- `acte_parcelle.surface_acquise_m2` decimal(14,2) nullable (`2026_07_31_150000:25-28`) — surface partielle saisie sur la fiche acte (`ActeFoncierController.php:923-928`). Pas lue par la valorisation ni par `prixAcquisitionReference`.

### 2.b Montants cités (#63, #39, « 2003 D 14132 », #38)

Le montant affiché à côté de ces libellés, en liste comme en fiche, est **`actes_fonciers.prix_numeraire`**, formaté en euros (`Index.vue:52-60`, `Show.vue:80-84`).

- « #63 », « #39 », « #38 » = `id` de l’acte quand `reference_acte` est vide.
- « 2003 D 14132 » = `reference_acte` renseigné.

Ce n’est ni un prix au mètre carré, ni une quote-part de parcelle, ni `prix_declare`, ni un ODF. La commande de la section 4 relit ces quatre lignes (id, référence, sens, `prix_numeraire`, date) pour le constater en base. Non exécutée ici.

### 2.c Prix d’achat déjà calculé ou stocké sur la parcelle

**Non stocké.** Aucune colonne de prix d’achat sur `parcelles`.

**Calculé à la volée, en total € de l’acte entier, pas en €/m², pas divisé par les surfaces.** `prixAcquisitionReference()` (`ParcelleValorisationService.php:343-373`) :

1. Premier propriétaire actuel (`proprietairesActuels->first()`, sans `orderBy`, `:356`).
2. Si son pivot `parcelle_proprietaire.acte_foncier_id` pointe un acte qui n’est pas une vente et a un `prix_numeraire`, retourne **ce montant entier**.
3. Sinon, le plus récent acte non-vente de la parcelle (`date_acte` desc) qui a un `prix_numeraire`, montant entier.
4. Sinon null.

Affiché sur la fiche : « Prix d’acquisition de référence » (`Show.vue:2262-2263`). L’écart « vs acquisition » (`:2397-2404`) est `valeur` (total € de la parcelle) − ce prix d’acte entier (`serialize` `:404-407`). Si l’acte couvre plusieurs parcelles, le même total d’acte est repris sur chacune.

Patrimoine ne passe pas par cette méthode. Il prend le premier acte du `with` trié `date_acte` desc, `id` desc, **sans exclure les ventes** (`PatrimoineFoncierQuery.php:49-58` et `:92-93`) et expose `prix_numeraire` tel quel (`:153-154`), colonne « Prix acte » (`Patrimoine/Index.vue:166` et `:215-216`).

## 3. Usages qui lisent les valorisations

Unités rappel : colonne liste « Valo. » = dernière `valeur` en **€ totaux** (`Index.vue:898-900`). Colonne « Cible » = **€/m²** (`:912-914`). Tuile « Valorisation » = somme des totaux €, avec sans-valo effectif à 0. Tuile « Cible » = somme de (prix €/m² × surface cadastrale). Tuile « Gain potentiel » = somme des gains €.

| Écran / calcul | Fichier:ligne | Ce qui est lu |
|---|---|---|
| Liste parcelles, colonnes Valo. et Cible | `Prospection/Index.vue:1538-1543`, `:1700-1726` | dernière `valeur` € ; cible €/m² ; « Sans valo » ; « À compléter » ; « +N autres » |
| Sérialisation liste | `ProspectionController.php:386-405` | `valorisation_recente`, `valorisation_cible_eur_m2`, `valorisations_autres`, flags à compléter, `sans_valorisation` = effectif |
| Tri Cible | `ParcelleProspectionQuery.php:326` | `parcelles.valorisation_cible_eur_m2`. La colonne Valo. n’est pas dans `COLONNES_TRIABLES` (`Index.vue:321-329`) |
| Filtres URL `valorisation_a_completer` et `valorisation_cible_a_completer` | `Index.vue:398-402` ; `ParcelleProspectionQuery.php:390-396` | `restreindreValorisationACompleter` / `Cible` |
| Tuiles totaux | `Index.vue:1414-1424` ; SQL `:829-837` | actuelle €, cible € (prix × m²), gain € |
| Sous-liste (mode commune) | `ProspectionParcellesSublist.vue:208-222`, branchée `Index.vue:1843` | mêmes montants ; pas le libellé « Sans valo » |
| Export Excel | `ProspectionListeExportService.php:22-43`, `:132-163`, `:174-186` ; appel `ProspectionController.php:139-159` | colonne Valorisation = `valeur` € ; colonne cible = €/m² ; explication ; gain € ; « Sans valorisation » oui/non. Ligne Total : somme € actuelle en col. 12, somme € cible (prix × surface) en col. 13 **sous l’en-tête « Valorisation cible (€/m²) »**, phrase en col. 14, gain en col. 15. Pas une formule SOMME des cellules |
| Fiche parcelle, carte Valorisations | `Parcelles/Show.vue:2242-2554` ; payload `ParcelleController.php:448-450` et `:1282-1287` | courante, historique, cible + explication, case sans valo, comparaison, prix d’acquisition de référence |
| Saisie | `ParcelleValorisationController.php:101-152` ; routes `web.php:996-1014` | POST/PUT/DELETE des lignes ; PUT cible ; PUT sans-valorisation. Droits `foncier.gestion` et `foncier.prix` |
| Documents d’expertise | `ParcelleValorisation.php:99-101` ; `Show.vue:2455-2465` | morph `entity_documents` sur chaque ligne |
| Supervision, cartes indicateurs | `ParcelleSupervisionQuery.php:92-111` ; affichage `Supervision/Index.vue:141-143` | somme des dernières `valeur` €. Phrase plancher : `ParcelleSupervisionController.php:52` |
| Supervision, répartition | `ParcelleSupervisionQuery.php:846` ; `Supervision/Index.vue:203-213` | même somme € par développeur ou par commune |
| Supervision, « Valorisation à compléter » et « Valorisation cible à compléter » | `ParcelleSupervisionQuery.php:397-410` ; cartes `Supervision/Index.vue:170-184` | liens vers `/foncier/prospection?...=1` (`:439-450`) |
| Supervision, qualité « Détenue sans valorisation » | `:156-158`, `:266-268`, `:783` | absence de ligne (valeur récente NULL), pas l’effectif sans valo |
| Supervision, qualité « Acte sans prix » | `:159`, `:784` | `prix_numeraire IS NULL`. La colonne est NOT NULL : un acte à 0,00 € ne matche pas |
| Patrimoine | `PatrimoineFoncierQuery.php:76-77`, `:153-171` ; `Patrimoine/Index.vue:166-222` | prix d’acte entier (acte le plus récent, ventes incluses) ; dernière `valeur` € et sa date ; `nb_valorisations`. Pas la cible, pas le sans-valo |
| Synthèse dossier | `DossierSyntheseTab.vue:607` | `promesses.prix_numeraire` uniquement. Aucune lecture de `parcelle_valorisations` ni de la cible parcelle |
| Alertes applicatives (`AlerteService`, Aktor) | recherche `valorisation` dans `app/Services/AlerteService.php` et `app/Services/Assistant` | aucune occurrence. Les « alertes » valorisation sont les cases rouges et les cartes Supervision, pas une notification |

La liste et l’export envoient les montants sans retester `foncier.prix` dans `serialize` (`ProspectionController.php:399-404`) ni dans `ligneParcelle`. La fiche et le patrimoine masquent les montants sans ce droit (`ParcelleValorisationService.php:217-223` et `:401` ; `PatrimoineFoncierQuery.php:153-170`).

## 4. Commande tinker (lecture seule)

À coller dans `php artisan tinker`, pas dans un `--execute` entre guillemets PowerShell (`$` serait mangé). Aucun `!` (PsySH l’exécute comme un shell). `SELECT` uniquement. `deleted_at` est filtré explicitement (équivalent withTrashed : le scope SoftDeletes n’est pas utilisé). `GROUP BY` ne contient que la clé groupée.

```php
$actif = DB::selectOne("
SELECT
  SUM(CASE WHEN nb = 0 THEN 1 ELSE 0 END) AS p0,
  SUM(CASE WHEN nb = 1 THEN 1 ELSE 0 END) AS p1,
  SUM(CASE WHEN nb >= 2 THEN 1 ELSE 0 END) AS p2plus,
  COUNT(*) AS parcelles
FROM (
  SELECT p.id, COUNT(pv.id) AS nb
  FROM parcelles p
  LEFT JOIN parcelle_valorisations pv ON pv.parcelle_id = p.id
  WHERE p.deleted_at IS NULL
  GROUP BY p.id
) t
");
$corbeille = DB::selectOne("
SELECT
  SUM(CASE WHEN nb = 0 THEN 1 ELSE 0 END) AS p0,
  SUM(CASE WHEN nb = 1 THEN 1 ELSE 0 END) AS p1,
  SUM(CASE WHEN nb >= 2 THEN 1 ELSE 0 END) AS p2plus,
  COUNT(*) AS parcelles
FROM (
  SELECT p.id, COUNT(pv.id) AS nb
  FROM parcelles p
  LEFT JOIN parcelle_valorisations pv ON pv.parcelle_id = p.id
  WHERE p.deleted_at IS NOT NULL
  GROUP BY p.id
) t
");
$cible = DB::selectOne("
SELECT
  SUM(CASE WHEN valorisation_cible_eur_m2 IS NOT NULL THEN 1 ELSE 0 END) AS avec_cible,
  SUM(CASE WHEN valorisation_cible_eur_m2 IS NULL THEN 1 ELSE 0 END) AS sans_cible
FROM parcelles
WHERE deleted_at IS NULL
");
$sans = DB::selectOne("
SELECT
  SUM(CASE WHEN sans_valorisation IS NULL AND destination = 'retrocession' THEN 1 ELSE 0 END) AS auto_retrocession,
  SUM(CASE WHEN sans_valorisation = 1 THEN 1 ELSE 0 END) AS force_oui,
  SUM(CASE WHEN sans_valorisation = 0 THEN 1 ELSE 0 END) AS exige,
  SUM(CASE WHEN sans_valorisation IS NULL AND (destination IS NULL OR destination <> 'retrocession') THEN 1 ELSE 0 END) AS auto_inactif
FROM parcelles
WHERE deleted_at IS NULL
");
$actes = DB::selectOne("
SELECT
  SUM(CASE WHEN prix_numeraire > 0 THEN 1 ELSE 0 END) AS prix_positif,
  SUM(CASE WHEN prix_numeraire = 0 THEN 1 ELSE 0 END) AS prix_zero,
  SUM(CASE WHEN prix_numeraire IS NULL THEN 1 ELSE 0 END) AS prix_null,
  COUNT(*) AS actes
FROM actes_fonciers
");
$multi = DB::selectOne("
SELECT COUNT(*) AS parcelles_plusieurs_actes
FROM (
  SELECT ap.parcelle_id
  FROM acte_parcelle ap
  INNER JOIN parcelles p ON p.id = ap.parcelle_id AND p.deleted_at IS NULL
  GROUP BY ap.parcelle_id
  HAVING COUNT(DISTINCT ap.acte_foncier_id) > 1
) t
");
$exemplesActes = DB::select("
SELECT id, reference_acte, sens, prix_numeraire, date_acte
FROM actes_fonciers
WHERE id IN (63, 39, 38) OR reference_acte = '2003 D 14132'
ORDER BY id
");
$exemplesValo = DB::select("
SELECT p.id AS parcelle_id, p.section_cadastrale, p.numero, p.code_insee_commune,
       p.surface_cadastrale_m2, p.deleted_at,
       pv.id AS valo_id, pv.date_valorisation, pv.valeur, pv.methode
FROM parcelles p
INNER JOIN parcelle_valorisations pv ON pv.parcelle_id = p.id
INNER JOIN (
  SELECT parcelle_id
  FROM parcelle_valorisations
  GROUP BY parcelle_id
  HAVING COUNT(*) >= 2
  ORDER BY parcelle_id
  LIMIT 5
) multi ON multi.parcelle_id = p.id
WHERE p.deleted_at IS NULL
ORDER BY p.id, pv.date_valorisation DESC, pv.id DESC
");
dump([
  'actif' => $actif,
  'corbeille' => $corbeille,
  'cible' => $cible,
  'sans' => $sans,
  'actes' => $actes,
  'multi' => $multi,
  'exemples_actes' => $exemplesActes,
  'exemples_valo' => $exemplesValo,
]);
```

`valeur` des exemples est le total € stocké. `methode` est le code, pas une unité. Les parcelles en corbeille sont le second bloc (`deleted_at IS NOT NULL`). `actes_fonciers` n’a pas de `deleted_at`.

## 5. Conclusion factuelle

**Déjà là, réutilisable tel quel**

- Une cible unique par parcelle, en €/m², avec explication obligatoire : `valorisation_cible_eur_m2` + `valorisation_cible_explication`.
- Trois états « sans valorisation » + motif facultatif, automatique sur Rétrocession (`destination = retrocession` et colonne null).
- Le prix d’acte existe : `actes_fonciers.prix_numeraire`, total €, affiché « Prix d’achat » ou « Prix de vente ».
- Le lien N-N acte ↔ parcelles existe (`acte_parcelle`), avec une surface acquise facultative par ligne.
- La surface cadastrale est la surface déjà utilisée pour passer du total € au €/m².

**Absent du modèle visé**

- Pas de prix d’achat stocké ni calculé en €/m² sur la parcelle. Le « prix d’acquisition de référence » est le `prix_numeraire` entier de l’acte, recopié à l’identique sur chaque parcelle liée.
- Pas de division par la somme des surfaces des parcelles de l’acte. Aucun code ne choisit quelle surface sommer (cadastrale, arpentée, `surface_acquise_m2`).
- Pas de « prix estimé » unique. L’« actuelle » est la dernière ligne d’un historique de totaux €, avec une méthode (5 codes) et un commentaire facultatif, pas un motif obligatoire.
- Pas de prix déclaré, de frais ni d’ODF sur l’acte.

**Pièges de reprise (constat, pas une marche à suivre)**

- Unités mélangées : `valeur` = € totaux ; cible = €/m² ; liste Valo. affiche les € totaux ; fiche « Actuelle » affiche d’abord le €/m² calculé. Reprendre `valeur` comme un €/m² multiplierait ou diviserait une deuxième fois.
- Plusieurs lignes : la courante est `date_valorisation` desc, `id` desc. Une date plus ancienne avec un `id` plus grand ne gagne pas. Il n’y a pas de ligne « validée ». Les autres lignes, leurs documents et `entity_field_audits` restent.
- `valeur = 0` est une valorisation présente (la qualité « sans valorisation » ne la compte pas ; « à compléter » non plus, car une ligne existe).
- « Sans valorisation » effectif met les totaux liste et export à 0 sans supprimer la ligne. Supervision et Patrimoine continuent d’additionner cette `valeur`.
- Caster `sans_valorisation` en boolean transformerait le NULL automatique en false.
- Une parcelle peut avoir plusieurs actes. Fiche et Patrimoine ne choisissent pas le même : pivot du premier propriétaire actuel hors vente, contre acte de date la plus récente ventes comprises. `first()` sans tri si plusieurs détenteurs actuels.
- `prix_numeraire` à 0,00 € (actes « #63 », « #39 » tels qu’affichés) n’est pas NULL. La carte « Acte sans prix » ne les voit pas.
- La surface du €/m² actuel est la cadastrale seule. L’arpentée et `surface_acquise_m2` sont ignorées. Une cadastrale nulle ou 0 laisse le €/m² actuel vide alors que le total € existe (`comparer`, surface ignorée si elle retombe à 0).
- Mettre l’estimé ou l’achat comme nouvelle ligne de `parcelle_valorisations` le ferait élire « courante » dès que sa date est la plus récente (#1734, toujours vrai : `sousRequeteValorisationRecente` et `synthetiserPourParcelle` ne filtrent pas `methode`).

## DÉPLOIEMENT

Aucun. Fiche seule. Pas de migration, pas de `view:clear`, pas de `journal:sync`.

## LEÇON

La courante est la dernière `parcelle_valorisations.valeur` en euros (date, puis id), pas un prix au mètre carré et pas une ligne validée. La cible est déjà l’unique €/m². Le prix d’acte est `prix_numeraire` entier, non ventilé. Ne pas caster `sans_valorisation`. Ne pas confondre la carte qualité « pas de ligne » avec « À compléter ».
