---
suivi: 1302
date: 2026-08-12
sujet: marche_retenues_etats_diag_unite_perimetre
chantier: marches_compta
type: diag
statut: poussé
hash: 09b99eb2
fichiers:
  - tools/diag/verif_unite_retenues_etats_1302.php
  - docs/suivi/SUIVI_1302_diag_unite_perimetre_retenues_etats.md
---

## PROMPT ENVOYÉ

PHASE 1 DIAG #1302 — dette 57 : MarcheRetenuesEtatsService compare retenues TTC à théoriques HT ; second défaut suspecté de périmètre (CP vs RG historique). Lecture seule : script tools/diag/ + fiche. Aucun fix app/.

## SYNTHÈSE

### Fiches lues
- `#1287` : états Retenue / En cours via FactureStatutService ; source unique pour onglet + Synthèse.
- `#1292` : conversion affichage Synthèse `ttcVersHt` / `tauxTvaMarchePct` — service états **non** modifié.
- `#1296` / `#1300` : CP « retenue » Synthèse ≠ cumul historique situations (facture historique sans référence).

### OBJET 1 — Inventaire consommateurs (17 lignes prod/tests utiles ; < 20 → analyse poursuivie)

| # | Appelant fichier:ligne | Méthode | Écran / usage | Valeur | Unité attendue appelant | Impact si unité service → HT |
|---|------------------------|---------|---------------|--------|-------------------------|------------------------------|
| 1 | `MarcheController.php:666` | `pourMarche` | Show → props `retenues` + `alertes` | cumuls RG/CP/OPC/CIE/finitions, alertes | sémantique HT (comparé à théorique HT) | **corrige** l’onglet Retenues & Pénalités |
| 2 | `MarcheController.php:776-777` | `pourMarche` → `detail_rg` / `detail_cp` | Show listes lignes | `montant_retenu` / `montant_preleve` | affiché comme € retenue (HT implicite) | lignes passent en HT → OK si cohérent |
| 3 | `MarcheController.php:700` | `pourMarche` passé à synthese | Synthèse financière | mêmes cumuls bruts | **déjà convertis** `#1292` | **À RISQUE — double ÷ TVA** si service devient HT sans retirer `ttcVersHt` |
| 4 | `MarcheSyntheseFinanciereService.php:110` | `pourMarche` (si pas d’états) | Synthèse | idem | HT via `ttcVersHt` | **À RISQUE — double conversion** |
| 5 | `MarcheSyntheseFinanciereService.php:628+` | consomme `$etats` | cumuls haut + alertes HT | retenu/en cours | HT | neutre **si** on retire la conversion après fix service |
| 6 | `MarcheSyntheseFinanciereService.php:460,792` | `lignesComptablesOntMontantNonNul` | garde divergence | bool | n/a | **neutre** |
| 7 | `Show.vue` ~1769-1807, 4137+ | props `retenues` | onglet Retenues & Pénalités | montants + alertes | HT métier | devient correct après fix service |
| 8 | `MarcheSyntheseFinanciere.vue` | props `synthese` | Synthèse + alertes | déjà HT `#1292` | HT | **cassé** si double conversion (via #3/#4) |
| 9 | `MarcheCouvertureKpiService.php:91-125` | **n’appelle pas** MRES ; mêmes sources TTC | frise KPI header | `rgHt`/`cpHt`… mal nommés | prétend HT | **À RISQUE / frise** — resterait TTC si seul MRES corrigé |
| 10 | `CompteProrataService::collecteMarche` | appelé par MRES | source CP retenu | crédits CP | TTC stockés | conversion centralisée côté MRES OK |
| 11 | `ComptabilisationController.php:883` | `upsertDepuisComptabilisation` | écriture RG | `montant_retenu` TTC | TTC écritures | **ne pas convertir à l’écriture** dans le lot unité lecture |
| 12 | `DepotFactureController.php:2666` | `upsertHistorique` | RG historique | TTC reconstituté | TTC | neutre lecture |
| 13 | `MarcheRapprocherFactureService.php:120` | `resynchroniserMarche` | rapprochement | sync table | TTC | neutre |
| 14 | `MarcheDeplacerFactureService.php:106` | `resynchroniserMarche` | déplacement | sync | TTC | neutre |
| 15 | `FactureDecomptabilisationService.php:136` | `supprimerPourFacture` | décompta | delete | n/a | neutre |
| 16 | `CollecterRetenuesGarantieHistoriqueCommand` | `upsertHistorique` | artisan | écriture | TTC | neutre |
| 17 | `RetenueGarantieBackfillCommand` | upsert/supprimer | artisan backfill | écriture | TTC | neutre |

`BilanFinancierService` : **aucun** appel direct. Tests unitaires = stubs (hors inventaire risque affichage).

**Appelants à risque explicites si correction unitaire sans adaptation :**
1. `MarcheSyntheseFinanciereService` (`ttcVersHt` sur cumuls déjà HT) → Synthèse + alertes Synthèse.
2. `MarcheCouvertureKpiService` (frise) si on ne le touche pas : écart frise vs onglets HT.

### OBJET 2 — Unité par source (preuve marché 109 = arithmétique ticket + `#1292` ; DB locale stub)

| Grandeur | Source code | Unité réelle | Preuve 109 | Conv. → HT affichage |
|----------|-------------|--------------|------------|----------------------|
| RG retenu / en cours (table) | `retenues_garantie.montant_retenu` | **TTC** | 8720 → ÷(1+tva%) ≈ 7266,67 ≈ Synthèse 7266,61 | oui |
| RG/CP/OPC/CIE/finitions saisie | `computeRetenuesGlobales` | **TTC** | CIE 409 → ≈ 340,83 ≈ 340,50 | oui |
| CP retenu (Sage) | crédits GL via `CompteProrataService` | **TTC** | 2162 → ≈ 1801,67 ≈ 1801,40 | oui |
| `montant_cie` / `retenue_finitions_montant` | `ocr_raw.saisie` | **TTC** | aligné `#1292` | oui |
| Théorique RG/CP/OPC | taux × `montant_total_ht` | **HT** | RG theo 10080 | non |
| Théorique CIE | `cie_repartitions.montant_calcule_ht` (+ soft-delete ops) | **HT** | natif | non |
| Pénalités | `penalites.montant_ht` | **HT** | natif | non |
| `depot_factures.montant_ht` | colonne | **HT** | natif | non |

Aucune preuve DB locale (tables marches / retenues_garantie absentes) — **RESULTAT 0 NE VALIDE RIEN** pour inventaire exhaustif production ; arithmétique ticket confirme le facteur TVA marché (pas 1,2 en dur dans le script : formule `(1+taux/100)`).

### OBJET 3 — Périmètre factures (marché 109, code)

Règle `#1287` (`FactureStatutService::etatRetenue`) : payée → Retenue ; NV1+ non payée → En cours ; rejetée / avant NV1 → null. **Le service l’applique** pour RG (table + saisie), OPC/CIE/finitions (`ventilerCle`), pénalités.

| Retenue | Cumul « retenu » | Critère inclusion | Hist. « sans référence » |
|---------|------------------|-------------------|--------------------------|
| **RG** | Σ `retenues_garantie.montant_retenu` si etat=retenue (+ legacy sans dépôt) | table RG (Sage + `upsertHistorique`) filtrée `etatRetenue` | **incluse** (ligne historique dans `retenues_garantie`) |
| **CP** | `collecteMarche['provisionne']` | dépôts **avec** `intacct_bill_id`, crédit CP, payés (`depotEstProvisionne`) | **exclue** : pas de bill Sage → hors `depotsComptabilisesMarche` ; MRES n’ajoute **pas** l’historique CP auto/manuel au retenu onglet |
| **OPC / CIE / finitions** | saisie `computeRetenuesGlobales` sur dépôts `horsRejetEtArchive` avec etat non null | `etatRetenue` + montant saisie | selon présence saisie / etat |
| **Pénalités** | `penalites.montant_ht` + etat dépôt | etatRetenue | selon lien dépôt |

**Défaut de périmètre confirmé (code + `#1296`/`#1300`) :** CP retenu ≠ périmètre RG retenu sur l’historique ; indépendant de l’unité. Cumul historique situations (`historiqueSituationsHt`) peut donner CP ≈ 3 633,31 HT vs CP retenu états ≈ 1 801,40 HT.

### OBJET 4 — Script

`tools/diag/verif_unite_retenues_etats_1302.php` — lecture seule, sections 1–5, soft-delete CIE notés, pas de ×1,2 en dur.

Exécution locale : stub `erp_immo` → sections 2–5 données **RESULTAT 0 NE VALIDE RIEN**. Chiffres fausses alertes / anomalies réelles (MCH-2026-001, 033, 064, 034, ARBOREA) **à produire en prod** via le même script.

### OBJET 5 — Recommandation (pas de code)

1. **Où corriger :** dans `MarcheRetenuesEtatsService` (conversion centralisée TTC→HT pour RG/CP/OPC/CIE/finitions et alertes), en réutilisant `MarcheSyntheseFinanciereService::ttcVersHt` / `tauxTvaMarchePct` (extraire helper partagé si besoin) — **pas** un second facteur magique.
2. **Adapter en même temps :** retirer (ou no-op) la couche `#1292` sur les cumuls issus du service ; sinon **double conversion**. Frise `MarcheCouvertureKpiService` : lot **immédiatement suivant** (mêmes sources TTC) — sinon KPI header incohérent.
3. **Neutres :** écritures `RetenueGarantieService` (garder TTC en table) ; `lignesComptablesOntMontantNonNul` ; Bilan (pas d’appel).
4. **Périmètre CP :** lot **séparé** après unité (sinon mélange de causes dans les tests métier).
5. **Découpage PHASE 2 :** (A) fix unité + alertes dans MRES + débranchement `ttcVersHt` Synthèse sur ces cumuls + tests Show/Synthèse alignés ; (B) frise CouvertureKpi ; (C) périmètre CP (inclure historique dans retenu ou documenter écart volontaire) ; (D) re-run diag prod pour anomalies réelles restantes.

## DÉPLOIEMENT-TEST

```bash
git pull origin main
php -d display_errors=1 -l tools/diag/verif_unite_retenues_etats_1302.php
php -d display_errors=1 tools/diag/verif_unite_retenues_etats_1302.php
```

Sur OVH (prod) uniquement le script — **pas** de npm/composer. Coller la sortie brute sections 3–5 dans le lot PHASE 2.

## LEÇON

Corriger l’unité à l’affichage d’un seul onglet (`#1292`) laisse le service source et la frise en TTC : deux écrans, deux vérités. Un appelant qui convertit déjà casse si le service bascule en HT sans adaptation. Le périmètre CP (Sage-only) n’est pas un bug d’unité : le traiter dans le même commit masquerait la cause.
