diff --git a/CLAUDE.md b/CLAUDE.md index 43e9294..b0234f9 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -18,6 +18,9 @@ Mono-repo avec backend Symfony et frontend Nuxt en submodule git. | Auth | Session-based (cookies, pas JWT) | | | Containers | Docker Compose | | +## Glossaire Métier +Voir `docs/GLOSSAIRE_METIER.md` — glossaire complet du domaine métier (concepts, workflows utilisateur, correspondance métier↔code). À consulter pour comprendre le "pourquoi" derrière le code. + ## Project Structure ``` @@ -25,6 +28,11 @@ Inventory/ # Backend Symfony (repo principal) ├── src/Entity/ # Entités Doctrine (annotations PHP 8 attributes) ├── src/Controller/ # Controllers custom (session, comments, audit…) ├── src/EventSubscriber/ # Audit subscribers (onFlush) +├── src/Service/ # Services métier (sync, conversion, storage…) +├── src/Enum/ # Enums PHP (DocumentType, ModelCategory) +├── src/DTO/ # Data Transfer Objects (sync workflow) +├── src/Filter/ # Filtres API Platform custom +├── src/Command/ # Commandes Symfony CLI (compress-pdf, create-profile…) ├── config/ # Config Symfony ├── migrations/ # Migrations Doctrine (raw SQL PostgreSQL) ├── docker/ # Dockerfile + .env.docker @@ -111,7 +119,8 @@ Le frontend est un submodule git. Lors d'un commit frontend : #### Entités de normalisation (slots & skeleton requirements) Remplacent les anciennes colonnes JSON `structure` et `productIds` par des tables relationnelles : -- **Slots** (données réelles d'un composant) : `ComposantPieceSlot`, `ComposantSubcomponentSlot`, `ComposantProductSlot` +- **Slots composant** (données réelles d'un composant) : `ComposantPieceSlot`, `ComposantSubcomponentSlot`, `ComposantProductSlot` +- **Slots pièce** (données réelles d'une pièce) : `PieceProductSlot` - **Skeleton Requirements** (définitions du ModelType) : `SkeletonPieceRequirement`, `SkeletonProductRequirement`, `SkeletonSubcomponentRequirement` ### Patterns @@ -127,6 +136,8 @@ Remplacent les anciennes colonnes JSON `structure` et `productIds` par des table - `MachineCustomFieldsController` — `/api/machines/{id}/add-custom-fields` (POST) : initialise les CustomFieldValue manquants pour une machine. - `CustomFieldValueController` — `/api/custom-fields/values/*` : CRUD + upsert pour les valeurs de champs perso. - `ComposantPieceSlotController` — `/api/composant-piece-slots/{id}` (PATCH) : mise à jour des slots pièce d'un composant. +- `ComposantProductSlotController` — `/api/composant-product-slots/{id}` (PATCH) : mise à jour des slots produit d'un composant. +- `ComposantSubcomponentSlotController` — `/api/composant-subcomponent-slots/{id}` (PATCH) : mise à jour des slots sous-composant d'un composant. - `SessionProfileController` — `/api/session/profile` (GET/POST/DELETE) : auth session (login/logout/current user). - `SessionProfilesController` — `/api/session/profiles` (GET) : liste des profils disponibles pour la session. - `AdminProfileController` — `/api/admin/profiles` : CRUD profils, gestion rôles et mots de passe (ROLE_ADMIN). @@ -136,6 +147,7 @@ Remplacent les anciennes colonnes JSON `structure` et `productIds` par des table - `DocumentQueryController` — `/api/documents/{entity}/{id}` (GET) : documents par site/machine/composant/pièce/produit. - `DocumentServeController` — `/api/documents/{id}/file|download` (GET) : servir/télécharger fichiers. - `ModelTypeConversionController` — `/api/model_types/{id}/conversion-check|convert` : vérification et conversion de ModelType. +- `ModelTypeSyncController` — `/api/model_types/{id}/sync-preview|sync-confirm` (POST) : prévisualisation et application de sync ModelType→Composants. - `HealthCheckController` — `/api/health` (GET) : health check. ### Custom Fields — Architecture @@ -143,11 +155,27 @@ Remplacent les anciennes colonnes JSON `structure` et `productIds` par des table - **Machines** : définitions = entités `CustomField` liées directement via `machineId` FK (pas de ModelType) - Les deux partagent la même entité `CustomFieldValue` pour stocker les valeurs -### Normalisation JSON → Tables (architecture slots) -Les anciennes colonnes JSON `structure` et `productIds` des Composants ont été remplacées par des tables relationnelles : -- **ModelType** définit le squelette via `SkeletonPieceRequirement`, `SkeletonProductRequirement`, `SkeletonSubcomponentRequirement` -- **Composant** stocke les données réelles via `ComposantPieceSlot`, `ComposantProductSlot`, `ComposantSubcomponentSlot` -- Chaque slot référence son skeleton requirement (`skeletonRequirement` FK) + l'entité sélectionnée + position +### Enums (`src/Enum/`) +- `DocumentType` — types de documents (photo, schéma, facture, etc.) +- `ModelCategory` — catégories de ModelType + +### Services (`src/Service/`) +- `ModelTypeSyncService` — synchronise les skeleton requirements d'un ModelType vers les composants existants +- `ModelTypeCategoryConversionService` — conversion de catégorie d'un ModelType +- `SkeletonStructureService` — gestion de la structure skeleton (requirements) +- `DocumentStorageService` — stockage et gestion des fichiers documents +- `PdfCompressorService` — compression des PDFs uploadés +- `src/Service/Sync/` — stratégies de sync par type de slot (tagged `app.sync_strategy`) + +### DTOs (`src/DTO/`) +- `SyncConfirmation`, `SyncPreviewResult`, `SyncExecutionResult` — objets de transfert pour le workflow de sync ModelType + +### Filters (`src/Filter/`) +- `MultiSearchFilter` — filtre API Platform pour recherche OR sur plusieurs champs (ex: name + reference) + +### EventSubscribers notables (non-audit) +- `PieceProductSyncSubscriber` — sync automatique des PieceProductSlots +- `UniqueConstraintSubscriber` — traduit les erreurs de contrainte unique PG en messages utilisateur lisibles ### Rôles (hiérarchie) ``` @@ -223,7 +251,7 @@ make test-setup # Créer/mettre à jour le schéma test ### Pattern de test - Hériter de `AbstractApiTestCase` (helpers auth + factories) - Ne PAS faire de TRUNCATE/cleanup dans tearDown — DAMA s'en occupe par rollback -- Factories : `createProfile()`, `createMachine()`, `createSite()`, `createComposant()`, `createPiece()`, `createProduct()`, `createConstructeur()`, `createCustomField()`, `createCustomFieldValue()`, `createModelType()`, `createMachineComponentLink()`, `createMachinePieceLink()`, `createMachineProductLink()`, `createComposantPieceSlot()`, `createComposantSubcomponentSlot()`, `createComposantProductSlot()` +- Factories : `createProfile()`, `createMachine()`, `createSite()`, `createComposant()`, `createPiece()`, `createProduct()`, `createConstructeur()`, `createCustomField()`, `createCustomFieldValue()`, `createModelType()`, `createMachineComponentLink()`, `createMachinePieceLink()`, `createMachineProductLink()`, `createComposantPieceSlot()`, `createComposantSubcomponentSlot()`, `createComposantProductSlot()`, `createPieceProductSlot()` - Auth : `createViewerClient()`, `createGestionnaireClient()`, `createAdminClient()`, `createUnauthenticatedClient()` ## URLs Locales