---
suivi: 1226
date: 2026-08-06
sujet: Diag unification dépôts documents (DnD + sélecteur + Outlook)
chantier: documents / uploads / GED
type: diagnostic
statut: poussé
hash: be534460
fichiers:
  - tools/diag/diag_uploads_documents_1226.php
  - docs/suivi/SUIVI_1226_diag_uploads_documents.md
---

# SUIVI #1226 — PHASE 1 DIAGNOSTIC — unification dépôts documents

## PROMPT ENVOYÉ

Unifier drag & drop + sélecteur classique + drop Outlook partout. Référence =
dépôt factures. Lecture seule : inventaire, anatomie référence, Outlook, local vs
SharePoint, limites/sécurité, imports vs documents, architecture, découpage lots.
Script OVH + fiche. Aucun code métier / migration / Vue / rebuild.

## FICHES LUES

- `#1063` — SharePoint / `OutlookAttachmentService` (ews→rest, Mail.Read) ;
  mention **#878 refresh** GED+Outlook (pas de fiche #878 dédiée).
- `#1098` — arborescence SharePoint marchés lotissement.
- `#1108` — erreurs silencieuses vs `FormErrorsSummary` (leçon dépôt).
- `#1203` — onglet DOCUMENTS foncier agrégé.
- `#1212` — import Excel étude Fi (fichier temporaire, pas GED).
- `#1115` — PHP prod `upload_max_filesize=128M` / `post_max_size=130M`.
- `#877` / `#874` / `#875` (via code) — Outlook dépôt factures.

---

## OBJET 1 — INVENTAIRE DES POINTS DE DÉPÔT

### Tableau (synthèse ; détail code dans inventaire agent)

| Écran | Composant Vue | Endpoint | Destination | Table | Max | Types | DnD | Outlook | Multi | MIME réelle |
|-------|---------------|----------|-------------|-------|-----|-------|-----|---------|-------|-------------|
| Dépôt factures | `DepotFactures/Index.vue` | `POST /depot-factures` | disk `factures` (privé) | `depot_factures` | 10×20 Mo | PDF | oui | oui | oui | `mimes:pdf` |
| Dépôt Outlook | idem | `POST /depot-factures/outlook` | idem | `depot_factures` | 20 Mo | PDF | oui | oui | 1 | magic `%PDF` + CT |
| PJ dépôt | `DocumentsJointsPanel.vue` | `…/depot-factures/{uuid}/documents` | disk `factures` | `documents_factures` | 20 Mo | pdf/images | non | non | non | `mimes:` |
| GED programme | `SharePointGedExplorer.vue` | `{api}/upload` (+ `-outlook`) | SharePoint | logs | 30×20 Mo | any | oui | oui | oui | **non** (taille) |
| Commercial GED | `CommercialDocumentsPanel` → explorer | idem | SharePoint | — | idem | any | oui | oui | oui | non |
| Checklist résa | `LotsGrillePanel.vue` | `…/reservations/{id}/documents` | SharePoint (+temp) | `reservation_documents` | 50 Mo | any | non | non | non | **non** |
| Foncier docs | `EntityDocumentsPanel.vue` | `foncier.*.documents.store` | disk `local` privé | `entity_documents` | 10 Mo | pdf/office/images | oui | non | oui | **oui** (service) |
| Tâches suivi | `TacheSuiviDocumentsPanel.vue` | `POST /taches-suivi/{id}/documents` | disk `local` | `tache_suivi_documents` | 10 Mo | pdf/office/images/txt | non | non | oui | `mimes:` |
| Marché pièces | `Marches/Show.vue` | `…/marches/{id}/documents` | **public** + SP | `marche_documents` | 20 Mo | any | non | non | non* | **non** |
| Avenant PJ | idem | `…/avenants/{id}/documents` | public + SP | `avenant_documents` | 20 Mo | any | — | non | oui | **non** |
| Devis marché | idem | `…/devis/{id}/documents` | local | `marche_devis_documents` | 20 Mo | pdf/doc/images | — | non | oui | `mimes:` |
| AO devis | `AppelsOffres/Show.vue` | AoDevis | local→SP job | `ao_devis` | 10 Mo | PDF | — | non | non | `mimes:pdf` |
| AO DCE/estim | `AoDocumentCard.vue` | AppelOffre docs | local→SP | AO cols | 500/50 Mo | zip/pdf | — | non | non | mimes + mime_content_type |
| Juridique | `Juridique/Create\|Show` | pièces | local | `pieces_jointes` | 20 Mo | front accept | non | non | oui | **faible** (size) |
| Signalements | `SignalementsPanel.vue` | SuiviDev | disk **public** | `bug_reports.image_path` | 5 Mo | images | non | non | non | image+mimes |
| Aide médias | `Aide/ArticleEditeur.vue` | aide medias | local | `aide_medias` | 10 Mo | images | non | non | non | oui |
| Communes zonages | `ReferentielCommunes.vue` | import-zonages | mémoire | `communes` | 20 Mo | csv/xlsx | non | non | non | mimes |
| Étude FI | `Scenarios/Show.vue` | import | parse temp | scenario | 10 Mo | xlsx | non | non | non | reader |
| Location public | `Location/Public/Formulaire` | pieces | `gestion_locative` | location PJ | 15 Mo | pdf/jpg/png | non | non | slots | mimes |
| Assistant | `Assistant/Index.vue` | message | local | `assistant_message_documents` | 3×5 Mo | images+pdf | non | non | ≤3 | service |
| Plan lot | `LotsGrillePanel` | lot plan | local | lot | 10 Mo | PDF | non | non | non | mimes |
| Contact docs | ProspectsPanel | contacts docs | local | `contact_documents` | 5 Mo | any | — | non | — | **non** |
| Chrono justif. | `ProgrammeChronologieEtapesPanel` | etape justificatif | SharePoint | etape SP | 20 Mo | pdf/office/images | non | non | non | mimes |
| Logo programme | Create/Edit | programme | public logos | `programmes` | 20 Mo | images | non | non | non | size |
| TMA | `ProgrammeTmasPanel` | TMA | TMA paths | TMA | 50 Mo | pdf/images | — | non | — | mimes |
| NF Habitat | `NfHabitat/Index` | classeur/PJ | local | nf_* | 20–50 Mo | PDF | non | non | — | mimes |
| RIB fournisseur | Demande/Audit | rib | local `ribs/` | demande | 5 Mo | pdf/images | non | non | non | mimes |
| Facture legacy | FactureController | upload | local | facture | 10 Mo | PDF | — | non | non | mimes |
| Import Pegão | ImportPegao | pdf | parse | lots | 20 Mo | PDF | — | non | non | mimes |
| SuiviDev MD | SuiviDev/Index | project-md | repo | — | 1 Mo | md/txt | non | non | non | ext |

