---
suivi: 1253
date: 2026-08-07
sujet: Hotfix migration soft delete parcelles — index support FK
chantier: foncier / parcelles
type: hotfix
statut: poussé
hash: 51886a8a
fichiers:
  - database/migrations/2026_08_07_170000_add_soft_deletes_to_parcelles_table.php
  - database/data/journal_mises_a_jour_post_2026_06_12.php
  - docs/suivi/SUIVI_1253_hotfix_migration_soft_delete_parcelles.md
---

# SUIVI #1253 — Hotfix migration soft delete parcelles

## PROMPT ENVOYÉ

Migration #1249 bloquée en prod (erreur 1553) : impossible de dropper
`parcelles_commune_section_numero_uq` car il sert d'index de support à la FK
`parcelles_commune_fk`. État partiel (DDL commit implicite). Corriger le fichier
existant (idempotent), voie (a) index de support, pas de migration de
rattrapage, pas de dé-enregistrement.

## FICHES LUES

- `#1249` — soft delete + `parcelle_vivante` STORED ; migration échouée mid-flight
- `#1099` — même motif 1553 sur `pp_parcelle_proprio_uq` ; créer index support
  AVANT `dropUnique` ; ne pas lever la FK
- `#1247` — DDL avant données, bindings, preuve = Pending=0
- `#1096` — pattern `detention_courante` STORED dans UNIQUE (déjà en prod MySQL 8.4)

## CAUSE RACINE

`parcelles_commune_fk` (`code_insee_commune` → `communes.code_insee`) s'appuie
sur le seul index dont la **première colonne** est `code_insee_commune` :
l'unique composite. MySQL refuse le `drop index` (1553). Les 13 FK entrantes
vers `parcelles.id` ne sont pas en cause.

## ÉTAT PARTIEL PROD (avant hotfix)

| Élément | État |
|---|---|
| `deleted_at` | présente |
| `deleted_by` | présente + FK users |
| `parcelle_vivante` | absente |
| `parcelles_deleted_at_idx` | présent |
| `parcelles_commune_section_numero_uq` | encore présent |
| migration | Pending (rejouera) |

## SOLUTION — voie (a)

Créer `parcelles_code_insee_commune_idx` sur `code_insee_commune` **avant**
`dropUnique`. MySQL bascule le support FK. Aucune contrainte levée (plus sûr
que (b) drop/recreate FK). L'index commune est utile aux filtres Prospection.

## IMPACT ÉCRANS (état partiel)

- SoftDeletes opérationnel (`deleted_at` existe) → corbeille / restore / exclusion listes OK.
- `parcelle_vivante` absente mais **jamais lue** par le code applicatif.
- Colonne code = **`deleted_by`** (alignée base) — pas `deleted_by_id`. Aucun correctif appli.
- Risque résiduel : unicité globale encore active → une 2ᵉ vivante de même réf.
  cadastrale (si contournement du contrôle appli) serait refusée par MySQL ;
  le flux nominal propose la restauration via `findTrashedDuplicate`.
- Aucun écran 500 constaté / attendu du seul manque de `parcelle_vivante`.

## RÈGLE À REPORTER AU PROJECT.md

> Avant tout `dropUnique` / `dropIndex`, vérifier qu'aucune FK ne s'appuie sur
> cet index — en créer un de remplacement AVANT la suppression.

## LEÇON

Récidive #1099 : une migration testée sans FK réelles ne prouve rien. Sur MySQL
InnoDB, un unique composite dont la 1ʳᵉ colonne couvre une FK **est** l'index
de support. Toujours créer l'index de remplacement **avant** le drop.

## DÉPLOIEMENT

```
git pull origin main
php artisan migrate --force
php artisan migrate:status | grep -c Pending   # → 0
php artisan optimize:clear
php artisan view:clear
php artisan journal:sync
```

Puis tests métier 6–9 (corbeille, recrérer même réf., deux soft-deleted).
