---
suivi: 1343
date: 2026-08-14
sujet: diag_reprise_bilan
chantier: marches_reprise
type: diag
statut: poussé
hash: 1ccf1c77
fichiers:
  - tools/diag/diag_reprise_bilan_1343.php
  - docs/suivi/SUIVI_1343_diag_reprise_bilan.md
---

## PROMPT ENVOYÉ

PHASE 1 DIAG #1343 — lecture seule. Inventaire des consommateurs financiers
marché/programme vs marche_reprise_lignes ; structure réelle ; écart
frise vs bilan ; risque double comptage reprise ∩ saisie historique ;
options d architecture chiffrees. Aucun fix (fix = #1344).

## FICHES LUES

- #1323 LEÇON : colonne HT sans branchement MRES ne change rien a l ecran
  Retenues. Dette ouverte : bilan / MRES / soft delete.
- #1326 LEÇON : dettes inchangees (MRES ignore reprises, bilan ignore
  reprises, pas de soft delete). Cette formulation est partiellement
  caduque — voir OBJET 1 / contradiction notice vs code.
- #1324 : memes dettes ; staging Sigma facture_ht / regle_ttc / rg_retenue_ht.
- #1295 LEÇON : correction partielle → deux chiffres sur le meme ecran
  (bandeau/frise vs Synthese). Risque n°1 du futur #1344.
- #1219 LEÇON : deux soldes (frise live vs reste_a_regler_ht cache) ;
  recalculer ecrit ; inversion d ordre ~ 149 kEUR de double comptage.
- #1320 LEÇON : plafond liberation = etats.rg.montant_retenu (MRES).
- #1342 : footer liste marches somme les lignes (live, avec reprise) ;
  KPI haut = autre formule. MySQL local 127.0.0.1:3306 refuse — meme
  constat ici.

Aucune fiche n inventoriait tous les consommateurs. #1323 a greppé
BilanFinancierService pour marche_reprise_lignes (zero hit) et en a
conclu « bilan ignore ». Le chemin reel est indirect
(repartitionKpiMarche → totalHtDepotsMarche → reprise).

## SYNTHÈSE

Aucun fichier app/ modifié. Script lecture seule + fiche.

php -l (sortie brute) :

    No syntax errors detected in tools/diag/diag_reprise_bilan_1343.php

MySQL local refuse (127.0.0.1:3306) — OBJET 2 COUNT / OBJET 3 ecarts /
OBJET 4 intersection = a coller depuis OVH. Commande :

    cd /home/hectare-envol/envol.hectare.fr && php -d display_errors=1 tools/diag/diag_reprise_bilan_1343.php

### OBJET 1 — Tableau des consommateurs

Scan runtime app/ + resources/js/ + routes/ : 46 occurrences, aucun
autre lecteur de marche_reprise_lignes / MarcheRepriseLigne /
MarcheRepriseMontantsService hors la chaine ci-dessous + CRUD SIWI.

Point unique de lecture agregee : MarcheRepriseMontantsService
(SUM montant_facture_ht / montant_paye_ht seulement — les colonnes
RG/CP/CIE/finitions reprise ne sont sommees nulle part hors l onglet
Reprise).

consommateur | fichier:ligne | source lue | lit marche_reprise_lignes | ecran(s)

MarcheRepriseMontantsService | app/Services/MarcheRepriseMontantsService.php:14 | SUM montant_facture_ht, montant_paye_ht | OUI | interne

MarcheMontantsAgregesService | app/Services/MarcheMontantsAgregesService.php:198 | delegue le precedent | OUI | interne

MontantFactureSousTraitantService::totalHtDepotsMarche | app/Services/MontantFactureSousTraitantService.php:189 | depot_factures + reprise (facture_ht ou paye_ht selon filtre statuts) | OUI (indirect) | voir lignes suivantes

MarcheController::transformMarcheForShow | MarcheController.php:2139-2153 | repartitionKpiMarche + reprisePourMarche | OUI | payload fiche (facture_ht / paye_ht)

