---
suivi: 1023
date: 2026-07-28
sujet: Classification postes bilan Marché / Budget en nomenclature admin
chantier: budgets-programme
type: feature
statut: poussé
hash: b40dc01d
fichiers:
  - database/migrations/2026_07_28_170000_rename_charge_directe_to_budget_and_add_applicable_marche.php
  - app/Models/PosteBudgetaireType.php
  - app/Http/Controllers/Admin/NomenclatureController.php
  - app/Http/Controllers/DepotFactureController.php
  - resources/js/Pages/Admin/Nomenclature.vue
  - database/data/journal_mises_a_jour_post_2026_06_12.php
  - tools/diag/diag_budget_1019.php
  - public/build/assets/Nomenclature-D9htQ8as.js
  - public/build/assets/Nomenclature-HusijmU0.js
  - public/build/assets/app-C-u27d9k.js
  - public/build/manifest.json
---

## PROMPT ENVOYÉ

SUIVI #1023

## CONTEXTE
ERP Hectarion (repo HECTAREG/erp-immo, prod envol.hectare.fr, OVH mutualisé).
`origin/main` actuel = `f3d68964`. AVANT DE CODER : `git fetch origin && git checkout main
&& git pull origin main`, puis coller `git log --oneline -1 origin/main`.

Nouveau chantier « BUDGETS PROGRAMME ». Premier lot : permettre à Robin de CLASSER chaque
poste budgétaire du bilan dans l'écran `/admin/nomenclature` selon son mode de rattachement :
un poste reçoit-il des factures via un MARCHÉ (contrat signé, situations, retenues) ou via
un BUDGET (enveloppe allouée, prestataires variables, ex. publicité) ?

Ce lot ne fait QUE la classification en nomenclature. Les enveloppes budgétaires, l'onglet
BUDGET du programme, le rattachement de facture à un budget et le bilan viendront dans des
lots ultérieurs. NE RIEN ANTICIPER de ces lots.

## ÉTAT EXISTANT (vérifié en prod, ne pas re-supposer)
Table `postes_budgetaires_types`, 42 postes pour l'enseigne 1 (ENVOL), 0 pour HECTARE et
GEMME. Flags booléens présents : `est_taxe`, `est_concessionnaire`, `lie_aux_marches`,
`lie_aux_factures`, `applicable_retenue_garantie`, `applicable_caution_bancaire`,
`applicable_compte_prorata`, `applicable_opc`, `applicable_cie`,
`applicable_charge_directe`, `actif`.

