---
suivi: 1265
date: 2026-08-11
sujet: PHASE 1 modelisation CIE (deux semantiques, cardinalite, mono-creancier)
chantier: cie / depot-factures / retenues / compta
type: diagnostic
statut: poussé
hash: 7cfe23e2
fichiers:
  - tools/diag/diag_modelisation_cie_1265.php
  - docs/suivi/SUIVI_1265_modelisation_cie.md
---

# SUIVI #1265 — Modélisation CIE (PHASE 1)

## PROMPT ENVOYÉ

PHASE 1 — modélisation et diagnostic. Interdiction absolue de modifier le code
applicatif (`app/`, `resources/`, `routes/`, `database/`). Aucune migration.
Script + fiche uniquement. Ne pas coder #1262 (abandonné, numéro consommé).

## FICHES LUES

- `#1261` — CIE absent du numérateur ; `montant_cie` = 0 sur associations ;
  association ≠ peuplement retenue.
- `#1263` — écritures Sage figées ; peupler `montant_cie` sans numérateur rompt
  D/C ; leçon : ne pas présenter un doublement de TVA comme correction sans
  mécanisme métier nommé (deux sémantiques du lien non vues).
- `#1224` — pas de case UI CIE ; « Associer à un CIE » distinct de `montant_cie`.
- `#890` / `#990` — finitions au numérateur ; « dont CIE » via
  `cie_repartitions.marche_id`.
- `#1043` — ne pas avaler les exceptions en zéro crédible.

## RÈGLES MÉTIER R1–R5 (confirmées Robin — acquis)

| Id | Règle |
|----|--------|
| R1 | L’avanceur est **toujours ENVOL**. Jamais une entreprise (OPC = autre mécanisme). |
| R2 | Un seul créancier : ENVOL. Le prestataire est payé par sa facture ; il n’est créancier de rien au titre CIE. |
| R3 | La retenue **solde** la dette du débiteur envers le CIE. |
| R4 | Un coût → **N** retenues sur **N** factures différentes. |
| R5 | Montant retenu = répartition de **ce** fournisseur, jamais le total opération. |

Finalité : savoir si on a bien récupéré l’argent des autres sociétés.

---

## OBJET 1 — Inventaire du modèle actuel

### `cie_operations` (migration `2026_05_23_100000` + `marche_id` #987)

| Colonne | Type (migration) | Null | Notes |
|---------|------------------|------|-------|
| id | bigint PK | NON | |
| programme_id | FK programmes | NON | cascadeOnDelete |
| date_operation | date | NON | |
| libelle | string(255) | NON | |
| montant_total_ht | decimal(15,2) | NON | unité métier du module |
| montant_total_ttc | decimal(15,2) | NON | |
| taux_tva | decimal(5,2) | NON | default 20 |
| fournisseur_avance_id | FK fournisseurs | NON | restrict — **mélange prestataire / ENVOL** |
| depot_facture_id | FK depot_factures | OUI | nullOnDelete — **PAS d’UNIQUE** en migration |
| commentaire | text | OUI | |
| created_by | FK users | OUI | nullOnDelete |
| marche_id | FK marches | OUI | #987 — marché du facturant |
| timestamps + softDeletes | | | soft delete OUI |

Index uniques : **aucun** sur `depot_facture_id`. L’app (`associerDepotFacture`) détache
les autres ops du même dépôt → au plus **1 opération active / dépôt**, mais
**1 opération → au plus 1 dépôt**.

### `cie_repartitions` (migration `2026_05_23_100100` + `marche_id` #990)

| Colonne | Type | Null | Notes |
|---------|------|------|-------|
| id | bigint PK | NON | |
| cie_operation_id | FK cascade | NON | |
| fournisseur_id | FK restrict | NON | débiteur |
| pourcentage | decimal(5,2) | NON | |
| montant_calcule_ht | decimal(15,2) | NON | |
| marche_id | FK nullable | OUI | #990 — « dont CIE » KPI |
| timestamps | | | **pas** de softDeletes |
| depot_facture_id | — | — | **ABSENT** |
| cumul_retenu_* | — | — | **ABSENT** |