\*Create marché : multi `documents_uploads`.

### Mécanismes DISTINCTS (dimensionnement)

| # | Mécanisme | Occurrences approximatives |
|---|-----------|----------------------------|
| A | Dropzone maison PDF + Outlook JSON + picker (dépôt) | 1 écran |
| B | `SharePointGedExplorer` (DnD + Outlook + picker → SP) | 2 (GED + commercial) |
| C | `EntityDocumentsPanel` (DnD + picker → local poly) | foncier multi-entités |
| D | Picker FormData « simple » (1 fichier, toast) | ~15 écrans |
| E | Picker multi FormData (tâches, juridique create…) | ~5 |
| F | Imports données (Excel/CSV/Pegão/MD) | ~5 |

**≈ 6 familles de capture UI**, **≈ 25+ points d’entrée**, **2 surfaces Outlook**
(dépôt + GED explorer). Composants Vue distincts qui « uploadent » : **≥ 18**.

---

## OBJET 2 — ANATOMIE RÉFÉRENCE (dépôt factures)

| Élément | Détail |
|---------|--------|
| Vue | `DepotFactures/Index.vue` (drop zone L1004–1036) — **pas** un composant dédié |
| DnD | HTML5 natif (`dragenter/leave/over/drop`) — **pas** PrimeVue FileUpload |
| Picker | `<input type="file" multiple accept=".pdf">` + click zone |
| Native | `File` → file list → `FormData` `fichiers[]` → `POST /depot-factures` |
| Outlook | `dataTransfer.getData('attachment')` JSON → `POST /depot-factures/outlook` (métadonnées) ; binaire via Graph serveur |
| Limites | Front : `MAX_UPLOAD_FILES=10`, `MAX_FILE_BYTES=20Mo` (constantes Vue) ; serveur : `max:20480` + Outlook `size≤20480000` + service 20 Mo |
| Erreurs | Toasts (non-PDF, trop gros, max fichiers, Outlook id, 422 message) — **pas silencieux** |
| Couplage | Fort : options de masse, préfill programme/marché, OCR/analyse, `router.reload`, PDF-only |

