---
suivi: 1223
date: 2026-08-05
sujet: diag flags retenues poste × dépôt (fiabilité + volumes)
chantier: depot-factures
type: diagnostic
statut: poussé
hash: 8ab6d37f
fichiers:
  - tools/diag/diag_retenues_flags_poste_1223.php
  - docs/suivi/SUIVI_1223_diag_retenues_flags_poste.md
---

## PROMPT ENVOYÉ

SUIVI #1223 PHASE 1 DIAGNOSTIC — retenues conditionnées par poste/rubrique :
fiabilité des flags + volumes à risque. Lecture seule. Risque B résolu par Robin
(zéro uniquement sur dépôts non validés ; pas d’arbitrage Sabrina/Rudy).

HEAD base = `8ad3a9d4` (#1221).

## FICHES LUES

- `SUIVI_1023` — `lie_aux_marches` / `lie_aux_factures` LEGACY NON FIABLE ; flags
  retenues sur `postes_budgetaires_types` ; ne pas s’en servir pour piloter.
- `SUIVI_1191` / `1193` — `peutSaisirRetenuesMarche` + `assertAucuneRetenueHorsMarche` ;
  mesure avant d’imposer (#1191 poste obligatoire → révoqué #1193 car 40/40 sans poste).
- `SUIVI_1189` — `workflow_modification` / frontière modifiable.
- `SUIVI_1184` — eager load partiel = donnée invisible sans erreur.
- `SUIVI_1188` — Schema::hasColumn avant chaque accès diag.

## OBJET 1 — Flags : schéma et fiabilité

### Niveau réel
Table **`postes_budgetaires_types`** (modèle `PosteBudgetaireType`).
Colonnes booléennes (migration `2026_06_22_130000`, **default true** + backfill
tous à 1) :
`applicable_retenue_garantie`, `applicable_caution_bancaire`,
`applicable_compte_prorata`, `applicable_opc`, `applicable_cie`.
Aussi : `lie_aux_marches`, `lie_aux_factures` (legacy), `est_taxe`,
`est_concessionnaire`, `applicable_marche`, `applicable_budget` (#1023).

**Pas** sur `postes_budgetaires` (instance programme, UNIQUE programme×poste_type)
ni sur `rubriques_bilan`.

### Usage déjà en place (marchés)
- `MarcheController::posteRetenuesApplicabilitePourMarche` + Show/Create
  (`useMarcheRetenuesParPoste`) : masquage UI + `normaliserRetenuesSelonApplicabilitePoste`
  force % à 0 à la création si flag faux.
- Défaut permissif si poste non résolu (tout à true).

### Verdict (chiffres = sortie script OVH — coller ici)
Critères script §3 :

| flag | à 1 | à 0/NULL | used flag=0 | flag=1 jamais | verdict |
|------|-----|----------|-------------|---------------|---------|
| *(à coller après `php tools/diag/diag_retenues_flags_poste_1223.php`)* | | | | | |

Règle : **NON FIABLE** si used_flag0 > 0 → ne pas piloter un masquage dépôt.
**NON DISCRIMINANT** si tout reste à 1 (défaut migration) → masquage sans effet
tant que Robin n’a pas coché les non-applicables.
`lie_aux_*` : **NON FIABLE** (déjà #1023), hors périmètre masquage retenues.

## OBJET 2 — Volumes à risque (INFORMATIF)

Décision Robin : zéro + masquage **uniquement** dépôts non encore validés ;
validés / comptabilisés / payés = lecture seule « (historique) ».
→ **Aucun arbitrage Sabrina/Rudy**.

Script §4 liste les dépôts avec retenue ≠ 0 alors que flag poste = 0/NULL,
ventilés `brouillon_ou_saisie` / `en_validation` / `historique_ou_cloture`.
§5 : marchés avec % ≠ 0 sans flag.

**Ne corriger aucune donnée.**

Frontière exacte encore ouverte (question Robin) :
- **A** = brouillon / saisie seulement ;
- **B** = tant que modifiable au sens `workflow_modification` (#1189).

## OBJET 3 — Taux par défaut (volet 2)

| Colonne marché | Type / défaut migration | Remplissage UI Create |
|----------------|-------------------------|------------------------|
| `retenue_garantie_pct` | decimal(5,2) default **5.00** | 5 |
| `compte_prorata_pct` | decimal(5,2) default **1.50** | 1.5 |
| `taux_opc_pct` | decimal (ajout #juin) default 0 | 0 |
| CIE / finitions | **pas de % marché** | montants dépôt |

Prérenseignement dépôt : **déjà partiel** —
`DepotFactureSaisieForm.applyRetenuesPrefillDepuisMarche` copie RG/CP/OPC depuis
le marché sélectionné (taux > 0 → coche + remplit ; sinon décoche + 0).
Cas flag applicable + marché à 0 % : panneaux restent fermés (comportement actuel).
Cas flag non applicable + % marché > 0 : écart possible (objet 2/5) — dépend curation flags.

**Aucune migration** pour ce volet.

## OBJET 4 — Chaîne poste → marché → dépôt

- Marché : **un** `poste_budgetaire_type_id` (pas de multi-poste marché).
- Dépôt connaît le marché (`marche_id`) → poste via marché ; hors marché
  (`sans_marche` / `rattachement_poste_direct` / FG) : retenues déjà masquées (#1191).
- Payload saisie marchés (`mapMarchePourSaisieDepotPayload`) embarque les **%**
  mais **pas** les flags `applicable_*`. Eager load actuel :
  `posteBudgetaireType:id,libelle,compte_comptable_id` — piège #1184 si on lit
  un flag non sélectionné → toujours false/absent sans erreur.
- PHASE 2 devra **étendre** le `select` / payload (ou joindre flags sur le marché).

## OBJET 5 — Cohabitation #1191

Condition combinée proposée (affichage) :

`peutSaisirRetenueX = peutSaisirRetenuesMarche && flag_applicable_X_du_poste_marché`

Sans dupliquer : garder `peutSaisirRetenuesMarche` intact ; AND flag par retenue.

### Recommandation 422 serveur
**Ne pas** ajouter un 422 dur « retenue non applicable au poste » sur PATCH/save
des dépôts **historiques** (leçon #1191→#1193).
Pour les dépôts **encore modifiables** : préférer **normalisation silencieuse à 0**
(alignée Q3B Robin) plutôt qu’un 422 qui bloque l’enregistrement d’autres champs.
Option soft : 422 seulement si l’utilisateur **poste explicitement** une retenue > 0
alors que le flag est faux *et* le dépôt est dans le périmètre zéro — à trancher
en PHASE 2 selon fiabilité des flags.

## OBJET 6 — Découpage PHASE 2

| Lot | Contenu | Dépendance |
|-----|---------|------------|
| **#1222** (en cours / prévu) | touche `DepotFactureSaisieForm.vue` | **terminer avant** tout lot 1223 front |
| **1223-P2a** affichage | masquer options retenues selon flags ; payload flags ; zéro si masqué sur dépôts non validés ; mention « (historique) » si retenue figée | après #1222 ; sérialisation SaisieForm |
| **1223-P2b** polish préremplissage | si besoin (déjà quasi fait) ; pas de migration | peut fusionner avec P2a |
| Arbitrage compta | **aucun** (risque B résolu) | — |

Ne pas toucher : calcul retenues, Sage, ComptabilisationController, nomenclature
(sauf lecture flags), MarchePenalitesSoldeService.

## SCRIPT

```text
php tools/diag/diag_retenues_flags_poste_1223.php
```

## LEÇON

Flags retenues ≠ lie_aux : même table, millésime plus récent (défaut true), déjà
consommés par l’UI marchés — fiabilité dépôt à **mesurer** avant masquage.
Eager load partiel (#1184) : ajouter les colonnes au `select` ou elles sont invisibles.
Mesurer avant d’imposer (#1191/#1193). Zéro rétroactif sur validé = décision compta ;
Robin a déjà borné aux non-validés → diag informatif seulement.