Autres tables du module : **aucune** (pas de table de prélèvements / règlements CIE).

### Cardinalités effectives

- Programme **1-N** opérations.
- Opération **1-N** répartitions.
- Opération **N-0..1** dépôt facture (lien unique côté opération).
- Répartition **aucun** lien dépôt → **impossible** de tracer N retenues.

---

## OBJET 2 — Deux sémantiques du lien + rôle manquant

`cie_operations.depot_facture_id` désigne aujourd’hui **deux choses opposées**
avec le **même** bouton « Associer à un CIE » :

| Sémantique | Sens | Retenue sur ce dépôt ? |
|------------|------|------------------------|
| (i) PRESTATION | Facture du prestataire = **coût** mutualisé | **NON** |
| (ii) RETENUE | Situation d’un débiteur | **OUI** (montant = sa répartition) |

### a) Nature déduite des 11 associations (à confirmer en prod via script)

Critère de rattrapage (déduction, **pas** règle durable) :
`fournisseur_avance_id == fournisseur du dépôt` → PRESTATION ; sinon
(avanceur ENVOL) → RETENUE candidate.

| op | dépôt | nature déduite (#1261/#1263) |
|----|-------|------------------------------|
| 3–10 | 774, 1345…1339 | PRESTATION (avanceur = fourn. dépôt) |
| 11–13 | 1316, 1230, 1229 | RETENUE (avanceur ENVOL ≠ fourn. dépôt) |

Noms exacts : sortie section 3 du script OVH (pas inventés ici).

### b) Critère déductible fiable à 100 % ?

**NON.** Cas futur : ENVOL (ou une société du groupe) est aussi **prestataire**
sur une autre opération → le critère `avanceur == fourn. dépôt` / `avanceur ∈ ENVOL`
casse en silence. Orientation Robin : nature **déclarée**, déduction uniquement
pour rattraper les 11 lignes.

### c) Champ proposé (papier — PAS de migration)

- **Où** : `cie_operations` (le lien `depot_facture_id` change de sens selon la nature).
- **Nom** : `nature_lien` (ou `type_association`).
- **Type** : enum string / CHECK — valeurs `prestation` | `retenue`.
- **Obligatoire** : oui en cible ; pour reprise, nullable temporairement puis backfill.
- **Défaut** : **aucun** défaut silencieux acceptable en prod cible — forcer saisie ;
  reprise : peupler d’après déduction puis revue humaine ops 11–13 / ambiguës.

Alternative plus propre (cf. objet 8) : séparer les liens — dépôt de coût sur
l’opération, dépôts de retenue sur les **répartitions / prélèvements** — alors
`nature_lien` sur l’opération devient inutile pour le dépôt unique.

---

## OBJET 3 — Blocage structurel (R4)

### a) Impossible d’accrocher 7 retenues à l’opération 3

Confirmé : une seule colonne `depot_facture_id` sur l’opération ; les
répartitions n’ont pas de FK dépôt. Op 3 (coût RB SOLUTION / dépôt 774) ne peut
pointer que 774 — pas les 7 situations débiteurs.

### b) Contournement Lahouari (ops 12 et 13)

Ops 12 et 13 : chacune **une seule** répartition à **100 %** (ACP CLOISON
340,50 · EMC 83 340,50) = une opération créée **par débiteur**. Commentaire
op 12 : « A REPRENDRE ». Ce n’est pas le modèle métier (1 coût, N répartitions,
N retenues) — c’est un contournement de la cardinalité.

### c) Où doit vivre le lien de retenue

**Sur la ligne de répartition** (et/ou table fille de prélèvements), pas sur
l’opération. Justification : la répartition porte déjà le débiteur + montant dû ;
R4 exige N dépôts ; R5 exige le montant de **cette** ligne.

### d) Répartition pas encore retenue

Cible : `reste_du_ht = montant_calcule_ht − cumul_retenu_ht`. Tant que
`reste_du_ht > 0`, JDS CONSTRUCTION « doit » encore 252,00 € — sans dépôt lié.

---

## OBJET 4 — Prestataire ≠ avanceur

### a) Ce que lit `fournisseur_avance_id` aujourd’hui

| Consommateur | Effet |
|--------------|--------|
| `CieService::calculerSoldesParProgramme` | solde += op.ht si avanceur = F |
| `CieService::calculerMatriceDettes` | créancier = avanceur ; débiteur = repart.fourn |
| `suggestionsReglements` | via soldes |
| `CieController` / formulaires | libellé « qui avance » ; contrainte marché facturant = avanceur |
| `transformOperation` / Index / Show | affichage |
| Écritures / `montant_cie` | **ne lit pas** ce champ pour le montant retenu |

Donc le champ pilote exclusivement le **modèle pair-à-pair** et l’UI opération.

### b) Ambiguïté d’entité — ARBITRAGE ROBIN (ne pas trancher)

Le module dit « ENVOL » créancier ; ENVOL existe comme fournisseur (F1159).
Mais l’argent sort de la **société du programme** (ex. FAC-2026-6004 :
« SNC LOUIS PASTEUR » ; dépôt 774 → société « E-11 — LOUIS PASTEUR »).

**Question** : créancier juridique = groupe ENVOL (F1159) ou **société porteuse**
du programme ? Conditionne l’écriture comptable de la retenue (compte / tiers).

### c) Séparation cible (papier)

- `prestataire_fournisseur_id` : qui a réalisé / facturé le coût (ops 3–10).
- `avanceur` : **toujours** la société porteuse / ENVOL selon arbitrage b —
  plus besoin de stocker un fournisseur « avanceur variable » si R1 est absolu ;
  sinon `creancier_societe_id` (entité programme) plutôt que fournisseur.
- Ne plus utiliser un seul `fournisseur_avance_id` pour les deux rôles.

---

## OBJET 5 — Soldes / Matrice / Règlements suggérés

### a) Code

| Écran | Méthode | Données |
|-------|---------|---------|
| Soldes | `CieService::calculerSoldesParProgramme` | ops.ht (avanceur +) − rep.ht (−) |
| Matrice | `calculerMatriceDettes` + `compenserDettesCroisees` | debiteur→avanceur = rep.ht |
| Règlements | `suggestionsReglements` | glouton sur soldes |

UI : `resources/js/Pages/Cie/Index.vue` (onglets) via `CieController`.

### b) Algo

**Mathématiquement cohérent** (totaux bouclent : créanciers 1 764 + 681 = 2 445 =
débiteurs) **mais appliqué à un modèle inadapté** (dettes entre entreprises).
Pas de défaut arithmétique propre détecté au-delà du modèle faux.

### c) Cible mono-créancier — recommandation

**Supprimer** Matrice et Règlements suggérés (source d’erreur **actionnable** :
ex. ACP CLOISON → RB SOLUTION 83 alors que RB est déjà payé ; cumuls avec
retenue op 12 → sur-paiement).

**Remplacer** Soldes par un état simple :
« qui doit combien à ENVOL / société porteuse ; déjà retenu ; reste à récupérer ».

---

## OBJET 6 — Solde de la dette par la retenue (R3) + correction 8 bis

Aujourd’hui : **aucun** lien retenue → extinction dette.

### a) Manquant

- Pas de statut / cumul sur répartition.
- Pas de montant réellement retenu ni date.
- Pas de lien vers le(s) dépôt(s) porteurs.
- Pas de gestion du partiel.

Un booléen « soldé » **ne suffit pas** (deux retenues partielles sur deux
situations) → **cumul_retenu_ht** + table/lignes de prélèvement multi-dépôts.

### b) Rejet / suppression dépôt / retrait association

La dette doit **se rouvrir** : décrémenter le cumul (et annuler la ligne de
prélèvement). Sinon l’ERP affiche « récupéré » à tort.

### c) Retenu ≠ encaissé

Une retenue sur situation **non payée** n’est pas de l’argent rentré.
Finalité module → distinguer au minimum :
- `retenu_ht` (déclaré sur facture / écriture),
- `encaisse_ht` (dépôt en paiement confirmé / payé / parti Sage),
sinon KPI « récupéré » mensonger.

---

## OBJET 7 — Inventaire des consommateurs

| fichier:zone | consommateur | donnée lue | montant ? | écriture Sage ? | impacté modèle cible ? |
|--------------|--------------|------------|-----------|-----------------|------------------------|
| `EcrituresComptablesCalculator` ~L302/588 | reconstitution retenues / ROLE_CIE | `montant_cie` | OUI | OUI (si régénéré dans lignes) | OUI |
| `depotFactureEcrituresCalculator.js` ~L248/492 | miroir JS | `montantCie` | OUI | non (preview) | OUI |
| `DepotFactureSaisieForm.vue` ~L3121, frise, cagnotte | UI saisie | `montant_cie`, `cie_association` | OUI affichage | non | OUI (case 5e) |
| `ComptabilisationController` cie_ht | APBILL / retenues | via calculator | OUI | OUI | OUI |
| `MarcheCouvertureKpiService` | CIE capté / à venir | `montant_cie` + `cie_repartitions` | OUI KPI | non | OUI |
| `MarcheKpiHeader.vue` | « dont CIE » | `cie_a_venir_ht`, `cie_ht` | OUI | non | OUI |
| `RetenueGarantieService` / `RetenueHistoriqueCollecteService` | historique retenues | `montant_cie` saisie | OUI | indirect | OUI |
| `FactureVentilationAffichagePresenter` | affichage | `montant_cie` | OUI | non | OUI |
| `FactureController` montant_cie_affiche | liste factures | saisie | OUI | non | OUI |
| `DepotFactureController` | persist saisie + assoc CIE | `montant_cie`, `CieService` | OUI | via saisie | OUI |
| `CieService` soldes/matrice/suggestions | onglets CIE | ops + reps | OUI (faux métier) | non | OUI (supprimer/remplacer) |
| `CieController` + `Cie/*.vue` | CRUD module | ops/reps | non monétaire facture | non | OUI |
| `SuiviChantierController` / Index | onglet CIE | props cie_ | non | non | OUI |
| Bilan / budget | — | pas de lecture CIE dédiée trouvée | non | non | faible |
| Cagnotte CP (`CompteProrata`) | cagnotte chantier | CP, pas CIE dette | non CIE | non | CIE à retirer du bloc UI commun |
| Exports | — | pas d’export CIE dédié trouvé | — | — | selon lots futurs |

---

## OBJET 8 — Schéma cible (papier uniquement)

### Tables / colonnes (proposition)

**`cie_operations`** (coût mutualisé)
- programme_id, date, libelle
- montant_total_ht (**référence**), taux_tva (du marché mono-TVA), montant_total_ttc dérivé
- prestataire_fournisseur_id
- depot_facture_cout_id (nullable) — facture PRESTATION, **aucune** retenue
- creancier : selon arbitrage Robin (société programme vs F1159) — champ dédié
- plus de `fournisseur_avance_id` ambigu ; plus de `depot_facture_id` dual

**`cie_repartitions`**
- cie_operation_id, fournisseur_id (débiteur), pourcentage, montant_calcule_ht
- marche_id (où retenir)
- **cumul_retenu_ht** (incrémenté / décrémenté)
- reste_du_ht = montant − cumul (calculé ou colonne dérivée)

**`cie_prelevements`** (nouvelle — 1-N par répartition)
- cie_repartition_id, depot_facture_id, montant_retenu_ht, date
- statut lien dépôt (actif / annulé si rejet)
- permet N retenues partielles traçables

### HT / TTC

| Champ | Unité |
|-------|--------|
| montants module (ops, reps, cumul, reste, saisie case CIE) | **HT** |
| `ocr_raw.saisie.montant_cie` actuel | **TTC** (héritage) — cible : stocker HT **ou** documenter conversion |
| Conversion | taux **du marché** (mono-TVA retenues) — **interdit** ×1,2 en dur |
| Affichage saisie | HT saisi + TTC équivalent affiché à côté |

### Plan de reprise des 11 ops (Robin décide — pas de script)

| Lot | Auto ? | Humain ? | Rester en l’état ? |
|-----|--------|----------|-------------------|
| Ops 3–10 nature PRESTATION + multi-reps | nature + dépôt → `depot_facture_cout` | vérifier % / montants | montant_cie=0 OK |
| Ops 11–13 (1 rep 100 %, « A REPRENDRE ») | non fiables comme modèle cible | **refondre** : rattacher aux ops coût (ex. op coût 340,50) + prélèvements | ne pas « corriger » montants Sage |
| `montant_cie` = 0 partout | rien à rejouer Sage | — | **laisser** (ERP incomplet, pas corrompu) |
| Soldes/matrice historisés | — | désactiver UI | — |

---

## OBJET 8 bis — Cible écran

- 5e case **CIE** dans RETENUES (même rang RG/CP/Finitions/Pénalités).
- Activable ssi répartition déclarée, non entièrement soldée, pour ce fournisseur/marché ; sinon grisée + motif.
- Cochée par défaut si activable ; **décochable**.
- Champ montant = reste dû, **modifiable vers le bas uniquement** ; plafond serveur = reste dû.
- Unité : saisie **HT** ; afficher HT + TTC (taux marché).
- À côté : libellé op, date, montant réparti, déjà retenu, reste dû + **lien fiche op nouvel onglet**.
- Bloc « Cagnotte CP et CIE (programme) » : **ne plus porter le CIE** (association / retenue sortent de ce bloc).

---

## POINTS D’ARBITRAGE ROBIN

1. Nature déclarée : champ sur op vs séparation des liens coût/retenue.
2. Créancier juridique : groupe ENVOL vs société porteuse programme.
3. Sort onglets Soldes / Matrice / Règlements suggérés (reco : supprimer les deux derniers, remplacer Soldes).
4. Unité persistée de `montant_cie` (HT vs TTC) + migration sémantique.
5. Retenu vs encaissé : un ou deux cumuls.
6. Reprise ops 12–13 (« A REPRENDRE ») : refonte humaine obligatoire.
7. Ordre des lots suivants : modèle + UI case CIE **avant** tout peuplement `montant_cie` / numérateur (ne pas ressusciter #1262 tel quel).

## SIGNAL ACTIONNABLE

Tant que non tranché : les **règlements suggérés** peuvent faire payer une
entreprise à un prestataire **déjà réglé** par ENVOL — erreur utilisateur
possible immédiatement.

## DÉPLOIEMENT-TEST

- Commit : script + fiche uniquement.
- OVH : `php -d display_errors=1 tools/diag/diag_modelisation_cie_1265.php`
- Vérifier sections 3–7 non vides ; 0 ligne = ne valide rien.
- Aucune migration, aucun journal, aucun rebuild.

## LEÇON

Deux sémantiques sur une même FK sans discriminant = toute « correction »
chiffrée est suspecte (#1263). Cardinalité 1 dépôt / opération rend R4
impossible : le contournement 1-op-par-débiteur n’est pas une reprise automatique.
Le modèle pair-à-pair a une arithmétique juste et un métier faux — supprimer
l’actionnable (règlements suggérés) avant d’écrire des retenues.
