---
suivi: 1031
date: 2026-07-28
sujet: Correctif exigence poste prématurée sur sans_marche (#1029)
chantier: budgets-programme
type: fix
statut: déployé et validé en prod le 29/07 (dépôt « marché non encore créé » sans poste = accepté ; rattachement poste sans poste = refusé)
hash: d248aada
fichiers:
  - app/Http/Controllers/DepotFactureController.php
  - docs/suivi/SUIVI_1031_correctif_sans_marche_poste.md
  - docs/suivi/SUIVI_1029_ancrage_budget_controles.md
---

## PROMPT ENVOYÉ

SUIVI #1031

## CONTEXTE — URGENT, RÉGRESSION BLOQUANTE NON DÉPLOYÉE
ERP Hectarion (repo HECTAREG/erp-immo). AVANT DE CODER :
`git fetch origin && git checkout main && git pull origin main`, puis coller
`git log --oneline -1 origin/main`.

Le lot **#1029** (`ca232978`, poussé sur `origin/main`, **PAS ENCORE DÉPLOYÉ en prod**) a
introduit un contrôle strict de rattachement sur tous les points d'écriture d'un dépôt.
Tu avais toi-même signalé le problème dans ta synthèse, sous « Alerte métier » — il est
confirmé et doit être corrigé avant tout déploiement.

**Le défaut** : la règle « le poste budgétaire est obligatoire » a été appliquée au cas
`sans_marche = true` (case « Marché non encore créé — à relier ultérieurement »). Or
l'interface actuelle **efface le poste** via un watcher quand cette case est cochée
(`DepotFactureSaisieForm.vue`, watcher sur `sans_marche` ≈L3715 : si true → clear
`rattachement_poste_direct`, `poste_budgetaire_type_id`, `marche_id`, …).

Conséquence si on déployait en l'état : **toute sauvegarde d'une facture en mode
« marché à créer » renvoie 422**. C'est un cas d'usage courant (49 dépôts en production dans
cet état) utilisé quotidiennement par la comptabilité → la saisie de factures serait bloquée.

**Cause** : la règle est juste dans la cible (logique A, où le poste est choisi EN PREMIER
donc toujours connu), mais prématurée avec l'interface actuelle, qui n'a pas encore été
refondue (c'est le sous-lot 2b, non commencé).

## À FAIRE — correctif MINIMAL
Dans le contrôle `assertRattachementProgrammeBudgetValide`
(`app/Http/Controllers/DepotFactureController.php`) :

**RETIRER** l'exigence de poste obligatoire pour le seul cas `sans_marche = true`.
Un dépôt en mode « marché à créer » redevient enregistrable sans poste budgétaire, exactement
comme avant le #1029.

**CONSERVER INTÉGRALEMENT** toutes les autres règles du #1029, qui sont correctes :
- `rattachement_poste_direct = true` → **poste OBLIGATOIRE** (c'est la fermeture du trou
  #1022, elle doit rester : le formulaire propose bien un sélecteur de poste dans ce mode) ;
- `marche_id` ET `programme_budget_id` simultanément renseignés → REJET ;
- `programme_budget_id` : enveloppe existante, active, non supprimée, appartenant au programme
  du dépôt → sinon REJET ;
- `programme_budget_id` : poste de l'enveloppe avec `applicable_budget = 1` → sinon REJET ;
- `programme_budget_id` : aucune retenue non nulle (RG / caution / CP / OPC / CIE / finitions)
  → sinon REJET ;
- l'appel du contrôle depuis **TOUS** les points d'écriture, y compris `valider`,
  `validerHistorique`, `resolveEngagementPayloadPourValiderDejaValide`,
  `resolveEngagementPayloadPourValiderEtComptabiliser` → INCHANGÉ ;
- le blocage de comptabilisation si le poste n'a pas de compte comptable, avec `471000`
  réservé au cas `sans_marche` → INCHANGÉ ;
- la branche enveloppe de `ProgrammeChargeAccountResolver` → INCHANGÉE ;
- les colonnes `programme_budget_id`, le double ancrage et les relations → INCHANGÉS.

**Laisser un commentaire explicite dans le code**, à l'endroit du contrôle, indiquant que
l'exigence de poste pour `sans_marche` sera rétablie au sous-lot 2b, une fois le formulaire
refondu en logique A (poste choisi en premier). Cela évite qu'un futur lot la retire ou la
remette par erreur.

## GARDE-FOUS
- Correctif **MINIMAL** : ne modifier QUE la règle liée à `sans_marche`. Ne rien refactorer,
  ne rien optimiser, ne rien renommer au passage.
- **AUCUNE modification d'interface** : ne pas toucher `DepotFactureSaisieForm.vue`, ne pas
  toucher aux watchers, ne pas retirer de case à cocher. Aucun `.vue` modifié → aucun rebuild
  de bundle, aucun `public/build/` dans le commit.
- Aucune migration (les colonnes du #1029 sont conservées telles quelles).
- NE PAS toucher : le module tâches de suivi (#1028/#1030), le module Assistant/Aktor (#1027),
  le bilan, les marchés, Sage/Intacct.
- NE PAS committer `PROJECT.md` (diff local non commité dans ton arbre), ni les untracked
  (`ERP*.zip`, `_tmp_*.php`, `_tmp_extract/`, `design_handoff_*/`), ni aucun fichier des lots
  #1027 / #1030.

## TESTS À EXÉCUTER ET REPORTER
1. **Le test qui motive ce lot** : dépôt d'une facture programme avec « Marché non encore
   créé » coché, SANS poste → **enregistrement ACCEPTÉ** (avant ce correctif : 422).
2. Même cas, puis `valider` → accepté.
3. Même cas, puis `validerHistorique` → accepté.
4. « Pas de marché, rattacher à un poste budgétaire » coché SANS poste → **toujours 422**
   (le trou #1022 reste fermé — c'est essentiel, ne pas le rouvrir).
5. Même cas AVEC poste → accepté.
6. Facture de marché normale → valider → comptabiliser : non-régression complète.
7. `marche_id` + `programme_budget_id` forcés ensemble → toujours 422.
8. Ouverture en édition des 5 dépôts historiquement incohérents (#1091, #1164, #1165, #1168,
   #1183) → pas de 500.

## QUALITÉ CODE — SORTIE BRUTE EXIGÉE
`php -l` sur chaque fichier modifié, puis `ReflectionClass` + instanciation via le container
DI. **Coller les sorties brutes.** Un « OK » est refusé.
⚠️ Ce lot touche `DepotFactureController` : un 500 ici bloque toute la saisie de factures de
l'entreprise.

## LIVRABLE
- Diff complet (il doit être court : quelques lignes).
- Confirmation explicite qu'aucun `.vue` n'est modifié et qu'aucun `public/build/` n'est dans
  le commit.
- `git status --short` avant commit : uniquement les fichiers de ce lot.
- Commit + push. Sortie brute de `git log --oneline -1 origin/main` (VRAI hash) et
  `git show --stat <hash>`.
- Créer `docs/suivi/SUIVI_1031_correctif_sans_marche_poste.md` selon la convention
  `docs/suivi/README.md` : frontmatter (`chantier: budgets-programme`, `type: fix`,
  `statut: poussé`, `hash:`, `fichiers:`), PROMPT ENVOYÉ = ce prompt intégral, SYNTHÈSE
  remplie, DÉPLOIEMENT / TEST vide.
- Mettre également à jour la fiche `docs/suivi/SUIVI_1029_ancrage_budget_controles.md` :
  ajouter dans sa section LEÇON que l'exigence de poste sur `sans_marche` était prématurée
  et a été corrigée au #1031.
- SYNTHÈSE dans UN bloc de code markdown copiable.

SUIVI #1031

## SYNTHÈSE

- Correctif minimal dans `assertRattachementProgrammeBudgetValide` : retrait de l'exigence `sans_marche && posteTypeId === null` → 422.
- Commentaire #1031 / sous-lot 2b laissé en place.
- Toutes les autres règles #1029 conservées (`rattachement_poste_direct` obligatoire, exclusion marché+enveloppe, contrôles enveloppe, retenues, points d'écriture, compta 471000).
- Aucun `.vue`, aucun `public/build/`, aucune migration, aucun journal (correctif d'une régression non déployée).
- Tests unitaires d'assert (sans MySQL) : sans_marche sans poste → PASS OK ; rattachement_poste_direct sans poste → PASS 422 ; marche+enveloppe → PASS 422 ; marché normal → PASS OK.
- Fiche #1029 : section LEÇON ajoutée.

## DÉPLOIEMENT / TEST