MarcheController::buildMarcheKpiHeaderPayload | MarcheController.php:1936 | factureBrutHt deja enrichi → MarcheCouvertureKpiService | OUI facture/paye ; NON RG/CP/CIE/finitions reprise | bandeau KPI + frise (UI masquee #1313 SHOW_MARCHE_KPI_COUVERTURE_HEADER=false ; payload toujours calcule)

MarcheCouvertureKpiService::calculer | MarcheCouvertureKpiService.php:65 | factureBrutHt (deja repris) + retenues_garantie.montant_retenu + CompteProrataService + ocr_raw depots | OUI facture (indirect) ; NON colonnes *_reprise_ht | frise couverture

MarcheResumeWidgets | MarcheResumeWidgets.vue:113 | props.retenues = MRES | NON | onglet Synthese, bloc Retenues a date

MarcheSyntheseFinanciereService::pourMarche | MarcheSyntheseFinanciereService.php:91 / historiqueFactures:408 | depot_factures.montant_ht + MRES | NON | onglet Synthese totaux + historique situations

MarcheRetenuesEtatsService::pourMarche | MarcheRetenuesEtatsService.php:37 | depot_factures + retenues_garantie + CompteProrataService | NON | onglet Retenues et Penalites

DemandeLiberationRgService::plafondPourMarche | DemandeLiberationRgService.php:47 | etats.rg.montant_retenu (MRES) | NON | plafond liberation RG (#1320) + page LiberationRg

BilanFinancierService::buildMarchesKpiProgramme facture_valide | BilanFinancierService.php:1151 | repartitionKpiMarche(PAYE+VALIDE) = depots payes + reprise paye_ht | OUI (indirect, paye seulement) | Bilan programme colonne reel / facture valide + rubriques

BilanFinancierService::buildFactureNv1ByMarche | BilanFinancierService.php:1245 | SUM depot_factures.montant_ht SQL NV1 (saisie_historique incluse) | NON | Bilan colonne facture en attente

BilanFinancierService detail marche | BilanFinancierService.php:1130 | marches.total_facture_ht / reste_a_regler_ht caches | NON (sauf si recalculer a tourne apres la reprise) | Bilan, lignes marche depliees

Marche::recalculer | Marche.php:317 | totauxCachePourMarche (depots+reprise) | OUI si appele | colonnes marches.* — ECRIT ; store/update reprise et SiwiRepriseActionService ne l appellent pas

MarcheController::computeKpis | MarcheController.php:2978 | SUM depot_factures.montant_ht du programme | NON | KPI haut /programmes/{id}/marches

MarcheController::transformMarche (liste) | MarcheController.php:3155 | totauxHtDepotsListeMarches (enrichis reprise) | OUI | lignes tableau + footer TOTAL #1342

ProgrammeDashboardController | ProgrammeDashboardController.php:65 | DepotFacture::sommeMontantHtFactureApresNv1PourProgramme | NON | dashboard programme facture

ProgrammeDashboardPresenter slice bilan | ProgrammeDashboardPresenter.php:70 | getBilan (ecrit via refresh) | OUI reel (indirect) | slice previsionnel dashboard — ne pas appeler en diag

FactureController / DepotFactureController avancement | FactureController.php:767 / DepotFactureController.php:3538 | kpiAvancementMarche = totalHtDepotsMarche | OUI | fiche facture + depot, reste sous plafond #1295

CompteProrataService / CieService | services homonymes | tables CP/CIE + depots | NON | onglets Compte prorata / CIE programme

Export bilan / marches | aucun export CSV/XLSX des montants bilan marche dans app/ | — | — | aucun

Onglet Reprise SIWI (CRUD) | MarcheRepriseLigneController + Show.vue:4439 | table directe | OUI (affichage lignes, y compris RG) | onglet Reprise

Notice UI | Show.vue:4442 | texte n alimentent pas le bilan | contredit le code de facture_valide | fiche marche

Contradiction a ne pas trancher sans Robin (signalee, pas corrigee) :

1. Meme fiche marche : frise/payload facture_ht inclut reprise ;
   Synthese totaux.total_facture_ht ignore reprise. Deux chiffres
   des qu une ligne existe — recidive #1295, deja en place, bandeau
   masque #1313 donc moins visible mais payload + Synthese cohabitent.
2. Notice + fiches #1323/#1326 : le bilan ignore les reprises.
   Code buildMarchesKpiProgramme : colonne reel / facture valide
   = repartitionKpiMarche(PAYE+VALIDE) = depots payes + montant_paye_ht
   reprise. La notice est fausse pour cette colonne si paye_ht > 0.
   La colonne en attente (NV1) ignore bien la reprise.
3. Liste marches : KPI haut sans reprise ; lignes / footer #1342
   avec reprise.
4. montant_rg_reprise_ht n entre ni dans MRES, ni dans la
   frise provisionnee, ni dans le plafond #1320. C est le risque
   liberer une RG que le systeme croit n avoir jamais prelevee —
   confirme par le code, independamment des volumes OVH.

MarcheRepriseMontantsService ne lit pas les 4 colonnes retenues
reprise. Un fix qui n etend pas ce service laissera encore la RG
invisible.

### OBJET 2 — Structure reelle

Migrations (a confirmer SHOW COLUMNS OVH) :

id bigint PK
marche_id FK marches NOT NULL cascade
fournisseur_id FK fournisseurs NULL nullOnDelete
libelle varchar NOT NULL
montant_facture_ht decimal(14,2) NOT NULL default 0 — HT (nom + defaut 0)
montant_paye_ht decimal(14,2) NOT NULL default 0 — HT
montant_rg_reprise_ht decimal(14,2) NULL — HT (#1323)
montant_finitions_reprise_ht decimal(14,2) NULL — HT
montant_cp_reprise_ht decimal(14,2) NULL — HT
montant_cie_reprise_ht decimal(14,2) NULL — HT
date_reference date NULL
commentaire text NULL
created_by FK users NULL
timestamps
PAS de deleted_at (soft delete toujours absent)

Unite : pas de melange TTC/HT dans cette table. Toutes les colonnes
monetaires sont HT (suffixe + commentaire migration #1323).
Determination : SIWI ecrit facture_ht / rg_retenue_ht (deja HT) et
convertit regle_ttc → HT via SiwiTauxTva::ttcVersHt (#1336) pour
montant_paye_ht. Taux = siwi_marches.taux_tva (pas le defaut 20 du
helper marche).

Piege n°1 du projet (retenues_garantie.montant_retenu stocke TTC, MRES
convertit a la lecture #1304) : si #1344 additionne montant_rg_reprise_ht
(deja HT) dans le meme seau avant conversion, ou reconvertit la RG
reprise, facteur 1,2. Le dire explicitement dans le lot fix.

COUNT + lignes + montants : sortie OBJET 2 du script OVH a coller ici.
Local : 0 (MySQL refuse).

Staging annonce dans le prompt (Sigma facture_ht 50 607 706,26 EUR ·
Sigma regle_ttc 54 621 180,88 EUR · Sigma rg_retenue_ht 150 400,48 EUR) :
le script imprime aussi siwi_marches si la table existe — coller pour recouper.

### OBJET 3 — Ecart actuel (formules ; chiffres = OVH)

Ne pas appeler getBilan / recalculer. Formules du script :

- frise_facture_ht = totalHtDepotsMarche(null) = depots hors rejet + reprise facture_ht
- frise_paye_ht = totalHtDepotsMarche(PAYE+VALIDE) = depots payes + reprise paye_ht
- synthese_facture_ht = MSFS totaux.total_facture_ht (depots seuls)
- bilan_reel proxy = frise_paye_ht (meme source que facture_valide)
- bilan_nv1 = SUM depot_factures.montant_ht NV1 sans reprise
- cache = marches.total_facture_ht (stale si pas de recalculer)
- mres_rg = etats.rg.montant_retenu ; comparer a SUM montant_rg_reprise_ht

Attendu si une ligne existe avec facture_ht = F et paye_ht = P :

- ecart frise vs Synthese ≈ F (defaut deja en prod de code)
- ecart notice vs bilan reel ≈ 0 sur la colonne valide si P est bien
  additionne — la notice surestime le trou du bilan reel et
  sous-estime le trou Synthese / KPI liste / dashboard / NV1 / RG
- ecart MRES vs RG reprise = toute la RG reprise est invisible

Chiffres marche par marche : coller OBJET 3 OVH. Local : section sautee.

### OBJET 4 — Double comptage (regle, pas d implementation)

Distinction fiable :

- reprise = presence de ligne(s) marche_reprise_lignes.marche_id
- historique = depot_factures.saisie_historique = 1 OU
  type_source = historique (constante TYPE_SOURCE_HISTORIQUE)
- pas de FK depot ↔ reprise → pas de rapprochement ligne a ligne

Le bilan NV1 compte deja les saisies historiques
(sqlConditionCompteDansFactureApresNv1Valide). La frise aussi
(agregat depots). Donc aujourd hui : historique seul = 1 fois (OK).
Reprise seule, colonne reel = 1 fois via paye_ht (si P>0).
Reprise + historique sur le meme marche : frise et bilan reel
additionnent les deux → double comptage deja possible sans #1344.
Un fix qui ajoute la reprise au bilan sur la colonne reel
recompterait P une troisieme fois si on n y prend garde.

Regle d exclusion mutuelle candidate (arbitrage Robin, #1344) :

- si le marche a au moins une ligne de reprise, exclure les depots
  historiques du facture/paye (reprise = solde SIWI), ou
- interdire saisie historique tant qu une reprise existe (et
  reciproquement), ou
- n ajouter au bilan que facture_ht − paye_ht (impaye repris),
  jamais re-additionner paye_ht deja dans facture_valide.

Combien de marches dans ce cas : coller OBJET 4 OVH. Local : saute.

Rappel #1219 : meme famille de risque (deux sources, un solde).

### OBJET 5 — Options (pas de choix)

A — Lecture directe dans les services manquants, via
MarcheRepriseMontantsService etendu (facture/paye + rg/cp/cie/finitions).
Fichiers : MRES, MSFS, CouvertureKpi (provisionne/capte),
MarcheController::computeKpis, ProgrammeDashboardController,
BilanFinancierService::buildFactureNv1ByMarche, notice UI, et garde-fou
anti double paye_ht sur facture_valide. Exclusion historique au niveau
marche. Migration : non. Perf : 1 SUM/marche (deja le cas frise).
Risque : #1295 si un consommateur OBJET 1 est oublie — d ou le tableau.
Writer : appeler un refresh lecture (pas forcement recalculer) apres
CRUD reprise.

B — Vue SQL UNION depots HT ∪ lignes reprise.
Migration : CREATE VIEW. Tous les SUM depot_factures devraient passer
par la vue. Piege : reprise sans statut NV1 / sans saisie_historique /
sans facture_id. RG reprise n est pas une ligne de depot → la vue
ne couvre pas MRES / plafond #1320. Risque 1052 + only_full_group_by.
Perf : vue reevaluee a chaque bilan programme (170 → 1 128 marches).

C — Colonnes agregees materialisees sur marches
(facture_ht_reprise, paye_ht_reprise, rg_reprise_ht, …).
Migration : oui. Writer unique : MarcheRepriseLigneController +
SiwiRepriseActionService — aujourd hui ils n appellent pas
recalculer : a fermer dans le meme lot sinon cache mort.
Risque : stale (famille reste_a_regler_ht #1219). Perf : meilleure
pour le bilan. MRES n est couvert que s il lit aussi ces colonnes.

Recommandation non tranchee. Pour #1344, le piege immediat n est pas
oublier le bilan : c est ajouter paye_ht une seconde fois dans
facture_valide, et oublier MRES / plafond RG / Synthese / KPI liste /
dashboard (cinq ecrans encore sourds sur au moins un axe).

## DÉPLOIEMENT-TEST

    git pull origin main
    php artisan optimize:clear
    php artisan view:clear

Pas de migrate. Pas de journal:sync. Pas de route:clear.

    cd /home/hectare-envol/envol.hectare.fr && php -d display_errors=1 tools/diag/diag_reprise_bilan_1343.php

Coller la sortie OBJET 2-4 dans cette fiche (lot hash ou commentaire).
Aucun test ecran (diag). Aucun fix.

## LEÇON

Un grep marche_reprise_lignes dans BilanFinancierService est silencieux
et faux : le bilan reel passe par repartitionKpiMarche qui enrichit
deja montant_paye_ht. Inventaire = suivre les fonctions
(totalHtDepotsMarche / computeKpis / SQL NV1 / MRES), pas le nom de
table. Une notice utilisateur perimee est une source de double comptage
au lot suivant. Les colonnes RG reprise HT sans lecteur MRES sont
invisibles au plafond de liberation — le dire avant d importer 1 128
marches.
