---
suivi: 1652
date: 2026-09-22
sujet: Recalcul des totaux pendant la saisie (étape γ)
chantier: Foncier / Faisabilité
type: feature
statut: poussé
hash: 06d50f5c
fichiers:
  - resources/js/Pages/Foncier/Scenarios/Show.vue
  - resources/js/Components/Foncier/ScenarioKpiBar.vue
  - tests/Unit/ScenarioRecalculDirect1652SourceTest.php
  - tests/Unit/ScenarioGrilleInlineTva1635SourceTest.php
  - tests/Unit/FaisabiliteSaisieTableur1636SourceTest.php
  - tests/Unit/ScenarioGrilleTableur1642SourceTest.php
  - database/data/journal_mises_a_jour_post_2026_06_12.php
  - public/build/
  - docs/suivi/SUIVI_1652_recalcul_direct_pendant_la_saisie.md
---

# SUIVI #1652 — recalcul live pendant la saisie

## PROMPT ENVOYÉ

Étape γ du #1650. Brancher l’écran `/foncier/scenarios/{id}` sur l’endpoint
#1651 : totaux à jour pendant la saisie, sans enregistrer, sans formule JS.
Déclencheurs : Entrée (en plus de la descente clavier) et 2 s d’inactivité.
Tout recalculer. seq + AbortController. Estomper les calculés, jamais la
cellule en cours. Formatage fmtMontant existant. Échec non bloquant.

## FICHES LUES

- **#1651** (`45151664`, déployé) — contrat JSON, un run moteur, zéro
  persistance. Réponse validée en prod sur APS1 (scénario 4). Ne pas
  remettre l’endpoint en cause. KPI = mêmes clés que showDirect.
- **#1650** — debounce + seq ; JSON pas Inertia ; assiette hors graphe.
- **#1649** LEÇON — PrimeVue n’émet pas d’input DOM : watch sur le modèle
  (`ficheRows`, `lotRows`, `etudeSaisies`). RÉUTILISÉ, pas un second
  mécanisme.
- **#1648** LEÇON — dirty avant onSuccess ; PUT inchangés. Recalcul ne
  doit ni marquer ni démarquer une section.
- **#1645** — pas de `parcelles` dans le payload.
- **#1635/#1642/#1647** — Entrée descend, Tab à droite, Échap annule :
  CONSERVÉ, le recalcul s’y ajoute.

Anti-collision : 1651 est le dernier numéro. **1652 est libre.**
Show.vue en série (zones communes).

## SYNTHÈSE

### Déclencheurs
1. Les `watch` #1649 (`ficheRows` / `lotRows` / `etudeSaisies`) appellent
   encore `onSectionInput` (dirty #1648). `onSectionInput` AJOUTE
   `planifierRecalcul()` pour fiche/grille/étude. Identité et assiette :
   dirty seulement, pas d’appel (hors graphe).
2. `planifierRecalcul` : si empreinte saisie ≠ dernière appliquée, annule
   le timer et en pose un de **2000 ms**. Toute nouvelle saisie
   réinitialise.
3. `onCelluleEnter` / `onCelluleEnterTable` : **d’abord**
   `lancerRecalculImmediat()` (annule le timer, fetch maintenant), **puis**
   la descente de ligne inchangée (`focusCellule`).
4. InputNumber, Select, Checkbox : le watch modèle couvre tous les types ;
   Entrée des cellules tableur déclenche en plus l’immédiat.

### Séquence et annulation
Compteur `recalculSeq` incrémenté à chaque fetch, envoyé, renvoyé.
Réponse avec `seq` < `recalculSeqApplique` ignorée.
`AbortController` : nouvel appel `abort()` le précédent.
Les deux.

### Application de la réponse
Overlay `ficheLive` / `grilleLive` / `etudeLive` : totaux + colonnes
calculées uniquement (`FICHE_CLES_CALCULEES` / `LOT_CLES_CALCULEES`).
Les clés de saisie sont filtrées (`FICHE_CLES_SAISIE.includes` → skip).
`appliquerBlocsRecalcul` n’écrit ni `ficheRows` ni `lotRows` ni
`etudeSaisies` ni `sectionsModifiees`.
Affichage via `ficheCalculee` / `lotCalcule` / computeds `*Totaux`.
Formatage : `fmtMontant` / `fmtSurface` existants (2 décimales fr-FR).

### Estompés pendant l’attente (debounce OU fetch)
- Valeurs de la barre KPI collante
- Bande KPI grille et étude Fi
- Pied de totaux fiche et grille
- Ligne ventilations + BASE PR fiche
- Colonnes calculées fiche (mode, prix total, €/m²)
- Colonnes calculées grille (€/m², TVA, honoraires)
- Sous-totaux de section étude Fi, montant HT, unitaire/quantité calculés
Indicateur discret « Recalcul… ». Pas de clignotement, pas de vidage.

### Jamais rafraîchi par la réponse
Toute cellule saisie (InputNumber, Select, Checkbox, InputText),
`etudeSaisies`, identité, assiette, `sectionsModifiees`.

### Échec
422 / 500 / réseau : totaux inchangés, estompage off, message
`role="status"` non bloquant. Saisie et PUT #1648 possibles.

### Quand ne pas appeler
Gelé, pas le droit d’édition calculable, empreinte identique, chargement
(empreinte posée au mount), enregistrement (stopperRecalcul + props
remplacent l’overlay).

### PHP
Aucun fichier PHP applicatif modifié. Endpoint #1651 inchangé.

## LEÇON
Ne jamais merger la réponse moteur dans les refs de saisie : overlay
calculé + helpers de lecture. Réutiliser `onSectionInput` plutôt qu’un
second watch, sous peine de désynchroniser dirty et recalcul. Entrée =
immédiat + descente, pas l’un à la place de l’autre.
