---
suivi: 1219
date: 2026-08-05
sujet: pénalités dans restant à facturer + reste_a_regler_ht
chantier: marches-frise-penalites
type: feature
statut: poussé
hash: 872a7729
fichiers:
  - app/Services/MarchePenalitesSoldeService.php
  - app/Services/MarcheCouvertureKpiService.php
  - app/Models/Marche.php
  - app/Models/Penalite.php
  - app/Http/Controllers/MarcheController.php
  - app/Http/Controllers/ProgrammeDashboardController.php
  - app/Http/Controllers/PenaliteController.php
  - resources/js/Components/Marches/MarcheKpiHeader.vue
  - public/build/
  - database/data/journal_mises_a_jour_post_2026_06_12.php
  - tools/diag/mesure_bilan_penalites_1219.php
  - tools/diag/recalcul_reste_penalites_1219.php
  - tests/Unit/MarchePenalitesSoldeServiceLogicTest.php
  - docs/suivi/SUIVI_1219_penalites_restant_a_facturer.md
---

## PROMPT ENVOYÉ

SUIVI #1219 — Pénalités dans la frise : intégration au restant + ventilation.
HEAD base = #1218 (`1fa12dda`). Q1=B (deux soldes), Q2=acceptee∪imputée, Q3=négatif affiché.

## FICHES LUES

- `SUIVI_1218` — deux soldes ; `acceptee` ; imputation vivante ; Sage ≠ frise.
- `SUIVI_1191` / `SUIVI_1192` — retenues historiques ; palette (rouge = danger).

## SYNTHÈSE

### OBJET 1 — Définition « imputée » + tableau 10 combinaisons

**Imputée** = `penalites.depot_facture_id IS NOT NULL` (source de vérité UI +
`synchroniserPenalitesImputees`). Le pivot `facture_penalites` est maintenu en
parallèle ; le statut `deduite` **n’est pas** posé automatiquement à l’imputation
(synonymes ? **non** — mesurable via script OVH).

| statut | imputée | déduite du solde ? | ligne affichage |
|--------|---------|--------------------|-----------------|
| notifiee | non | non | hors calcul (gris) |
| notifiee | oui | oui | dont imputé |
| contestee | non | non | hors calcul |
| contestee | oui | oui | dont imputé |
| acceptee | non | oui | dont accepté |
| acceptee | oui | oui | dont imputé (PRIME) |
| deduite | non | non | hors calcul |
| deduite | oui | oui | dont imputé |
| annulee | non | non | hors calcul |
| annulee | oui | oui | dont imputé |

Volumes 4 croisements : lancer `php tools/diag/mesure_bilan_penalites_1219.php` sur OVH
(stub local vide).

### OBJET 2–3 — Point de calcul unique

`MarchePenalitesSoldeService` — appelé par :
- `MarcheCouvertureKpiService::calculer` (frise)
- `Marche::recalculer` (`reste_a_regler_ht`)
- `MarcheController` (show / index / KPI programme)
- `ProgrammeDashboardController` (santé financière)

Formule frise : `restant = total − facturé − provisionné − capté − pénalités_déduites`
(sous-lignes CP/OPC/CIE/RG **inchangées** en amont de la déduction).

**Bilan** : lit la colonne `reste_a_regler_ht` — impact = Σ pénalités déduites par
programme après `recalculer`. **Aucune écriture comptable** : indicateur uniquement.
Ne touche pas `EcrituresComptablesCalculator` / APBILL / ROLE_PENALITES / Intacct.

Tableau bilan avant/après : **à coller depuis OVH** (mesure avant deploy + après
`recalcul_reste_penalites_1219.php`). Stub local : 0 pénalité.

### Consommateur signalé (non adapté — garde-fou)

`DepotFactureSaisieForm.vue` `resteApresFactureInfo` (kind `after_facture`) recalcule
`total − facturé − montant` **sans** pénalités. Affichage neutre lit `reste_a_regler_ht`
(OK). Dépassement sticky pendant la saisie d’un montant peut ignorer les pénalités —
suivi ultérieur si besoin (ne pas toucher `DepotFactureController` ce lot).

### OBJET 4 — Affichage

- Carte Pénalités : total déduit + dont accepté / dont imputé ; hors calcul en gris.
- `horsFrise` retiré ; segment barre inclus (équation marché = F+P+C+Pén+R).
- RESTANT : sous-ligne « dont pénalités −X » ; négatif + message rouge.
- Teinte carte : **slate `#64748b`** (fait acquis), rouge réservé au restant négatif (#1192).

### Mapping carte

| Libellé affiché | Source | Inclus calcul |
|-----------------|--------|---------------|
| Pénalités (principal) | total_deduit_ht | oui |
| dont accepté | dont_accepte_ht | oui (partie) |
| dont imputé | dont_impute_ht | oui (partie) |
| Hors calcul (non déduit) | hors_calcul_ht | non |
| Restant à facturer | restant_a_facturer_ht | — |
| dont pénalités −X | total_deduit_ht | déjà dans restant |

## DÉPLOIEMENT / TEST

1. `git pull origin main`
2. `php tools/diag/mesure_bilan_penalites_1219.php` (tableau avant — si pas encore mesuré)
3. `php tools/diag/recalcul_reste_penalites_1219.php` (rafraîchit colonne indicateur)
4. `php artisan view:clear` + `php artisan journal:sync`
5. Tests manuels 1–10 (frise, ventilation, imputation, HT/TTC, négatif, dashboard, bilan, saisie, marché sans pénalité)
6. **Ordre métier** : retirer l’avenant « PENALITES » **après** ce déploiement (sinon double comptage)

## LEÇON

- Un seul service pour frise et `reste_a_regler_ht` — les transforms show/index
  recalculaient aussi `total−facture` en parallèle : tous branchés.
- Dashboard ne lisait pas la colonne : formule `engage−facture` alignée sur le service.
- Imputation = `depot_facture_id`, pas le statut `deduite`.
- Mass `update()` d’imputation ne fire pas les events Eloquent → frise live OK ;
  colonne via validation / script recalcul / releaseImputation.
