---
suivi: 1166
date: 2026-08-03
sujet: Commercial — acte en main + frais d’acte dans KPI remises (#102)
chantier: commercialisation
type: feature
statut: poussé
hash: 9a8c404c
fichiers:
  - database/migrations/2026_08_03_160000_add_acte_en_main_frais_acte_to_reservations_table.php
  - app/Support/Commercial/RemisesAccordeesCalculator.php
  - app/Http/Controllers/CommercialisationController.php
  - app/Models/Reservation.php
  - app/Http/Requests/UpdateAcquereurFicheRequest.php
  - app/Http/Controllers/ReservationController.php
  - app/Services/ReservationAcquereurUpdateService.php
  - app/Support/ReservationHistoriquePresenter.php
  - resources/js/Composables/useRemisesAccordees.js
  - resources/js/Components/Commercialisation/LotsGrillePanel.vue
  - resources/js/Components/Commercialisation/SynthesePanel.vue
  - resources/js/Pages/Programmes/Acquereur/Show.vue
  - database/data/journal_mises_a_jour_post_2026_06_12.php
  - public/build/
  - docs/suivi/SUIVI_1166_commercial_acte_en_main_remises.md
---

## PROMPT ENVOYÉ

SUIVI #1166 — deux champs réservation (acte en main + frais d’acte estimés)
intégrés au KPI Remises accordées. Réf. SUIVI_1154 / tâche #102. Positions :
case = déclencheur ; lot au prix grille + frais compte ; frais_inclus JSON
inchangé ; prix_vente / prix_grille intouchés.

## SYNTHÈSE

### Fiches lues
- `SUIVI_1154` — formule KPI actuelle ; ne pas confondre avec frais_inclus.
- `SUIVI_1158` — KPI programme entier, indépendant du filtre statut.
- `SUIVI_1164` — snapshot + presenter historique (raccord audit).
- Zone commune LotsGrillePanel (#1156→#1165) — rebase origin/main avant build.

### Colonnes
Aucune équivalente sur `reservations` (`acte_en_main`, `frais_acte*`,
`frais_notaire` absents). `frais_inclus` (JSON) et `frais_acte_verses`
(promesses foncières) laissés tels quels.

Colonnes ajoutées : `acte_en_main` (bool, défaut false),
`frais_acte_estimes_ttc` (decimal 12,2 nullable). Migration idempotente.

### Formule KPI (#1166)
Pour chaque lot `reserve`/`acte` :
`max(0, grille − vente) + (acte_en_main ? max(0, frais_acte_estimes_ttc) : 0)`
Jointure via réservation active (`en_cours`/`acte`) ; lot orphelin → frais 0.
Back : `RemisesAccordeesCalculator`. Vue : `useRemisesAccordees.js`
(LotsGrillePanel + SynthesePanel). `remises_promotions` = remises + promos.

### Positions retenues
2. Case décochée → frais exclus du KPI, valeur conservée.
3. Vente ≥ grille + frais cochés → les frais entrent (ex. 3 000).

### Mapping UI
| Libellé | Posté | DB | Back |
|---------|-------|-----|------|
| Acte en main | acte_en_main | reservations.acte_en_main | bool |
| Montant estimé frais d’acte | frais_acte_estimes_ttc | reservations.frais_acte_estimes_ttc | decimal |

Champ montant désactivé si case décochée. Whitelist #1156 + snapshot
historique #1164.

### Totaux KPI
Avant (prod #1154) : **107 304,48 €** (17 lots, écart commercial seul).
Après déploiement sans saisie : **identique** (tous `acte_en_main` = false).
Écart futur = Σ frais des résas cochées ; lots vente≥grille sans frais
restent à 0 (max(0, …)).

## DÉPLOIEMENT-TEST

OVH : `git pull` ; `php artisan migrate --force` ;
`php artisan optimize:clear` ; `php artisan journal:sync`.

Tests : exemple 200k/195k/3k → 8k ; décoché → 5k montant conservé ;
grille+3k → 3k ; back=Vue ; filtre statut n’altère pas le KPI ;
hors whitelist → erreur ; historique avant/après.

## LEÇON

Le KPI « lots » avec attributs « réservation » exige une jointure
explicite + règle d’orphelin. Dupliquer la formule en Vue sans module
partagé divergera (déjà #1154) — un composable/calculator unique.
Ne pas réutiliser `frais_inclus` pour un KPI monétaire structuré.
