---
suivi: 1110
date: 2026-07-30
sujet: Foncier — fiche offre (URL, inline, documents provisoires)
chantier: module-foncier
type: feature
statut: poussé
hash: d98d8baa
fichiers:
  - database/migrations/2026_07_30_234000_create_entity_documents_table.php
  - app/Models/EntityDocument.php
  - app/Models/OffreFonciere.php
  - app/Services/Foncier/OffreDocumentService.php
  - app/Services/Foncier/FieldAuditService.php
  - app/Http/Controllers/Foncier/OffreFonciereController.php
  - routes/web.php
  - resources/js/Pages/Foncier/Offres/Show.vue
  - resources/js/Pages/Foncier/Dossiers/Show.vue
  - tests/Feature/Foncier/FoncierOffreFicheDocumentsTest.php
  - tests/Feature/Foncier/FoncierOffreFicheDocumentsSourceTest.php
  - tests/Feature/Foncier/FoncierOffrePerimetreSourceTest.php
  - tests/Unit/Foncier/OffreDocumentServiceTest.php
  - database/data/journal_mises_a_jour_post_2026_06_12.php
  - public/build/
  - docs/suivi/SUIVI_1110_foncier_fiche_offre_documents.md
---

## PROMPT ENVOYÉ

SUIVI #1110 — l’offre devient une fiche (URL propre, inline, documents provisoires).
Prérequis #1102/#1107/#1108. Pas de SharePoint. Liste blanche PATCH + audit sensible.

## PHASE 1 — AUDIT DOCUMENTS

Confirmé : pas de table polymorphe documents générique réutilisable pour le foncier.
Existent ailleurs : `pieces_jointes` (juridique / attachable), PJ NF — domaines dédiés,
pas adaptés aux jets d’offre. Décision : table `entity_documents` (morph + classement
+ reference_externe pour basculement futur sans migration). Branchée uniquement sur
`OffreFonciere` dans ce lot. Disque `local` → `storage/app/private` (jamais public/storage).

## DÉCISION « RÉPONSE »

Conservée **dans la liste du dossier** (scan rapide des offres en attente) **et** sur
la fiche (instruction complète). Motif : la liste sert l’opération quotidienne ; la
fiche sert l’instruction. Pas de migration forcée de l’action.

## LIVRÉ

- `foncier.offres-foncieres.show` → Inertia `Foncier/Offres/Show` (plus un redirect)
- Inline via `InlineEditableField` + PATCH whitelist `champsInlineAutorises()`
- Validation partagée `offreFieldRules()` ; `foncier.prix` pour montant/prix_m2
- Audit `AuditsFieldChanges` sur l’offre ; sensibles : montant, prix_m2, montant_contre_offre
- Documents privés : upload multi, inline PDF/images, delete physique + ligne
- Limites : **2 Mo** / fichier (aligné `upload_max_filesize=2M`), **10** / offre
- Création reste dans l’onglet dossier ; « Ouvrir » → fiche ; formulaire d’édition déplié retiré

## PREUVE FILTRAGE SENSIBLE (couche données)

```
{"id":null,"champ":"montant","champ_libelle":"Montant","ancienne_valeur":null,"nouvelle_valeur":null,"valeurs_masquees":true,"user_id":7,"user_name":null,"created_at":"...","created_at_label":"..."}
```

Valeurs 200000/180000 **absentes** du JSON. Test source `test_filtrage_sensible_present_ligne_json_sans_valeurs`.

## LEÇON

Un document provisoire n’a pas à polluer un système de classement — et un historique
de prix non filtré rouvre par l’audit ce que la permission ferme.
