---
suivi: 1385
date: 2026-09-01
sujet: diag_nv2_enseigne_hectare
chantier: factures
type: diagnostic
statut: poussé
hash: ab73c679
fichiers:
  - tools/diag/diag_nv2_enseigne_hectare_1385.php
  - docs/suivi/SUIVI_1385_diag_nv2_enseigne_hectare.md
---

## PROMPT ENVOYÉ

SUIVI #1385 PHASE 1 — diagnostic NV2 sur programmes HECTARE (cas FA0003275).

## FICHES LUES

- Aucune fiche NV2/enseigne directe ; #112 (`dd17850c`) via `git log` pour origine exception FG.

## SYNTHÈSE

### OBJET 1 — Où la règle est posée (POINT CENTRAL)

**Mécanisme existant frais généraux (saut NV2)**  
`FactureEstFraisGeneraux::check()` → `programme_id === null`  
(`app/Support/FactureEstFraisGeneraux.php:15`)

**Bifurcation après NV1 validée**  
`FactureWorkflowApresNv1Service::apresNv1Valide()` L21-37 :
- si FG → `ouvrirMisePaiementEtAComptabiliser()` → statuts **`a_comptabiliser`**
- sinon (tout programme, quelle que soit l'enseigne) → crée `workflow_validations` niveau **`nv2`** + statuts **`en_validation_nv2`**

**Miroirs du même test FG** :
- `creerWorkflowsApresDepotAutoNv1()` L61-79
- `statutsApresDepotAutoNv1()` L87-97
- `FactureValidationService::syncDepotAfterValidation()` L307-311
- `DepotFactureController::valider()` L2090-2097 (auto-NV1 déposant validateur)
- `FactureFriseEtapePresenter` (frise sans étape NV2 si FG)
- `ValidationFactureNv2TaskSync` / `NotificationFactureService::notifierValidateurNv2` (skip si FG)

**Conclusion** : la règle « pas de NV2 » n'existe que pour **`programme_id` null**. Toute facture rattachée à un programme — HECTARE inclus — passe NV2 par défaut. C'est le comportement voulu pour ENVOL, jamais étendu à HECTARE.

### OBJET 2 — Enseigne accessible ?

| Élément | Détail |
|---------|--------|
| Colonne | `programmes.enseigne_id` (FK NOT NULL depuis 2026-06-05) |
| Relation | `Programme::enseigne()` BelongsTo → `enseignes.id` |
| Donnée | `enseignes.code` (`HECTARE`, `ENVOL`, …) — **pas** déduction depuis `code_envol` |
| Chemin facture | `$facture->loadMissing('programme.enseigne')` |
| Déjà utilisé | `ValidateurNv2Resolver::isEnseigneEnvol()` L11-16 — choix **validateur** NV2, pas le saut d'étape |

`entite_id` / société facture : distinct de l'enseigne programme.

### OBJET 3 — Ampleur (chiffres prod)

Script read-only : `tools/diag/diag_nv2_enseigne_hectare_1385.php`  
**À exécuter sur OVH** (base locale indisponible au diagnostic Cursor).

Mesures prévues :
- dépôts **NV2 en cours** sur programmes `enseignes.code = HECTARE` (filtre `nv2_en_cours` = app)
- workflows NV2 `en_attente` avec `validateur_id` renseigné
- volume mensuel créations workflow NV2 (24 mois)
- cas FA0003275 + programme HEC30-001

### OBJET 4 — Depuis quand, pourquoi

| Date | Commit | Effet |
|------|--------|-------|
| 2026-04-20 | `9caa4bab` | Sprint factures + workflow NV1/NV2 (tous programmes) |
| 2026-06-04 | **`dd17850c` (#112)** | **Exception FG seule** : « Programme inchange » — NV2 conservé pour tout programme |
| 2026-06-05 | `abd400de` | Socle multi-enseigne (`enseignes`, `programmes.enseigne_id`) — **sans** modifier le circuit NV2 |

**Pourquoi HECTARE passe NV2** : pas un bug de régression récent — la règle #112 n'a jamais distingué les enseignes. HECTARE ajouté après ; le circuit « programme → NV2 » s'applique par défaut comme ENVOL. `ValidateurNv2Resolver` différencie déjà ENVOL (email config) vs autres (DG) **pour l'affectation**, pas pour supprimer l'étape.

### OBJET 5 — Impacts si la règle change

| Zone | Fichier / écran | Cohérence requise |
|------|-----------------|-------------------|
| Transition post-NV1 | `FactureWorkflowApresNv1Service`, `FactureValidationService` | Ne plus créer workflow NV2 HECTARE |
| Envoi dépôt auto-NV1 | `DepotFactureController` L2090+ | Idem statuts initiaux |
| Frise fiche facture | `FactureFriseEtapePresenter` | Masquer NV2 (comme FG) |
| Tâches Mes tâches | `ValidationFactureNv2TaskSync`, `ValidationFactureNv2WorkflowHooks` | Pas de tâche NV2 |
| Notifications | `NotificationFactureService::notifierValidateurNv2` | Pas d'email NV2 ; compta oui |
| Liste globale / onglets | `FactureGlobaleListeQuery`, `FactureGlobaleEtapeStatutFilter` | Compteurs NV2 ↓, à comptabiliser ↑ |
| Résumé programme | `ProgrammeDashboardPresenter::buildFacturesATraiter` | Compteur « Validation NV2 » |
| Validations `/validations` | `ValidationController` | Moins de lignes NV2 DG |
| Dashboard header | `HandleInertiaRequests::computeFacturesEnAttenteCount` | PDG NV2 count |
| Droits modification | `DepotFactureController` (NV2 editable) | Fenêtre droits NV2 absente |
| Réalignement | `FactureWorkflowRealignementService` | FG-like sans NV2 |
| **Factures déjà en NV2** | workflows `en_attente` + tâches Mes tâches | **Pas de rattrapage auto** — voir COMPLÉMENT ci-dessous |

### OBJET 6 — Options d'implémentation (sans choix)

**Option A — Helper central type `FactureSauteEtapeNv2::check(Facture)`**  
Condition : FG **OU** `programme.enseigne.code === 'HECTARE'` (+ param seuil HT nullable plus tard).  
Appel dans tous les points miroir FG.  
Fichiers : ~6-8 PHP. Risque régression moyen si un miroir oublié. Seuil : paramètre optionnel + config.

**Option B — Colonne / JSON sur `enseignes`**  
Ex. `skip_validation_nv2` bool ou `workflow_config` JSON. Backfill HECTARE.  
Fichiers : migration + modèle + helper + mêmes call sites. Risque faible long terme. Seuil : clé JSON `skip_nv2_seuil_ht`.

**Option C — Config applicative `config/factures_workflow.php`**  
Liste codes enseignes sans NV2 + seuils par code.  
Fichiers : config + helper + call sites. Déploiement pour changer. Seuil : entrée config par enseigne.

## DÉPLOIEMENT-TEST

Phase 1 uniquement — exécuter le script diag sur OVH avant phase 2.

## LEÇON

Le saut NV2 n'est paramétré que via `programme_id === null`. L'enseigne est en base et accessible, mais n'intervient aujourd'hui que pour **qui** valide NV2, pas **si** NV2 existe.

---

## COMPLÉMENT — Valideur NV2 (correction OBJET 5)

### Erreur initiale

La colonne `depot_factures.valideur_nv2_user_id` **n'existe pas** dans le schéma ERP. Conclure « aucun valideur nommé » depuis cette colonne est **fausse**.

### Source réelle du valideur NV2

| Étape | Fichier:ligne | Mécanisme |
|-------|---------------|-----------|
| Création workflow | `FactureWorkflowApresNv1Service.php:27-33` | `validateur_id => null` à la création |
| Affectation lazy | `NotificationFactureService.php:106-124` | UPDATE `workflow_validations.validateur_id` |
| Résolution | `ValidateurNv2Resolver.php:36-52` | Branche enseigne programme |
| ENVOL | `ValidateurNv2Resolver.php:38-41` + `config/notifications.php:15-16` | Email config `VALIDATEUR_NV2_ENVOL` (défaut **laury.portales@envol.fr**) → user ERP |
| HECTARE, GEMME, etc. | `ValidateurNv2Resolver.php:50-52` + `NotificationFactureService.php:109-114` | **1er `user_id`** retourné par Microsoft Graph pour le groupe **`GS_DIRECTION_GENERALE`** (`config/services.php` → `AZURE_GROUP_DIRECTION_GENERALE`) |
| Membres Graph | `NotificationFactureService.php:188-213` | `GraphApiService::getMembresGroupe` → match `users.azure_id` ou email |

**Droit de valider** (plus large que l'affectation nominale) :
- `FactureValidationPermissions::canValidateNv2()` L155-156 : **tout** utilisateur dont `m365GroupNames()` contient **`GS_DIRECTION_GENERALE`** peut valider une NV2 en attente.
- `ValidationController.php:141,208-210` : `est_pdg` (= GS_DIRECTION_GENERALE) voit **toutes** les factures NV2 en attente dans `/validations`, pas seulement celles où `validateur_id` = son id.

**Laury PORTALES** sur HECTARE : cohérent si elle est membre **`GS_DIRECTION_GENERALE`** (validation via `est_pdg` / `canValidateNv2`) et/ou si Graph la place en **premier** membre DG lors de `notifierValidateurNv2` → `workflow_validations.validateur_id` renseigné → frise `actor_label` + tâche Mes tâches.

### Périmètre du paramétrage

| Niveau | Existe | Utilisé pour NV2 aujourd'hui |
|--------|--------|-------------------------------|
| **Global M365** | `GS_DIRECTION_GENERALE` | Oui — affectation (non-ENVOL) + droit valider |
| **Config email ENVOL** | `notifications.validateur_nv2.envol` | Oui — programmes enseigne ENVOL uniquement |
| **Entité** | `entites.validateur_nv2_user_id` | **Non** — admin UI (#596), `ValidateurNv2Resolver` ne le lit pas |
| **Programme** | `programme_responsables` | **Non** pour NV2 (NV1 seulement via rôles CT/RP) |
| **Dépôt** | `ocr_raw.saisie.validateur_*` | **NV1 seulement** (`DepotFactureValidateurEffectif`) |

→ **Globalement** pour HECTARE/GEMME (groupe DG), **par enseigne ENVOL** via config email.

### Conséquence fix HECTARE (sans NV2)

| Paramétrage | Action phase 2 |
|---------------|----------------|
| `notifications.validateur_nv2.envol` | **Conserver** — ENVOL |
| `entites.validateur_nv2_user_id` | **Ne pas supprimer** — inopérant aujourd'hui, prévu #595 |
| `GS_DIRECTION_GENERALE` | **Conserver** — ENVOL NV2 + droits DG |
| Nouvelles factures HECTARE | Plus de `notifierValidateurNv2` → plus d'affectation `validateur_id` nv2 |

### OBJET 5 corrigé — Orphelins si NV2 supprimé pour HECTARE

**Chiffres acquis (prod Robin, à conserver)** :
- HECTARE : **1** facture NV2 en cours (dépôt **1516**, FA0003275, programme **45** LE GRES 1)
- HECTARE : **4** passages NV2 sur 90 jours vs **33** ENVOL
- GEMME : **12** programmes actifs, **0** facture NV2 en cours
- `entites.enseigne` fiable ; `entites.nom` non (ex. GEM-01 libellé « HECTARE »)

| Enseigne | Workflows NV2 `en_attente` | Tâches Mes tâches NV2 ouvertes | Orphelins après fix |
|----------|---------------------------|--------------------------------|---------------------|
| HECTARE | **1** (FA0003275 — validation manuelle Robin) | **1** probable (liée au workflow) | À fermer manuellement ou via validation Robin ; pas de rattrapage auto |
| GEMME | **0** | **0** | Aucun |
| ENVOL | inchangé | inchangé | Paramétrage NV2 **conservé** |

Le paramétrage validateur (config ENVOL, groupe DG, colonnes entité) **reste en place** pour ENVOL ; pour HECTARE il devient **inopérant** sur les **nouvelles** factures (plus d'étape NV2), sans nettoyage destructif.

### Script diag complété

Section « COMPLÉMENT — Valideur NV2 » dans `tools/diag/diag_nv2_enseigne_hectare_1385.php` :
- vérifie absence colonne dépôt
- liste users `m365_groups` brut GS_DIRECTION_GENERALE
- workflows NV2 en_attente par enseigne HECTARE/GEMME/ENVOL
- détail dépôt #1516 / tâches ouvertes