### Extractible en l’état ?

**NON.** La dropzone est **inline** et couplée au workflow dépôt (masse, OCR,
reload liste). Réutilisable : pattern d’événements + `OutlookAttachmentService`
(déjà partagé avec GED). Coût : **extraire un composant de capture** (1 lot),
puis brancher des adapters — pas un copier-coller du bloc Index.

`SharePointGedExplorer` est **déjà** plus proche d’un composant réutilisable
(apiBase, allowWrite, refresh `applyExplorerView`) mais **destination SP figée**.

---

## OBJET 3 — DROP OUTLOOK

| Question | Réponse code |
|----------|--------------|
| Ce que le navigateur envoie | **Métadonnées JSON** (`attachment` DT) : `attachmentItemId`, name, contentType, size, message ids — **pas** le binaire File (Nouvel Outlook). Classic desktop peut aussi fournir des `File`s → chemin native. |
| Graph | `OutlookAttachmentService` : token **utilisateur** (`GraphApiService::getAccessToken`), scopes `Mail.Read` / `Mail.ReadWrite`, `translateExchangeIds` ewsId→restId, `GET …/attachments/{id}/$value` |
| PHP-FPM | Oui : Http Graph dans la requête web. **Aucun job/CLI** dans le chemin actuel. Généralisation = garder l’appel dans un controller web. |
| Consentement | Scope Azure + re-login ; sinon `RuntimeException` explicite « Reconnexion Microsoft… Mail.Read absente ». Dégradation : DnD fichier + sélecteur restent. |
| #878 | Pas de fiche `SUIVI_878_*`. Mention #1063 « GED + Outlook drop (#878 refresh) ». Code actuel GED appelle `applyExplorerView(data)` après upload/Outlook — **bug possiblement atténué** ; à **reproduire en prod** avant généralisation (ne pas generaliser un refresh cassé). |

---

## OBJET 4 — DESTINATIONS

| Règle réelle (code) | Exemples |
|---------------------|----------|
| Disque privé `local` / `factures` | entity_documents, tâches, dépôt, joints, aide, assistant |
| Disque **public** | marche_documents, avenant (sous `storage/app/public`), logos, bug images |
| SharePoint direct | GED explorer, commercial GED |
| Temp local → SharePoint | réservation checklist, AO jobs, avenant/marché best-effort SP |
| Parse only (pas stocké GED) | étude Fi, zonages, Pegão |

