---
suivi: 1277
date: 2026-08-11
sujet: CIE chargement dues — loading infini
chantier: depot-factures
type: fix
statut: poussé
hash: d22b3bfc
fichiers:
  - resources/js/Pages/DepotFactures/DepotFactureSaisieForm.vue
  - database/data/journal_mises_a_jour_post_2026_06_12.php
  - public/build/
  - docs/suivi/SUIVI_1277_cie_chargement_dues.md
---

# SUIVI #1277 — CIE : fin du `loading` infini des dues

## PROMPT ENVOYÉ

Fix front strict sur `DepotFactureSaisieForm.vue`. Serveur hors de cause (GET `cie-retenue-dues` 200, payload `dues` ×2, `modifiable: true` sur dépôt 1230 / Lahouari). Phases `idle|loading|ok|error` sans issue bloquante ; motifs toujours lisibles ; activation CIE sur `modifiable` serveur ; bandeau diag conservé (discret, permanent).

## SYNTHÈSE

### Cause du `loading` infini (OBJET 1)

| Piste | Verdict | Preuve |
|---|---|---|
| a) `cieDuesLoading=true` avant une garde | **ÉCARTÉE** (code #1276) | Loading posé après les early-return uuid / hors marché / marche\|fournisseur. |
| b) promesse jamais résolue / pas de `finally` | **PARTIELLE** | Un `finally` existait déjà (#1276) ; une promesse **pendante** (réseau / course) laisse quand même `phase=loading`. Pas de timeout ni d’abort. |
| c) mauvais mapping de clés | **ÉCARTÉE** comme cause du loading | Front lit déjà `data.dues`. Un mapping faux aurait donné `ok · 0 ligne(s)`, pas `loading`. |
| d) déclenchements concurrents du watch | **RETENUE** | `watch([marche_id, fournisseur_id, uuid], { immediate: true })` sans sérialisation. Un 2ᵉ appel remet `phase=loading` et `n=0` ; si cet appel ne se termine pas, l’UI reste figée alors que le 1ᵉr a pu réussir (ou l’inverse). Changement de marché nullifie puis restaure `fournisseur_id` → multi-appels. |
| e) exception JS antérieure | **NON VÉRIFIÉE à l’écran** (console Robin) | À contrôler si le fix timeout/abort ne suffit pas. |

**Correctif** : compteur `cieDuesRequestSeq` + `AbortController` (annule l’appel précédent) + timeout 20 s + `cieDuesLoading=true` uniquement après les gardes + `finally` qui force la sortie de `loading` + phases réduites à `idle|loading|ok|error` (plus de `skipped` ambigu).

### Activation case CIE (OBJET 4)

Après `phase=ok`, `cieCaseActivatable` exige `cieServerModifiable === true` (champ `modifiable` du JSON), **pas** `depotRetenuesModifiables`. Ce computed reste inchangé pour RG / CP / OPC / historiques → **convergence ultérieure** à prévoir (une seule notion « modifiable » pour les cinq retenues).

### Bandeau diag (OBJET 5)

Conservé, plus petit / gris (`text-[10px] text-[#94A3B8]`), **toujours visible** dès que `peutSaisirRetenuesMarche` (y compris `idle`) — contrainte Robin : ne doit pas disparaître.

### Mapping payload → front

| Serveur | Front |
|---|---|
| `dues[]` | `cieLignes` |
| `dues[].reste_du_ht` | `ligne.reste_du_ht` |
| `dues[].montant_calcule_ht` | `ligne.montant_calcule_ht` |
| `dues[].cumul_retenu_ht` | `ligne.cumul_retenu_ht` |
| `dues[].preleve_sur_depot_ht` | `ligne.preleve_sur_depot_ht` |
| `reste_total_ht` | (agrégat serveur ; front recalcule via somme `reste_du_ht`) |
| `taux_tva_marche` | `cieTauxTvaFromEndpoint` → TTC CIE |
| `modifiable` | `cieServerModifiable` → activation case |

### Phases

| Phase | Case | Motif |
|---|---|---|
| `idle` | grisée | hors marché / ids absents / motif idle |
| `loading` | grisée | chargement des retenues CIE… |
| `ok` (dues>0, modifiable) | activable | (vide) |
| `ok` (0 ligne) | grisée | aucun CIE dû pour ce fournisseur sur ce marché |
| `ok` (!modifiable) | grisée | dépôt non modifiable (payé, comptabilisé ou Sage) |
| `error` | grisée | échec chargement CIE (HTTP n) : … |

## DÉPLOIEMENT-TEST

1. `git pull origin main`
2. `php artisan optimize:clear` + `view:clear`
3. `php artisan journal:sync`
4. Tests manuels Robin (dépôt 1230 Lahouari) : bandeau `ok · 2 ligne(s)` ; case activable ; règle #1268 (2 dues → aucune pré-coche) ; survie prélèvement après 2ᵉ save (signaler si sync détruit).

**Vérification agent** : lecture de code + correctif ; **pas** de rejeu sur l’écran réel prod (usurpation Lahouari / dépôt 1230).

## LEÇON

Un état de chargement **sans issue** (pas de timeout, pas d’annulation des courses, phase `loading` orpheline) est une forme de **silence** : la case est grisée et le motif « chargement… » masque l’échec réel. Tout `loading` doit toujours retomber (`finally` + abort + filet).
