gmao/docs/ui-audit/DOCUMENT_CENTER_PROPOSAL_V2.md

212 lines
10 KiB
Markdown
Raw Normal View History

# Centre documentaire global — proposition V2
**Statut : étude et spécification uniquement.** Aucun code applicatif, modèle,
route, migration, permission ou menu n'est créé par ce document.
## A. Décisions de périmètre
La V1 agrégera en lecture les documents déjà attachés à trois sources :
- interventions (`InterventionDocument`) ;
- équipements (`EquipmentDocument`) ;
- produits d'entretien (`ProductDocument`).
Il n'y aura ni table `Document` commune, ni copie physique, ni upload ou
suppression depuis `/documents/`. `Contract.contract_file`, la prévention et
les données personnelles du personnel sont explicitement hors V1. Outlook,
ENT et les autres intégrations restent hors périmètre.
## B. Cartographie réelle actuelle
| Source | Table / modèle | Parent obligatoire | Métadonnées actuelles | Routes observées |
|---|---|---|---|---|
| Intervention | `intervention_documents` / `InterventionDocument` (`app_new/core/models/maintenance.py`) | `intervention_id` | `filename`, `filepath`, `description`, `uploaded_at`, `uploaded_by_id` | `/documents/upload/intervention/<id>`, téléchargement, édition de description, suppression |
| Équipement | `equipment_documents` / `EquipmentDocument` (`app_new/core/models/equipment.py`) | `equipment_id` | `filename`, `filepath`, `description`, `uploaded_at`, `uploaded_by_id` | famille `/documents/...` et famille `/equipments/<id>/documents/...` |
| Produit | `cleaning_product_documents` / `ProductDocument` (`app_new/core/models/cleaning.py`) | `commercial_product_id` | `document_type`, `title`, `filename`, `filepath`, `version`, `published_at`, `uploaded_at`, `uploaded_by_id`, `is_current` | `GET /cleaning/documents` ; upload/téléchargement produit à compléter dans une évolution séparée |
| Contrat | `contracts.contract_file` | aucun objet documentaire | chemin texte unique | non indexé par le centre V1 |
`/documents/` répond actuellement 404 : cette absence est le bug UI-011,
mais elle ne justifie pas la création d'un stockage parallèle.
## C. Stockage physique et risques
`Config.UPLOAD_FOLDER` pointe vers `app_new/uploads`, monté par Docker dans le
volume `gmao_uploads` (`/app/app_new/uploads`). La limite globale est de 50 MiB.
Les conventions historiques sont `uploads/interventions/<uuid>_<nom>`,
`uploads/equipments/<uuid>_<nom>` et
`uploads/documents/<equipment_id>/<uuid>.<extension>`.
Les extensions autorisées sont contrôlées et `secure_filename` est utilisé,
mais le MIME réel n'est pas inspecté, le nom original n'est pas toujours
séparé du nom interne et taille/MIME/hash ne sont pas uniformément conservés.
Les téléchargements passent par `send_file` et ne sont pas exposés comme
répertoire statique ; il faut néanmoins vérifier existence, racine autorisée,
parent et permission à chaque téléchargement. Les écritures interrompues
peuvent laisser un fichier orphelin ou une ligne sans fichier. Une future
maintenance devra produire un rapport d'orphelins sans supprimer
automatiquement les données.
## D. Dette de routes équipement
Deux blueprints écrivent des équipements dans deux chemins et avec deux
contrats de métadonnées. La route historique tente notamment de renseigner
`file_type` et `file_size`, champs absents de `EquipmentDocument`. Avant le
centre, il faut choisir un adaptateur de service de téléchargement commun,
conserver les anciens chemins lisibles, puis déprécier une famille de routes
sans casser les URL existantes. Cette convergence est une condition de
qualité, mais ne doit pas entraîner de déplacement physique automatique en V1.
## E. Architecture recommandée : adaptateurs de lecture
Chaque source implémentera conceptuellement un adaptateur qui transforme une
ligne spécialisée en un résultat commun en mémoire :
```text
DocumentResult
source_kind intervention | equipment | product
document_id identifiant de la ligne source
parent_id identifiant de l'objet métier
display_name nom lisible
document_type type métier affichable
description description éventuelle
date uploaded_at ou published_at
context_label Intervention…, Équipement…, Produit…
context_url URL de détail autorisée
location_summary chemin calculé ou « 3 emplacements »
download_target action contrôlée par l'adaptateur
```
La vue globale ne devient jamais propriétaire du fichier. La recherche peut
utiliser trois sélections alignées et `UNION ALL`, ou trois requêtes paginées
fusionnées dans un premier incrément. Les relations parent restent la source
de vérité.
### Alternative étudiée : index commun
Une table d'index serait utile pour 10 000+ documents, tags, OCR ou recherche
plein texte. Elle ajouterait toutefois un backfill, des synchronisations et le
risque d'index périmé. Elle pourrait être introduite plus tard comme index
réparable (jamais comme propriétaire physique), sans être créée pour la V1.
## F. Types métier et migration future
Les produits conservent `fds` et `fiche_technique` ainsi que les valeurs déjà
présentes. Les interventions et équipements recevront, lors d'une migration
future, un champ `document_type` court et indexé avec les valeurs :
- `notice` — Notice / manuel ;
- `facture` — Facture ;
- `devis` — Devis ;
- `rapport` — Rapport / compte rendu ;
- `certificat` — Certificat / contrôle ;
- `photo` — Photo ;
- `plan` — Plan ;
- `autre` — Autre.
La migration ajoutera une colonne nullable ou avec défaut `autre`, remplira
les documents historiques avec `autre`, puis pourra rendre le champ non nul
après vérification. Aucun fichier ne sera réécrit. Les adaptateurs afficheront
`Autre` pour une valeur absente ou inconnue.
## G. Localisations multiples
La localisation est calculée à partir des relations : équipement → salle →
zone → bâtiment ; produit → lots/stock → emplacements. Si un produit est dans
plusieurs emplacements, le résultat affichera par exemple **3 emplacements**
et ouvrira le détail des chemins individuels. Aucun chemin bâtiment/zone/salle
ne sera dénormalisé dans une future table documentaire.
## H. RBAC
La permission nouvelle proposée est `documents.view`. Aucun
`documents.manage` n'est créé. Un résultat est visible uniquement si :
```text
documents.view ET permission de lecture de la source
```
Donc `intervention.view`, `patrimoine.view` ou `stock.view` s'ajoutent selon
la source. Le téléchargement réutilise la même décision en V1 : pas de
`documents.download` séparé, sauf besoin de séparation démontré. Édition et
suppression restent dans le module source et ses permissions (`manage`). Les
liens vers un parent sont masqués si l'utilisateur ne peut pas le lire.
## I. Interface proposée
`/documents/` afficherait :
1. titre **Centre documentaire** et phrase d'aide ;
2. recherche (nom, titre, description, parent, référence) ;
3. filtres principaux : type, source, période ;
4. filtres avancés : bâtiment, zone, salle, équipement, produit ;
5. liste paginée : document, type, lié à, localisation, date, actions.
Les actions sont **Consulter/Télécharger** et **Ouvrir le contexte**. Aucun
upload global ni suppression globale. Un état vide expliquera que les fichiers
s'ajoutent depuis les fiches intervention, équipement ou produit et proposera
des liens de création autorisés.
## J. Vue FDS
`/documents/fds` sera une vue filtrée, non un second stockage : produit,
référence commerciale, fabricant/fournisseur, version, date, statut courant,
emplacements et téléchargement. Une future fonction « Classeur FDS » pourra
exporter une liste/PDF, mais n'appartient pas au MVP.
## K. Recherche, filtres et performance
SQL classique suffit pour la V1 : recherche préfixe/contient sur les champs
déjà disponibles, filtrage par source/type/date et pagination obligatoire.
À 100 ou 1 000 documents, les adaptateurs sont suffisants. À 10 000, mesurer
les `UNION ALL`, ajouter index sur parent/date/type et envisager un index
commun. Les jointures de localisation seront paginées et chargées de façon
prévisible, sans N+1.
## L. Sécurité et intégrité
Avant implémentation, spécifier : validation extension + MIME, limite de
taille, `Content-Disposition` sûr, racine de chemin canonique, refus d'IDOR,
contrôle parent/RBAC avant téléchargement, absence d'exécution de fichiers,
et journalisation des erreurs. Prévoir des tests fichier manquant, ligne
orpheline, chemin hors racine, document supprimé et permission source absente.
## M. Compatibilité historique
Les trois tables et leurs fichiers restent intacts. Les noms ou types absents
sont adaptés à la lecture (`Autre`, nom de fichier sécurisé). `contract_file`
reste exclu et documenté comme dette, sans conversion silencieuse.
## N. Tests avant développement
- intervention, équipement et produit visibles ;
- recherche, filtres, pagination et FDS ;
- plusieurs localisations produit ;
- lien vers contexte avec permission ;
- absence d'IDOR et téléchargement contrôlé ;
- utilisateur sans `documents.view` ;
- utilisateur sans permission source ;
- fichier manquant/orphelin ;
- suppression depuis le module source retirant le résultat global ;
- non-régression des routes historiques.
## O. MVP et évolutions
**V1 obligatoire :** agrégation des trois sources, `/documents/`, recherche,
types/source/période, pagination, localisation calculée, téléchargement
sécurisé, lien de contexte, RBAC `documents.view`, vue `/documents/fds`.
**Plus tard :** index SQL commun, tags, OCR, plein texte PDF, versionnage,
prévisualisation, classeur FDS, contrats, prévention et documents généraux.
L'ajout d'un document reste toujours contextuel dans la V1.
## P. Questions à valider avant développement
1. Confirmer les libellés et la liste des types intervention/équipement.
2. Confirmer que `documents.view` doit être accordée en plus des permissions
sources.
3. Confirmer qu'aucun `documents.download` distinct n'est requis en V1.
4. Choisir l'ordre et le style de consolidation des deux familles de routes
équipement.
5. Définir une politique de rétention pour fichiers orphelins.
6. Décider plus tard si `contract_file` mérite une migration dédiée.