**Composant agnostique possible ?** OUI pour la **capture** (fichiers + Outlook
meta). NON pour un unique « upload » opaque : SharePoint est asynchrone / distant /
faillible (76 `Log::warning` SharePointService, dette #1063). Props :
`onFiles(File[])` / `onOutlookAttachment(meta)` + caller choisit adapter
`LocalDiskAdapter` | `SharePointAdapter` | `ImportParserAdapter`.

Arborescence foncier SharePoint (tranchée, non implémentée) : le composant de
capture **n’a pas à connaître** le routage ; l’adapter SharePoint (futur) reçoit
`folderPath` / `item_id` — **compatible sans réécriture** du capture.

UX SP lent : GED toast erreur + parfois refresh contenu erreur ; warnings SP peu
surveillés → risque échec semi-muet hors toast.

---

## OBJET 5 — LIMITES / SÉCURITÉ

| Source | Valeur |
|--------|--------|
| Dépôt / Outlook / GED fichier | 20 Mo |
| Foncier / tâches | 10 Mo |
| Réservation / TMA / NF classeur | 50 Mo |
| AO DCE | **500 Mo** (proche plafond PHP 128M → **risque incohérent** si PHP 128M) |
| Prod PHP (#1115) | upload 128M / post 130M |
| Stub local diag | upload **2M** / post **8M** (≠ prod) |

### Trous de sécurité (prioritaires)

1. **Pas de `mimes:`** : GED, marché pièces, avenant, contact docs, réservation,
   juridique serveur → un `.php` renommé peut passer la validation Laravel size-only.
2. **Disque public** pour marché/avenant/logos/bugs : fichiers potentiellement
   servis sous `/storage` — vérifier ACL URL en Phase 2.
3. Disk `local` : `storage/app/private` (hors docroot classique) mais
   `filesystems` a `'serve' => true` — s’assurer qu’aucune route ne stream sans ACL.
4. Antivirus : **aucun** trouvé.

---

## OBJET 6 — CE QUI N’EST PAS UN DOCUMENT

| Import | Traitement | Recommandation |
|--------|------------|----------------|
| Étude Fi Excel #1212 | parse, temp | Même **capture** UI, adapter `ImportParser` |
| Zonages communes CSV/XLSX | parse → colonnes | idem |
| Pegão PDF | parse lots | idem |
| PROJECT.md SuiviDev | remplace fichier repo | hors composant métier docs |
| Logo programme | asset image | capture simple OK, pas GED |

Attente Robin confirmée par le diag : **un composant de capture, traitements
aval distincts** (document vs import).

---

## OBJET 7 — ARCHITECTURE PROPOSÉE (écrite)

### `FileCaptureZone.vue` (nom provisoire)

**Gère** : DnD HTML5, picker, Outlook `attachment` JSON (si `enableOutlook`),
multi, limites props, progression locale, toasts d’erreur **visibles**, liste
avant envoi, retrait.

**Props** : `accept`, `maxFiles`, `maxBytes`, `multiple`, `enableOutlook`,
`disabled`, `label`.

**Events** : `files-selected(File[])`, `outlook-attachments(meta[])`,
`reject({reason, file?})`.

**Slots** : hint, actions.

**Ne gère pas** : destination, ACL, rattachement, MIME métier final, SharePoint.

**Dégradation** : `enableOutlook=false` ou 401 Mail.Read → message + DnD/picker
seuls ; adapter SP indisponible → toast explicite (jamais silence).

### Migration écrans (pas big bang)

1. Extraire capture + documenter adapters.
2. Brancher surfaces **sans** Outlook d’abord (EntityDocuments, tâches, joints).
3. Unifier Outlook dépôt ↔ GED (même meta handler).
4. Marché / avenant (renforcer MIME + sortir du public si décidé).
5. Imports (étude Fi, zonages) en dernier.

Écrans pouvant rester en l’état sans incohérence UX visible : Intacct TestBill,
SuiviDev PROJECT.md, NF Habitat (PDF métier étroit), RIB (5 Mo dédié).

---

## OBJET 8 — DÉCOUPAGE LOTS

| Lot | Contenu | Fichiers | Risque | Dépendance |
|-----|---------|----------|--------|------------|
| 1226-P2a | `FileCaptureZone` + story/doc usage | **nouveau** `.vue` + éventuellement composable | moyen | — |
| 1226-P2b | Brancher `EntityDocumentsPanel` | `EntityDocumentsPanel.vue` (disputé) | moyen | P2a |
| 1226-P2c | Brancher tâches + DocumentsJoints | 2 panels | faible | P2a |
| 1226-P2d | Harmoniser Outlook meta (depot Index + GedExplorer) | `Index.vue` dépôt (**très disputé**), `SharePointGedExplorer.vue` | **élevé** | P2a ; **sérialiser** vs autres lots dépôt |
| 1226-P2e | MIME dur marché/avenant/GED (+ option hors public) | controllers + Show marché | élevé / sécu | décision Robin |
| 1226-P2f | Imports capture (étude Fi, zonages) | Scenarios/Show, ReferentielCommunes | moyen | P2a |
| 1226-P2g | Repro + fix #878 refresh GED Outlook | GedExplorer + fiche | moyen | repro prod |

**Migration DB ?** Non nécessaire pour l’unification capture. Pas de table unifiée
recommandée en Phase 2a. Colonne « origine » (outlook/native) = optionnel plus tard.

**Sérialisation** : jamais parallèle sur `DepotFactureSaisieForm` / `Index` dépôt /
`AppShell` / `Marches/Show` / `EntityDocumentsPanel` / `manifest.json`.

---

## SCRIPT

```bash
php tools/diag/diag_uploads_documents_1226.php
```

Sections : §1 schéma tables · §2 volumes · §3 local/SP · §4 refs code · §5 PHP
ini · §6 anomalies échantillon. Gardes `hasTable`/`hasColumn`.

## DÉPLOIEMENT / TEST

Diag only : `git pull` ; lancer le script sur OVH ; coller sortie dans cette fiche
ou ticket. Pas de migrate / journal / build.

## LEÇON

Trois façons de déposer ≠ un seul composant aujourd’hui : **6 familles**, Outlook
déjà bifide (dépôt vs GED) via le même service Graph. Extraire la **capture**,
pas le pipeline métier. Sécurité : plusieurs endpoints size-only. #878 à
reproduire avant de généraliser Outlook.