⚠️ `lie_aux_marches` et `lie_aux_factures` sont du LEGACY NON FIABLE (audit prod : des postes
portant 5 marchés ont `lie_aux_marches = 0`, et des postes à 0 marché l'ont à 1).
NE PAS les utiliser, NE PAS les modifier, NE PAS les supprimer. On les laisse en place.

## À FAIRE

### 1. Migration
- **RENOMMER** la colonne `applicable_charge_directe` → `applicable_budget`
  (`renameColumn`, natif Laravel 13). Conserver type bool NOT NULL default false.
  Les 3 postes actuellement à 1 (id 37 PUBLICITE, 45 HONORAIRES DE VENTE GLOBAL,
  7 PROVISION SAV / JURIDIQUE) doivent conserver leur valeur — c'est un renommage, pas une
  recréation. Prévoir le `down()` symétrique.
- **AJOUTER** la colonne `applicable_marche` (boolean NOT NULL default **false**).
- **AUCUN BACKFILL de `applicable_marche`.** Ne PAS déduire la valeur du nombre de marchés
  existants : la donnée de production est trompeuse (le marché était l'unique ancrage
  possible, des dépenses de 76 € ont été forcées en marché). Robin classera manuellement.

### 2. Répercuter le renommage
`applicable_charge_directe` apparaît à 16 endroits (hors `public/build/`). Tous doivent
passer à `applicable_budget` :
- `app/Models/PosteBudgetaireType.php` : fillable + cast
- `app/Http/Controllers/Admin/NomenclatureController.php` : payload index, validation store,
  create, validation update, update
- `app/Http/Controllers/DepotFactureController.php` : l'assertion d'éligibilité, le `select`
  dans le `with()`, et le `whereHas` du payload `postes_charge_directe_par_programme`
- `resources/js/Pages/Admin/Nomenclature.vue` : défaut du form, copies inline edit/sync,
  reset, openEdit, payload submit, badge, checkbox
Faire un `grep -rn "applicable_charge_directe"` final et confirmer **0 occurrence** hors
`public/build/`. Le nom de la clé de payload `postes_charge_directe_par_programme` peut
rester inchangé (hors périmètre, ne pas y toucher pour limiter le risque).

### 3. UI nomenclature — classification
Dans `resources/js/Pages/Admin/Nomenclature.vue` :
- Deux nouveaux contrôles **Marché** et **Budget**, dans la modale d'édition du poste,
  au même endroit et dans le même style que les flags existants.
- **ET AUSSI en édition inline dans la liste** : Robin doit classer 42 postes ; ouvrir la
  modale 42 fois est inacceptable. Ajouter deux toggles directement sur la ligne du poste,
  calqués sur le ToggleSwitch `actif` déjà présent (persistance immédiate, retour visuel).
- **Badges** dans la liste : « Marché » et « Budget ». Le badge « Charge directe » disparaît,
  remplacé par « Budget ».
- Les deux flags sont **INDÉPENDANTS** : un poste peut être marché seul, budget seul, les
  deux, ou aucun. Aucune exclusivité imposée à ce stade.
- **AVERTISSEMENT VISUEL** : si un poste a `applicable_marche = 1` OU `applicable_budget = 1`
  mais `compte_comptable_id` est NULL, afficher une icône d'alerte avec tooltip
  « Aucun compte comptable — la comptabilisation sera bloquée ». Audit prod : 12 postes
  sur 42 n'ont pas de compte comptable, il faut que ça se voie au moment du classement.

### 4. TABLEAU DE MAPPING OBLIGATOIRE
Pour les deux nouveaux contrôles (modale ET inline), fournir dans la synthèse un tableau
« libellé affiché ↔ valeur postée ↔ colonne DB ↔ libellé back ». Un décalage silencieux sur
un composant de choix a déjà causé un bug métier invisible (#608).

## GARDE-FOUS
- NE PAS toucher `lie_aux_marches`, `lie_aux_factures`, `est_taxe`, `est_concessionnaire`,
  ni aucun flag de retenue.
- NE PAS créer de table d'enveloppes, NE PAS créer d'onglet programme, NE PAS modifier le
  formulaire de dépôt de facture au-delà du strict renommage de la colonne.
- Le comportement fonctionnel du rattachement de facture doit rester IDENTIQUE après ce lot
  (le renommage est neutre : même filtre, même sémantique, autre nom).
- NE PAS committer `PROJECT.md` (il est modifié localement dans ton arbre depuis plusieurs
  lots — le laisser hors du commit), ni aucun des fichiers untracked présents
  (`ERP.zip`, `ERP2.zip`, `_tmp_*.php`, `_tmp_extract/`, `design_handoff_*/`).

## TESTS À EXÉCUTER ET REPORTER
1. `php artisan migrate` puis `migrate:status` → la migration est bien passée.
2. Vérifier en base que les postes 37, 45 et 7 ont toujours `applicable_budget = 1` après
   renommage, et que `applicable_marche = 0` partout.
3. `/admin/nomenclature` : les badges Marché/Budget s'affichent, les toggles inline
   persistent (recharger la page pour confirmer), la modale d'édition enregistre les 2 flags.
4. L'icône d'alerte « pas de compte comptable » apparaît bien sur un poste classé sans compte.
5. Dépôt de facture : cocher « pas de marché, rattacher à un poste budgétaire » →
   le sélecteur propose toujours exactement les mêmes 3 postes qu'avant le lot
   (non-régression du renommage).
6. `down()` de la migration testé en local (rollback puis re-migrate).

## QUALITÉ CODE — SORTIE BRUTE EXIGÉE
Pour chaque fichier PHP modifié : `php -l <fichier>`, puis un `ReflectionClass` sur chaque
classe modifiée + une instanciation via le container DI. **Coller les sorties brutes.**

## LIVRABLE
- Diff complet, grep final, rebuild bundle, commit + push, synthèse markdown.

SUIVI #1023

## SYNTHÈSE

Migration `2026_07_28_170000_rename_charge_directe_to_budget_and_add_applicable_marche.php` : rename `applicable_charge_directe` → `applicable_budget` (valeurs conservées), ajout `applicable_marche` default false sans backfill.

Répercussion code : modèle, NomenclatureController, DepotFactureController (filtre payload inchangé sémantiquement), Nomenclature.vue (badges Marché/Budget, toggles inline M/B, checkboxes modale, alerte compte manquant).

Bundle `Nomenclature-D9htQ8as.js` commité. Journal utilisateur ajouté.

php -l + ReflectionClass/DI OK. MySQL local indisponible → migrate / tests UI / down() à faire sur OVH après `git pull` + `migrate --force` + `journal:sync`.

Hash poussé : `b40dc01d`.

## DÉPLOIEMENT / TEST


