---
suivi: 1257
date: 2026-08-07
sujet: Diagnostic ambiguïté SQL facture_id / latestOfMany (écran Validations)
chantier: factures / validations / eloquent
type: diagnostic
statut: poussé
hash: 1d14b38d
fichiers:
  - tools/diag/diag_validation_ambiguous_1257.php
  - docs/suivi/SUIVI_1257_diag_ambiguite_facture_id.md
---

# SUIVI #1257 — DIAG ambiguïté `facture_id` (latestOfMany)

## PROMPT ENVOYÉ

PHASE 1 diagnostic uniquement. Interdiction de modifier le code applicatif.
Localiser l'origine du SQLSTATE 1052 `Column 'facture_id' in field list is ambiguous`
sur l'eager load `depotFacture:id,facture_id,uuid`, inventaire des consommateurs
ofMany, cause d'intermittence, forme des 3 correctifs existants, script prod
lecture seule, fix proposé non appliqué (#1258).

## FICHES / HISTORIQUE LUS

Quatre occurrences historiques du même défaut (sélection non qualifiée + `latestOfMany`) :

1. **#148-FIX** (`00501f78`) — `FactureSuperAdminCorrectionService` : colonnes `depot_factures.*`
2. **#156-FIX** (`616442fc`) — `MarcheRapprocherFactureService` : idem
3. **#864** (`43f8b4c4`) — `ApplicationAvoirDepotService` (liste avoirs proposables) : idem
4. **#1240** (signalement non corrigé) — pointe déjà `ValidationController` ~221/243 et le SQL
   `select id, facture_id, uuid from depot_factures` + `latestOfMany`

Complément : `c5280c53` a aussi qualifié `FactureController` (même classe de bug).

## OBJET 1 — Origine exacte

| Ligne | Méthode | Route | Écran Inertia |
|------:|---------|-------|---------------|
| 221 | `buildFacturesForUser` | `GET validations` → `validations.index` → `ValidationController@index` | `Validations/Index` (`mes_factures`) |
| 243 | `buildToutesFacturesAttente` | même route `index` | `Validations/Index` (`toutes_factures`, **admin GS_DSI seulement**) |

Deux **méthodes distinctes**, un seul point d'entrée HTTP : `index()`.

Attribution SQL : le `toSql` local de `'depotFacture:id,facture_id,uuid'` produit
exactement `select id, facture_id, uuid from depot_factures inner join (select MAX(...) as id_aggregate, depot_factures.facture_id ... ) as latestOfMany` — **identique** au log prod.
Aucune autre occurrence ne sélectionne exactement `id,facture_id,uuid` (ex. #232
`ApplicationAvoirDepotService` sélectionne `id,facture_id,multi_fournisseur,ocr_raw` → autre SQL, aussi crashable).

## OBJET 3 — Intermittence (conclusion)

**L'erreur se produit quand** un utilisateur ouvre `GET /validations` alors que
`buildFacturesForUser` **ou** (si admin) `buildToutesFacturesAttente` renvoie **au moins
une** `Facture`.

Preuve : `Illuminate\Database\Eloquent\Builder::get` n'appelle `eagerLoadRelations` que si
`count($models) > 0` (vendor …/Builder.php ~890-891). Collection vide → pas de requête
`depotFacture` → pas de 1052. Dès qu'il y a ≥1 facture en attente dans le périmètre,
le SELECT non qualifié part et MySQL lève 1052 (indépendamment de `sql_mode` /
`ONLY_FULL_GROUP_BY`).

## LEÇON

Sur `hasOne()->latestOfMany()`, toute restriction de colonnes doit **qualifier**
`depot_factures.facture_id` (et les autres) : la sous-requête expose aussi `facture_id`.
Ne pas retirer `facture_id` (matching Eloquent). Colonnes sans `facture_id` évitent le
1052 mais cassent le rattachement en silence.

## DÉPLOIEMENT / TEST DIAG

```
git pull origin main
php -d display_errors=1 tools/diag/diag_validation_ambiguous_1257.php
```

Coller la sortie (sections 1–6). Si `nb_factures_presentes_parmi_ids_log=0` → ne pas
conclure. Fix applicatif = lot **#1258** après validation Robin.
