---
suivi: 1239
date: 2026-08-07
sujet: rattrapage_sharepoint_item_id
chantier: sharepoint / documents
type: feature
statut: poussé
hash: 212434ef
fichiers:
  - database/migrations/2026_08_07_140000_add_sharepoint_item_id_ao_tables.php
  - app/Services/SharePointService.php
  - app/Services/SharePointItemIdRattrapageService.php
  - app/Http/Controllers/Admin/SharePointItemIdRattrapageController.php
  - app/Jobs/CreateSharePointAoFolder.php
  - app/Models/AppelOffre.php
  - app/Models/AoDevis.php
  - routes/web.php
  - tools/diag/rattrapage_sharepoint_item_id_1239.php
  - tools/diag/diag_liens_sharepoint_1236.php
  - tests/Unit/SharePointItemIdRattrapageMatchTest.php
  - database/data/journal_mises_a_jour_post_2026_06_12.php
  - docs/suivi/SUIVI_1239_rattrapage_item_id.md
---

# SUIVI #1239 — Alimenter `sharepoint_item_id`

## PROMPT ENVOYÉ

Alimenter `sharepoint_item_id` partout (migration AO/devis, persistance à
l'écriture, rattrapage inventaire + Graph web). Prérequis refonte arborescence.
Ne pas toucher arbo SP ni `sharepoint_url`. Simulation par défaut.

## FICHES LUES

- `#1236` — résolution par item_id inopérante sans données ; LEÇON URL = cache fragile.
- `#1232` — inventaire JSON avec `files[].item_id` = source (a) sans Graph.
- `#1228` — colonnes marché/avenant déjà en schéma ; fichiers locaux OK.

## OBJET 1 — Migration

`2026_08_07_140000_add_sharepoint_item_id_ao_tables.php` :
- `ao_devis.devis_pdf_sharepoint_item_id`
- `appels_offres.dce_sharepoint_item_id`, `estimatif_moe_sharepoint_item_id`,
  `sharepoint_folder_item_id`
- Index courts ; `Schema::hasColumn` ; `down()` sûre.
- `marche_documents` / `avenant_documents` : **non recréées** (déjà présentes).

Repo local fichiers migrations : 405 → 406. Prod attendu +1 (273→274 si base à 273).

## OBJET 2 — Points d'écriture (exhaustif)

| Point | Avant | Après #1239 |
|-------|-------|-------------|
| `MarcheController` dépôt marché | déjà `uploadFichierItem` → URL+id | inchangé (OK) |
| `MarcheController` PJ avenant | idem | inchangé (OK) |
| `uploadAoDevis` | `uploadFichierApp` → URL seule | id + URL |
| `uploadAoDocument` (DCE / estimatif) | URL seule | id + URL |
| `creerDossierAppelOffre` / `CreateSharePointAoFolder` | folder URL seule | + `sharepoint_folder_item_id` |
| `uploadFichierApp` | retour `?string` webUrl | `?array{id,webUrl,name}` |
| TMA / réservation checklist | URL seule (pas de colonne item) | webUrl extrait ; **hors rattrapage Robin** — listé |

Cause racine AO : `uploadFichierApp` jetait l'`id` Graph.

## OBJET 3 — Rattrapage

- CLI : `tools/diag/rattrapage_sharepoint_item_id_1239.php` (simulation défaut, `--apply`)
  — source **(a) inventaire #1232** (match URL puis **nom fichier / programme** pour URLs
  stale TRITONS).
- WEB Graph **(b)** : `GET /admin/sharepoint/rattrapage-item-id-1239?graph=1&limit=40&offset=0`
  (`&apply=1` pour écrire) — fractionné, GS_DSI/ADMIN.
- Idempotent ; `DB::table()->update` sur la seule colonne item_id ; sortie ligne à ligne.

## OBJET 4 — 47 liens TRITONS

Match prioritaire par **nom de fichier dans l'inventaire du programme 2**, pas par le
chemin `env34-002--triton` périmé. Chiffres exacts = sortie script OVH après inventaire
programme 2 à jour.

## OBJET 5 — Irréductibles

Listés nominativement en fin de script (`IRREDUCTIBLES`). Laisser NULL. Conditionnent
la refonte arbo.

## OBJET 6 — Inventaire programme 2

Après rattrapage + purge/reset inventaire : manuels attendus ~39 (vs 85). À coller OVH.

## SYNTHÈSE

Sans item_id à l'écriture, #1236 était mort ; ce lot ferme la cause + rattrape l'historique.

## DÉPLOIEMENT-TEST

```text
git pull origin main
php artisan migrate --force
php artisan route:clear
php artisan view:clear
php artisan journal:sync
# inventaire prog 2 à jour si besoin
php tools/diag/rattrapage_sharepoint_item_id_1239.php
php tools/diag/rattrapage_sharepoint_item_id_1239.php --apply
# restes Graph : URL web rattrapage-item-id-1239?graph=1&apply=1
php tools/diag/diag_liens_sharepoint_1236.php
```

## LEÇON

Persister l'`id` Graph **dans la même transaction métier** que l'URL. Un rattrapage
sans correction d'écriture se périme immédiatement. URL stale ≠ fichier perdu :
retrouver par **nom** dans l'arbo réelle (inventaire).
