diff --git a/CARNET_DE_BORD.md b/CARNET_DE_BORD.md
deleted file mode 100644
index d23778c..0000000
--- a/CARNET_DE_BORD.md
+++ /dev/null
@@ -1,425 +0,0 @@
-# đ Carnet de Bord - Migration Inventory â Symfony
-
-**Projet** : Migration backend NestJS/Prisma â Symfony/API Platform
-**Début** : 2026-01-10
-**Objectif** : Migrer vers Symfony + JWT + API Platform propre et maintenable
-
----
-
-## đ Convention de liaison des commits (INV)
-
-- **Format** : `[INV-YYYYMMDD-XX]`
-- **Usage** : mĂȘme code dans les commits du backend **et** du frontend + ajout ici pour retrouver le duo rapidement.
-
-## đ§Ÿ Journal des liaisons INV
-
-- INV-20260111-01 : ajout du lien submodule `Inventory_frontend` (commit backend : `987aa5c`, commit frontend : `936a73f`)
-- INV-20260111-02 : alignement front API Platform + sessions (commit backend : `f7fc1bd`, commit frontend : `e99f053`)
-
-## đŻ Contexte
-
-- **Situation initiale** :
- - `Inventory_backend/` : NestJS + Prisma (fonctionnel, ~11k lignes)
- - `Inventory_frontend/` : Nuxt 3 (fonctionnel, 105 fichiers)
- - Base de données PostgreSQL avec données en production
-
-- **Objectif** :
- - Backend Symfony 8 + API Platform + JWT
- - Garder les donnĂ©es existantes (migration Prisma â Doctrine)
- - Frontend Nuxt connecté au nouveau backend
- - Docker : 2 backends en parallĂšle pendant transition
-
----
-
-## â
Phase 1 : PrĂ©paration (TERMINĂE - 10/01/2026)
-
-### Ce qui a été fait
-
-#### 1. Docker & Infrastructure â
-- **pgAdmin ajouté** au docker-compose.yml
- - Port : 5050
- - Login : admin@admin.com / admin
- - Container : `pgadmin-inventory`
- - Volume persistant : `pgadmin_data`
- - **Serveur PostgreSQL pré-configuré** :
- - Fichier `docker/pgadmin/servers.json` monté automatiquement
- - Fichier `docker/pgadmin/pgpass` pour authentification sans mot de passe
- - Connexion automatique à `db:5432/inventory` au démarrage
- - Nom du serveur : "Inventory PostgreSQL"
-
-#### 2. Bundles Symfony installĂ©s â
-```bash
-# Versions installées
-- lexik/jwt-authentication-bundle: v3.2.0
-- vich/uploader-bundle: v2.9.1
-- symfony/uid: 8.0.*
-```
-
-#### 3. JWT Configuration â
-- **Clés RSA générées** : `config/jwt/private.pem` + `public.pem`
-- **security.yaml configuré** :
- - Firewall `login` : pattern `^/api/login_check` avec `json_login`
- - Firewall `api` : pattern `^/api` avec `jwt` authenticator
- - Provider : `app_user_provider` (entité Profile via email)
- - Password hasher : bcrypt auto
-
-#### 4. EntitĂ© Profile créée â
-**Fichier** : `src/Entity/Profile.php`
-
-**Caractéristiques** :
-- Implémente `UserInterface` + `PasswordAuthenticatedUserInterface`
-- Champs :
- - `id` : string (30 chars, CUID-compatible pour Prisma)
- - `email` : string unique (username pour JWT)
- - `password` : string (hashed)
- - `roles` : array JSON (ROLE_USER par défaut)
- - `firstName`, `lastName` : string
- - `isActive` : boolean
- - `createdAt`, `updatedAt` : DateTimeImmutable
-- Repository : `ProfileRepository` avec `PasswordUpgraderInterface`
-- API Platform : endpoints CRUD auto-générés
-
-#### 5. Base de DonnĂ©es â
-- **Migration créée** : `Version20260110175413`
-- **Table** : `profiles` créée avec succÚs
-- **Utilisateur test créé** :
- ```
- Email: admin@admin.com
- Password: admin123
- Roles: ['ROLE_USER', 'ROLE_ADMIN']
- ```
-
-#### 6. API Platform â
-- **Endpoint racine** : http://localhost:8081/api/
-- **Réponse** :
- ```json
- {
- "@context": "/api/contexts/Entrypoint",
- "@id": "/api/",
- "@type": "Entrypoint",
- "profile": "/api/profiles"
- }
- ```
-- **OpenAPI Docs** : Configurées (à tester)
-
-#### 7. Configuration Apache â
-- **VirtualHost** : `docker/php/config/vhost.conf`
-- **DocumentRoot** : `/var/www/html/public`
-- **AllowOverride** : All (pour `.htaccess`)
-- **Port** : 8081 (Apache) â accessible depuis l'hĂŽte
-
-#### 8. Routing Symfony â
-- **Routes définies** :
- - `/api/login_check` : Login JWT
- - `/api/test` : Test endpoint (TestController)
- - `/api/*` : API Platform auto-routes
-- **Vérification** :
- ```bash
- php bin/console debug:router api_test
- php bin/console router:match /api/test --method=GET
- # â
Route found and matches
- ```
-
-#### 9. .htaccess créé â
-**Fichier** : `public/.htaccess`
-
-**Contenu** : Symfony standard avec mod_rewrite
-
----
-
-### â ïž ProblĂšmes identifiĂ©s
-
-#### 1. Routes inaccessibles via Apache (404)
-
-**SymptĂŽme** :
-```bash
-curl http://localhost:8081/api/test
-# â 404 Not Found
-```
-
-**Tests effectués** :
-- â
Route existe : `php bin/console debug:router api_test`
-- â
Route match : `php bin/console router:match /api/test`
-- â
Symfony fonctionne : `curl http://localhost:8081/api/` â JSON OK
-- â
PHP built-in server OK :
- ```bash
- php -S localhost:9000
- curl http://localhost:9000/api/test
- # â {"status":"ok","message":"Test endpoint works!"}
- ```
-- â Apache 404 : Depuis l'hĂŽte via port 8081
-
-**Diagnostic** :
-- Le problÚme est **Apache-spécifique**
-- Symfony/PHP fonctionnent correctement
-- Le `.htaccess` n'est probablement **PAS lu par Apache**
-- HypothĂšses :
- 1. `AllowOverride All` non pris en compte
- 2. `mod_rewrite` mal configuré
- 3. Ordre des directives Apache incorrect
- 4. ProblĂšme de permissions sur `.htaccess`
-
-**Actions Ă faire** :
-- [ ] Vérifier permissions `.htaccess` dans container
-- [ ] Tester `apache2ctl configtest`
-- [ ] Activer logs de rewrite : `LogLevel alert rewrite:trace3`
-- [ ] Tester FallbackResource dans vhost au lieu de `.htaccess`
-
-#### 2. JWT Login non testé
-
-**Raison** : Bloqué par problÚme #1 (routes inaccessibles)
-
-**Actions Ă faire** :
-- [ ] Résoudre problÚme Apache
-- [ ] Tester `POST /api/login_check` avec credentials
-- [ ] Vérifier génération du token JWT
-- [ ] Tester route protégée avec token
-
----
-
-## đ Configuration Actuelle
-
-### Docker Compose
-
-```yaml
-services:
- web:
- ports:
- - "8081:80" # Symfony API
- - "3001:3000" # (prévu pour Nuxt)
-
- db:
- ports:
- - "5433:5432" # PostgreSQL
-
- pgadmin:
- ports:
- - "5050:80" # pgAdmin
-```
-
-### Variables d'Environnement
-
-**Fichier** : `docker/.env.docker.local`
-
-```env
-# PostgreSQL
-POSTGRES_DB=inventory
-POSTGRES_USER=root
-POSTGRES_PASSWORD=root
-POSTGRES_PORT=5433
-
----
-
-## â
Phase 2 : Migration DB + Frontend (TERMINĂE - 10/01/2026)
-
-### Ce qui a été fait
-
-#### 1. EntitĂ©s Doctrine alignĂ©es Prisma â
-- **Toutes les entités manquantes** créées (Machine, ModelType, Composant, Piece, Product, Links, Requirements, CustomField, Document, etc.)
-- **IDs en string(36)** pour compatibilité CUID/UUID
-- **Colonnes Prisma en camelCase** conservées via `name="..."` (ex: `machineId`, `createdAt`, `supplierPrice`)
-- **Corrections** :
- - `Document.path` passé en `TEXT`
- - `CustomField.options` nullable
- - `TypeMachineComponentRequirement.required` corrigé
-
-#### 2. Migration DB inventory-data â inventory â
-- **Dump data-only + normalisation** (conversion des identifiants quoted vers lowercase)
-- **Mapping table Prisma** : `"ModelType"` â `model_types`
-- **Exclusions** : `profiles`, `_prisma_migrations`
-- **Import validé** : `Counts match for all tables.`
-
-Scripts utiles :
-```bash
-scripts/normalize-dump.py
-scripts/validate-migration.php
-```
-
-#### 3. Frontend basculĂ© sur Inventory_frontend â
-- `make dev-nuxt` pointe vers `Inventory_frontend/`
-- `README.md` mis Ă jour
-- **Base API** ajustée : `http://localhost:8081/api`
-
-Fichiers modifiés :
-```
-makefile
-README.md
-Inventory_frontend/.env
-Inventory_frontend/nuxt.config.ts
-Inventory_frontend/app/services/modelTypes.ts
-```
-
----
-
-# pgAdmin
-PGADMIN_EMAIL=admin@admin.com
-PGADMIN_PASSWORD=admin
-PGADMIN_PORT=5050
-
-# Symfony
-APP_ENV=dev
-APP_SECRET=changeme_super_secret_key_123456789
-JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem
-JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem
-JWT_PASSPHRASE=your_jwt_passphrase_change_me
-
-# NestJS (pour futur parallĂšle)
-NESTJS_PORT=3000
-SESSION_SECRET=changeme_session_secret
-CORS_ORIGIN=http://localhost:3001
-```
-
----
-
-## đ§ Phase 2 : Debugging & Tests (EN COURS)
-
-### Objectifs
-- [x] Résoudre problÚme Apache `.htaccess`
-- [ ] Tester authentification JWT complĂšte
-- [ ] Créer endpoint de test public fonctionnel
-- [ ] Documenter la solution Apache
-
-### Prochaines étapes
-1. **Fix Apache** : Logs de debug + test FallbackResource
-2. **Test JWT** : Login + génération token + route protégée
-3. **Documentation** : Documenter la config Apache qui fonctionne
-
----
-
-## đ MĂ©triques
-
-### Temps passé
-- **Phase 1** : ~3h (exploration + setup + debugging)
-- **ProblĂšme Apache** : ~1h30 (en cours)
-
-### Fichiers créés/modifiés
-
-**Nouveaux fichiers** :
-- `src/Entity/Profile.php`
-- `src/Repository/ProfileRepository.php`
-- `src/Controller/TestController.php`
-- `public/.htaccess`
-- `config/routes/routing.controllers.yaml`
-- `create_test_user.php` (script utilitaire)
-- `migrations/Version20260110175413.php`
-- `docker/pgadmin/servers.json` (config serveur PostgreSQL)
-- `docker/pgadmin/pgpass` (credentials PostgreSQL)
-- `CARNET_DE_BORD.md` (ce fichier)
-
-**Fichiers modifiés** :
-- `docker-compose.yml` (+ pgAdmin)
-- `docker/.env.docker.local` (+ variables Symfony/JWT/pgAdmin)
-- `docker/php/config/vhost.conf` (DocumentRoot â public/)
-- `config/packages/security.yaml` (JWT firewalls)
-- `config/routes.yaml` (+ api_login_check)
-- `composer.json` (+ lexik JWT, vich uploader)
-
----
-
-## đ Leçons Apprises
-
-### 1. Symfony 8 + API Platform
-- **Attributs PHP 8** : `use Symfony\Component\Routing\Attribute\Route;` (pas `Annotation`)
-- **Routes controllers** : Nécessite `config/routes/routing.controllers.yaml` avec `type: attribute`
-- **API Platform** : Auto-génÚre les endpoints CRUD avec `#[ApiResource]`
-
-### 2. JWT Authentication
-- **3 composants** :
- 1. Firewall `login` : `json_login` intercepte `/api/login_check`
- 2. Firewall `api` : `jwt` vérifie le token sur `/api/*`
- 3. Access control : `PUBLIC_ACCESS` vs `IS_AUTHENTICATED_FULLY`
-- **username_path** : Permet de mapper `email` au lieu de `username`
-- **Provider** : Doit ĂȘtre dĂ©fini dans le firewall `login`
-
-### 3. Doctrine Migrations
-- **ID Prisma CUID** : Garder en `string(30)` pour compatibilité
-- **Lifecycle callbacks** : `#[ORM\PrePersist]` pour `createdAt`/`updatedAt`
-- **UserInterface** : Nécessite `getUserIdentifier()`, `getRoles()`, `eraseCredentials()`
-
-### 4. Docker & Apache
-- **`.htaccess` vs VirtualHost** : Le vhost peut override le `.htaccess`
-- **AllowOverride All** : Indispensable pour que `.htaccess` fonctionne
-- **FallbackResource** : Alternative au mod_rewrite dans `.htaccess`
-- **Debugging** : Tester avec PHP built-in server pour isoler le problĂšme
-
----
-
-## đ Ressources Utiles
-
-### AccĂšs aux Services
-
-```
-đ pgAdmin: http://localhost:5050
- ââ Login: admin@admin.com / admin
- ââ Serveur: "Inventory PostgreSQL" (prĂ©-configurĂ©)
- ââ Database: inventory
- ââ Note: Le serveur PostgreSQL est automatiquement connectĂ© au dĂ©marrage
-
-đ API Platform: http://localhost:8081/api/
- ââ Docs: http://localhost:8081/api/docs (Ă venir)
-
-đïž PostgreSQL: localhost:5433
- ââ User: root / root
- ââ Database: inventory
-```
-
-### Commandes fréquentes
-
-```bash
-# Symfony
-make shell # Entrer dans le container
-php bin/console cache:clear # Clear cache
-make cache-clear-full # Clear cache + purge var/cache
-php bin/console debug:router # Lister routes
-php bin/console debug:firewall # Lister firewalls
-php bin/console doctrine:migrations:migrate # Exécuter migrations
-
-# Docker
-make start # Démarrer containers
-make stop # ArrĂȘter containers
-docker logs -f php-inventory-apache # Logs Apache
-docker logs -f pgadmin-inventory # Logs pgAdmin
-docker exec php-inventory-apache bash # Shell root
-
-# Tests API
-curl http://localhost:8081/api/ # Test API Platform
-curl -X POST http://localhost:8081/api/login_check \
- -H "Content-Type: application/json" \
- -d '{"email":"admin@admin.com","password":"admin123"}'
-```
-
-### Documentation
-- [Lexik JWT Bundle](https://github.com/lexik/LexikJWTAuthenticationBundle/blob/2.x/Resources/doc/index.rst)
-- [API Platform Security](https://api-platform.com/docs/core/security/)
-- [Symfony Security](https://symfony.com/doc/current/security.html)
-
----
-
-## đ Historique des Changements
-
-### 2026-01-15 - Session 3
-- â
Filtre API Platform `category` sur `ModelType`
-- â
Normalisation des structures `ModelType` (structure â skeleton)
-- â
Migration `custom_fields.options` en JSON
-- â
Ajout commande `make cache-clear-full`
-- â
Correctifs frontend: headers API Platform, pagination par catégorie, persistance tri
-
-### 2026-01-10 - Session 2 (20h30)
-- â
ProblÚme Apache résolu (routes fonctionnelles)
-- â
Phase 2 complÚte (JWT 100% opérationnel)
-- â
Authentification testée avec succÚs
-- â
Réorganisation projet (frontend/ + _archives/)
-- â
Ătat des lieux dans MIGRATION_PLAN.md
-- â
5 commits conventionnels créés
-- đ Base inventory-data analysĂ©e (673 lignes)
-
-### 2026-01-10 - Session 1 (19h00)
-- â
Création projet migration
-- â
Phase 1 complĂšte (pgAdmin, JWT, Profile, migrations)
-- â ïž ProblĂšme Apache identifiĂ© (routes 404)
-- đ Carnet de bord créé
-
----
-
-**DerniĂšre mise Ă jour** : 2026-01-15 13:45
-**Statut** : Phase 3 EN COURS â ïž - Migrations et intĂ©gration frontend
diff --git a/Inventory_frontend b/Inventory_frontend
index 41f5319..c06c852 160000
--- a/Inventory_frontend
+++ b/Inventory_frontend
@@ -1 +1 @@
-Subproject commit 41f5319b670149a494c6a1102a63c092bc13fb58
+Subproject commit c06c8524939491c21304853a2515a7674525e69f
diff --git a/MIGRATION_PLAN.md b/MIGRATION_PLAN.md
deleted file mode 100644
index b31cddc..0000000
--- a/MIGRATION_PLAN.md
+++ /dev/null
@@ -1,1419 +0,0 @@
-# Plan de Migration : NestJS/Prisma â Symfony/API Platform
-
-**Date de création**: 2026-01-10
-**DerniĂšre mise Ă jour**: 2026-01-11
-**Objectif**: Migrer le backend Inventory (NestJS + Prisma) vers Symfony 8 + API Platform avec JWT auth, tout en conservant les données existantes et en minimisant les disruptions.
-
----
-
-## đ Ătat des Lieux - 2026-01-11
-
-### â
Phase 1 : PrĂ©paration (TERMINĂE, ajustĂ©e)
-- â
Bundles Symfony installés (JWT, VichUploader, Uid)
-- â
Entité Profile créée (UserInterface + session ready)
-- â
pgAdmin configuré et connecté à PostgreSQL
-- â
Docker fonctionnel (web:8081, db:5433, pgadmin:5050)
-- â
API Platform configurée avec préfixe `/api`
-- â ïž JWT dĂ©sactivĂ© temporairement (session cookie pour compatibilitĂ© front)
-
-### â
Phase 2 : Tests & Validation (TERMINĂE, version session)
-- â
API Platform retourne JSON-LD correctement
-- â
Routes session `/api/session/profile` + `/api/session/profiles` opérationnelles
-- â
CORS prévu via Nelmio (mais variable d'env à injecter dans container)
-
-### đŠ DonnĂ©es Ă Migrer (Base: `inventory-data`)
-
-**Total: 673 lignes réparties sur 22 tables (migrées)**
-
-| Table | Lignes | Priorité | Commentaire |
-|-------|--------|----------|-------------|
-| `sites` | 3 | â ïž Critique | Point d'entrĂ©e, relations cascades |
-| `machines` | 3 | â ïž Critique | EntitĂ© centrale |
-| `type_machines` | 3 | â ïž Critique | Configuration machines |
-| `ModelType` | 71 | đŽ Haute | Templates composants/piĂšces/produits |
-| `composants` | 23 | đŽ Haute | - |
-| `pieces` | 82 | đŽ Haute | Plus grande table mĂ©tier |
-| `products` | 2 | đĄ Moyenne | - |
-| `constructeurs` | 20 | đĄ Moyenne | - |
-| `custom_fields` | 95 | đŽ Haute | Configuration champs custom |
-| `custom_field_values` | 203 | đŽ Haute | Plus grande table (valeurs) |
-| `documents` | 14 | đĄ Moyenne | Fichiers uploadĂ©s |
-| `machine_component_links` | 25 | đŽ Haute | Relations machines â composants |
-| `machine_piece_links` | 99 | đŽ Haute | Relations machines â piĂšces |
-| `machine_product_links` | 0 | đą Basse | Table vide |
-| `type_machine_*_requirements` (Ă3) | 22 | đĄ Moyenne | Config requirements types |
-| `_*Constructeurs` (Ă4) | 14 | đĄ Moyenne | Tables de liaison ManyToMany |
-| `profiles` | 2 | đą Basse | Utilisateurs session (migrĂ©s) |
-
-**Complexités identifiées (résolues ou contournées)** :
-- â
IDs CUID Prisma â conservĂ©s en `string(36)` cĂŽtĂ© Doctrine (compatibles)
-- â
Champs JSON complexes importés (components, criticalParts, machinePieces, specifications)
-- â
Relations polymorphiques Document + CustomFieldValue migrées
-- â
Tables de liaison ManyToMany (`_*Constructeurs`) migrées
-- â
Custom fields dynamiques migrés (95 définitions, 203 valeurs)
-
-### đïž Structure Actuelle des Projets
-
-```
-/home/r-dev/Inventory/
-âââ Inventory_backend/ # NestJS + Prisma (11k lignes)
-â âââ src/ # 18 modules NestJS
-â âââ prisma/schema.prisma # SchĂ©ma DB source
-â âââ package.json
-âââ Inventory_frontend/ # Nuxt 3 (front principal)
-â âââ app/
-â â âââ composables/
-â â âââ pages/
-â âââ package.json
-âââ src/ # ⥠Symfony backend
-â âââ Entity/ # Toutes entitĂ©s créées
-â âââ Controller/ # Session + custom fields + documents + skeleton
-â âââ Repository/
-âââ config/ # Configuration Symfony
-âââ migrations/ # Migrations Doctrine
-âââ docker-compose.yml # Web + DB + pgAdmin
-```
-
-### đŻ Prochaines Ătapes (Phase 3 - Mise en production fonctionnelle)
-
-#### Ătape 3.1 : RĂ©organisation du Projet (optionnel)
-- [ ] Déplacer `Inventory_frontend/` vers `frontend/` (si besoin)
-- [ ] Archiver `Inventory_backend/` vers `_archives/backend_nestjs/`
-- [ ] Mettre Ă jour .gitignore
-
-#### Ătape 3.2 : EntitĂ©s & API Platform (TERMINĂE)
-- [x] Site, TypeMachine, Machine
-- [x] ModelType, Composant, Piece, Product
-- [x] Constructeur, Document
-- [x] CustomField, CustomFieldValue
-- [x] MachineComponentLink, MachinePieceLink, MachineProductLink
-- [x] TypeMachine*Requirement (Ă3)
-
-#### Ătape 3.3 : Migration des DonnĂ©es (TERMINĂE)
-- [x] Backup `inventory-data`
-- [x] Schéma Doctrine aligné
-- [x] Import `inventory-data` â `inventory`
-- [x] Validation des comptes (script `scripts/validate-migration.php`)
-
-#### Ătape 3.4 : Front Nuxt (EN COURS)
-- [x] Base URL API -> `http://localhost:8081/api`
-- [x] Parsing JSON-LD dans composables
-- [x] Normalisation des relations vers IRI
-- [x] Endpoints session profile compatibles
-- [ ] Corriger CORS (injecter `CORS_ALLOW_ORIGIN` dans container + clear cache)
-- [ ] Valider le squelette machine (endpoint `/api/machines/{id}/skeleton`)
-- [ ] Valider documents catalogue (composant/piece/produit)
-
-#### Ătape 3.5 : SĂ©curitĂ© (Ă FAIRE)
-- [ ] Réactiver JWT (aprÚs stabilisation)
-- [ ] Ajouter refresh token si nécessaire
-
----
-
-## Table des MatiĂšres
-1. [Vue d'ensemble](#vue-densemble)
-2. [Architecture Cible](#architecture-cible)
-3. [Mapping Prisma â Doctrine](#mapping-prisma--doctrine)
-4. [Stratégie de Migration des Données](#stratégie-de-migration-des-données)
-5. [Configuration Docker](#configuration-docker)
-6. [Plan d'Exécution Phase par Phase](#plan-dexécution-phase-par-phase)
-7. [Gestion des Risques](#gestion-des-risques)
-8. [Checklist de Migration](#checklist-de-migration)
-
----
-
-## 1. Vue d'ensemble
-
-### Situation Actuelle
-- **Backend NestJS**: Port 3000, Prisma ORM, PostgreSQL, Sessions cookie-based
-- **Frontend Nuxt 3**: Port 3001, API calls vers localhost:3000
-- **Base de données**: PostgreSQL 16 sur port 5433, 28 modÚles Prisma, données en production
-- **Docker**: Container unique avec PHP 8.4 + Node 24.12, Apache, XDebug
-
-### Objectif Final
-- **Backend Symfony 8**: Port 8081, Doctrine ORM, API Platform 4.2, JWT auth
-- **Frontend Nuxt 3**: Port 3001, API calls vers localhost:8081
-- **Base de donnĂ©es**: MĂȘme PostgreSQL, migration Prisma â Doctrine
-- **Docker**: 2 backends en parallĂšle pendant la transition (NestJS:3000 + Symfony:8081)
-
-### Pourquoi cette migration ?
-- Code généré par IA (ChatGPT) à nettoyer et reprendre en main
-- Symfony + API Platform = stack PHP standard, mature, bien documentée
-- JWT auth plus adapté pour API REST moderne
-- Meilleure intégration avec l'écosystÚme PHP existant
-
----
-
-## 2. Architecture Cible
-
-### Stack Technique
-
-#### Backend Symfony
-```
-Symfony 8.0
-âââ API Platform 4.2 # REST API auto-documentĂ©e (OpenAPI)
-âââ Doctrine ORM 3.6 # ORM pour PostgreSQL
-âââ Lexik JWT Bundle # JWT authentication
-âââ Nelmio CORS Bundle # CORS pour frontend
-âââ VichUploader Bundle # Upload de documents
-âââ Symfony Validator # Validation des DTOs
-âââ Doctrine Migrations # Gestion des migrations DB
-```
-
-#### Structure de Projet
-```
-src/
-âââ Entity/ # 16 entitĂ©s Doctrine
-â âââ Site.php
-â âââ Machine.php
-â âââ Composant.php
-â âââ Piece.php
-â âââ Product.php
-â âââ TypeMachine.php
-â âââ ModelType.php
-â âââ Constructeur.php
-â âââ Profile.php
-â âââ Document.php
-â âââ CustomField.php
-â âââ CustomFieldValue.php
-â âââ MachineComponentLink.php
-â âââ MachinePieceLink.php
-â âââ MachineProductLink.php
-â âââ TypeMachine*Requirement.php (Ă3)
-âââ Repository/ # Repositories Doctrine
-âââ Controller/ # Controllers custom si besoin
-âââ State/ # State providers/processors API Platform
-âââ Validator/ # Contraintes de validation custom
-âââ Service/ # Services mĂ©tier (Ă©quivalents NestJS services)
-```
-
-#### Configuration JWT
-
-**Flow d'authentification**:
-```
-1. POST /api/login_check
- Body: {"username": "...", "password": "..."}
- Response: {"token": "eyJ0eXAiOiJKV1QiLCJhbGc..."}
-
-2. RequĂȘtes authentifiĂ©es
- Header: Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGc...
-
-3. Token refresh (optionnel)
- POST /api/token/refresh
- Body: {"refresh_token": "..."}
-```
-
-**Configuration sécurité** (`config/packages/security.yaml`):
-```yaml
-security:
- password_hashers:
- App\Entity\Profile:
- algorithm: auto
-
- providers:
- app_user_provider:
- entity:
- class: App\Entity\Profile
- property: email # Ă ajouter Ă Profile
-
- firewalls:
- login:
- pattern: ^/api/login
- stateless: true
- json_login:
- check_path: /api/login_check
- success_handler: lexik_jwt_authentication.handler.authentication_success
- failure_handler: lexik_jwt_authentication.handler.authentication_failure
-
- api:
- pattern: ^/api
- stateless: true
- jwt: ~
-
- access_control:
- - { path: ^/api/login, roles: PUBLIC_ACCESS }
- - { path: ^/api/docs, roles: PUBLIC_ACCESS }
- - { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
-```
-
----
-
-## 3. Mapping Prisma â Doctrine
-
-### 3.1 Types de Données
-
-| Prisma | Doctrine | Notes |
-|--------|----------|-------|
-| `String` | `string` | `@ORM\Column(type="string")` |
-| `String @id @default(cuid())` | `Ulid` | `@ORM\Id` + `@ORM\GeneratedValue(strategy="CUSTOM")` |
-| `Int` | `integer` | `@ORM\Column(type="integer")` |
-| `Decimal @db.Decimal(10,2)` | `string` | `@ORM\Column(type="decimal", precision=10, scale=2)` |
-| `Boolean` | `bool` | `@ORM\Column(type="boolean")` |
-| `DateTime` | `\DateTimeImmutable` | `@ORM\Column(type="datetime_immutable")` |
-| `Json` | `array` | `@ORM\Column(type="json")` |
-| `String[]` | `array` | `@ORM\Column(type="json")` |
-| `String?` | `?string` | `@ORM\Column(nullable=true)` |
-
-### 3.2 Relations
-
-| Prisma | Doctrine | Exemple |
-|--------|----------|---------|
-| `@relation(onDelete: Cascade)` | `@ORM\JoinColumn(onDelete="CASCADE")` | Machine â Site |
-| `@relation(onDelete: SetNull)` | `@ORM\JoinColumn(nullable=true, onDelete="SET NULL")` | Composant â Product |
-| `Model[]` | `@ORM\OneToMany` + `Collection` | Site â machines |
-| `Model` | `@ORM\ManyToOne` | Machine â site |
-| Pas de `@relation` | `@ORM\ManyToMany` | Machine â Constructeurs |
-
-### 3.3 Contraintes et Index
-
-| Prisma | Doctrine |
-|--------|----------|
-| `@unique` | `@ORM\Column(unique=true)` |
-| `@@unique([field1, field2])` | `@ORM\UniqueConstraint(columns=["field1", "field2"])` |
-| `@@map("table_name")` | `@ORM\Table(name="table_name")` |
-| `@db.VarChar(120)` | `@ORM\Column(type="string", length=120)` |
-| `@default("")` | `#[ORM\Column(options: ['default' => ''])]` |
-
-### 3.4 Mapping Détaillé des 16 Entités
-
-#### 1. Site
-```php
-#[ORM\Entity(repositoryClass: SiteRepository::class)]
-#[ORM\Table(name: 'sites')]
-#[ApiResource(
- operations: [
- new Get(),
- new GetCollection(),
- new Post(),
- new Put(),
- new Delete()
- ]
-)]
-class Site
-{
- #[ORM\Id]
- #[ORM\Column(type: 'ulid', unique: true)]
- #[ORM\GeneratedValue(strategy: 'CUSTOM')]
- #[ORM\CustomIdGenerator(class: UlidGenerator::class)]
- private ?Ulid $id = null;
-
- #[ORM\Column(type: 'string', length: 255)]
- #[Assert\NotBlank]
- private string $name;
-
- #[ORM\Column(type: 'string', length: 255, options: ['default' => ''])]
- private string $contactName = '';
-
- #[ORM\Column(type: 'string', length: 20, options: ['default' => ''])]
- private string $contactPhone = '';
-
- #[ORM\Column(type: 'string', length: 500, options: ['default' => ''])]
- private string $contactAddress = '';
-
- #[ORM\Column(type: 'string', length: 10, options: ['default' => ''])]
- private string $contactPostalCode = '';
-
- #[ORM\Column(type: 'string', length: 100, options: ['default' => ''])]
- private string $contactCity = '';
-
- #[ORM\Column(type: 'datetime_immutable')]
- private \DateTimeImmutable $createdAt;
-
- #[ORM\Column(type: 'datetime_immutable')]
- private \DateTimeImmutable $updatedAt;
-
- #[ORM\OneToMany(mappedBy: 'site', targetEntity: Machine::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
- private Collection $machines;
-
- #[ORM\OneToMany(mappedBy: 'site', targetEntity: Document::class, cascade: ['remove'], orphanRemoval: true)]
- private Collection $documents;
-
- #[ORM\PrePersist]
- public function setCreatedAtValue(): void
- {
- $this->createdAt = new \DateTimeImmutable();
- $this->updatedAt = new \DateTimeImmutable();
- }
-
- #[ORM\PreUpdate]
- public function setUpdatedAtValue(): void
- {
- $this->updatedAt = new \DateTimeImmutable();
- }
-}
-```
-
-#### 2. Machine
-```php
-#[ORM\Entity]
-#[ORM\Table(name: 'machines')]
-#[ApiResource]
-class Machine
-{
- #[ORM\Id]
- #[ORM\Column(type: 'ulid')]
- private ?Ulid $id = null;
-
- #[ORM\Column(type: 'string', length: 255, unique: true)]
- private string $name;
-
- #[ORM\Column(type: 'string', length: 255, nullable: true)]
- private ?string $reference = null;
-
- #[ORM\Column(type: 'decimal', precision: 10, scale: 2, nullable: true)]
- private ?string $prix = null;
-
- #[ORM\ManyToOne(targetEntity: Site::class, inversedBy: 'machines')]
- #[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')]
- private Site $site;
-
- #[ORM\ManyToOne(targetEntity: TypeMachine::class, inversedBy: 'machines')]
- #[ORM\JoinColumn(nullable: true)]
- private ?TypeMachine $typeMachine = null;
-
- #[ORM\ManyToMany(targetEntity: Constructeur::class, inversedBy: 'machines')]
- #[ORM\JoinTable(name: 'machine_constructeurs')]
- private Collection $constructeurs;
-
- #[ORM\OneToMany(mappedBy: 'machine', targetEntity: MachineComponentLink::class, cascade: ['persist', 'remove'])]
- private Collection $componentLinks;
-
- #[ORM\OneToMany(mappedBy: 'machine', targetEntity: MachinePieceLink::class, cascade: ['persist', 'remove'])]
- private Collection $pieceLinks;
-
- #[ORM\OneToMany(mappedBy: 'machine', targetEntity: MachineProductLink::class, cascade: ['persist', 'remove'])]
- private Collection $productLinks;
-
- #[ORM\OneToMany(mappedBy: 'machine', targetEntity: Document::class, cascade: ['remove'])]
- private Collection $documents;
-
- #[ORM\OneToMany(mappedBy: 'machine', targetEntity: CustomFieldValue::class, cascade: ['persist', 'remove'])]
- private Collection $customFieldValues;
-
- #[ORM\Column(type: 'datetime_immutable')]
- private \DateTimeImmutable $createdAt;
-
- #[ORM\Column(type: 'datetime_immutable')]
- private \DateTimeImmutable $updatedAt;
-}
-```
-
-#### 3. TypeMachine (avec champs JSON)
-```php
-#[ORM\Entity]
-#[ORM\Table(name: 'type_machines')]
-#[ApiResource]
-class TypeMachine
-{
- #[ORM\Id]
- #[ORM\Column(type: 'ulid')]
- private ?Ulid $id = null;
-
- #[ORM\Column(type: 'string', length: 255, unique: true)]
- private string $name;
-
- #[ORM\Column(type: 'text', nullable: true)]
- private ?string $description = null;
-
- #[ORM\Column(type: 'string', length: 100, nullable: true)]
- private ?string $category = null;
-
- #[ORM\Column(type: 'string', length: 100, nullable: true)]
- private ?string $maintenanceFrequency = null;
-
- // Champs JSON (structure hiérarchique, specs techniques)
- #[ORM\Column(type: 'json', nullable: true)]
- private ?array $components = null;
-
- #[ORM\Column(type: 'json', nullable: true)]
- private ?array $criticalParts = null;
-
- #[ORM\Column(type: 'json', nullable: true)]
- private ?array $machinePieces = null;
-
- #[ORM\Column(type: 'json', nullable: true)]
- private ?array $specifications = null;
-
- #[ORM\OneToMany(mappedBy: 'typeMachine', targetEntity: Machine::class)]
- private Collection $machines;
-
- #[ORM\OneToMany(mappedBy: 'typeMachine', targetEntity: CustomField::class, cascade: ['remove'])]
- private Collection $customFields;
-
- #[ORM\OneToMany(mappedBy: 'typeMachine', targetEntity: TypeMachineComponentRequirement::class, cascade: ['persist', 'remove'])]
- private Collection $componentRequirements;
-
- #[ORM\OneToMany(mappedBy: 'typeMachine', targetEntity: TypeMachinePieceRequirement::class, cascade: ['persist', 'remove'])]
- private Collection $pieceRequirements;
-
- #[ORM\OneToMany(mappedBy: 'typeMachine', targetEntity: TypeMachineProductRequirement::class, cascade: ['persist', 'remove'])]
- private Collection $productRequirements;
-}
-```
-
-#### 4. ModelType (avec Enum)
-```php
-enum ModelCategory: string
-{
- case COMPONENT = 'COMPONENT';
- case PIECE = 'PIECE';
- case PRODUCT = 'PRODUCT';
-}
-
-#[ORM\Entity]
-#[ORM\Table(name: 'model_types')]
-#[ORM\UniqueConstraint(name: 'unique_category_name', columns: ['category', 'name'])]
-#[ApiResource]
-class ModelType
-{
- #[ORM\Id]
- #[ORM\Column(type: 'ulid')]
- private ?Ulid $id = null;
-
- #[ORM\Column(type: 'string', length: 120)]
- private string $name;
-
- #[ORM\Column(type: 'string', length: 60, unique: true)]
- private string $code;
-
- #[ORM\Column(type: 'string', enumType: ModelCategory::class)]
- private ModelCategory $category;
-
- #[ORM\Column(type: 'text', nullable: true)]
- private ?string $notes = null;
-
- #[ORM\Column(type: 'text', nullable: true)]
- private ?string $description = null;
-
- #[ORM\Column(type: 'json', nullable: true)]
- private ?array $componentSkeleton = null;
-
- #[ORM\Column(type: 'json', nullable: true)]
- private ?array $pieceSkeleton = null;
-
- #[ORM\Column(type: 'json', nullable: true)]
- private ?array $productSkeleton = null;
-
- #[ORM\OneToMany(mappedBy: 'typeComposant', targetEntity: Composant::class)]
- private Collection $composants;
-
- #[ORM\OneToMany(mappedBy: 'typePiece', targetEntity: Piece::class)]
- private Collection $pieces;
-
- #[ORM\OneToMany(mappedBy: 'typeProduct', targetEntity: Product::class)]
- private Collection $products;
-
- // Relations avec les requirements (Ă3)
- // Relations avec les custom fields (Ă3)
-}
-```
-
-#### 5. Document (Relations Polymorphiques)
-```php
-#[ORM\Entity]
-#[ORM\Table(name: 'documents')]
-#[ApiResource(
- normalizationContext: ['groups' => ['document:read']],
- denormalizationContext: ['groups' => ['document:write']]
-)]
-class Document
-{
- #[ORM\Id]
- #[ORM\Column(type: 'ulid')]
- private ?Ulid $id = null;
-
- #[ORM\Column(type: 'string', length: 255)]
- private string $name;
-
- #[ORM\Column(type: 'string', length: 255)]
- private string $filename;
-
- #[ORM\Column(type: 'string', length: 500)]
- private string $path;
-
- #[ORM\Column(type: 'string', length: 100)]
- private string $mimeType;
-
- #[ORM\Column(type: 'integer')]
- private int $size;
-
- // Relations polymorphiques (nullable)
- #[ORM\ManyToOne(targetEntity: Machine::class, inversedBy: 'documents')]
- #[ORM\JoinColumn(nullable: true, onDelete: 'CASCADE')]
- private ?Machine $machine = null;
-
- #[ORM\ManyToOne(targetEntity: Composant::class, inversedBy: 'documents')]
- #[ORM\JoinColumn(nullable: true, onDelete: 'CASCADE')]
- private ?Composant $composant = null;
-
- #[ORM\ManyToOne(targetEntity: Piece::class, inversedBy: 'documents')]
- #[ORM\JoinColumn(nullable: true, onDelete: 'CASCADE')]
- private ?Piece $piece = null;
-
- #[ORM\ManyToOne(targetEntity: Product::class, inversedBy: 'documents')]
- #[ORM\JoinColumn(nullable: true, onDelete: 'CASCADE')]
- private ?Product $product = null;
-
- #[ORM\ManyToOne(targetEntity: Site::class, inversedBy: 'documents')]
- #[ORM\JoinColumn(nullable: true, onDelete: 'CASCADE')]
- private ?Site $site = null;
-}
-```
-
-#### 6-16. Autres Entités
-Les autres entitĂ©s suivent le mĂȘme pattern :
-- **Composant, Piece, Product** : Similaires Ă Machine avec relations vers ModelType
-- **Constructeur** : ManyToMany avec Machine/Composant/Piece/Product
-- **Profile** : Entité utilisateur (à enrichir avec email/password pour JWT)
-- **CustomField, CustomFieldValue** : SystĂšme de champs dynamiques
-- **MachineComponentLink, MachinePieceLink, MachineProductLink** : Tables de liaison avec overrides
-- **TypeMachine*Requirement (Ă3)** : DĂ©finition des requirements (minCount, maxCount, label, etc.)
-
----
-
-## 4. Stratégie de Migration des Données
-
-### 4.1 ProblĂšme : CUID vs ULID
-
-**Prisma utilise CUID** (`@default(cuid())`) :
-- Format: `clabcd123xyz...` (25 caractĂšres)
-- Exemple: `cldu8kzj30000vh8q9k0y8h7i`
-
-**Doctrine recommande ULID** :
-- Format: `01ARZ3NDEKTSV4RRFFQ69G5FAV` (26 caractĂšres)
-- Type Symfony : `Symfony\Component\Uid\Ulid`
-
-**Solutions possibles** :
-
-#### Option A : Garder les CUID comme chaĂźnes (RECOMMANDĂE)
-```php
-#[ORM\Id]
-#[ORM\Column(type: 'string', length: 30)]
-private string $id;
-```
-
-**Avantages** :
-- â
Aucune modification des IDs existants
-- â
Migration transparente, pas de mapping d'IDs
-- â
Relations FK préservées
-- â
Frontend continue Ă utiliser les mĂȘmes IDs
-
-**Inconvénients** :
-- â Pas de type Ulid natif Symfony
-- â Moins "moderne"
-
-#### Option B : Migrer CUID â ULID avec mapping
-```php
-#[ORM\Id]
-#[ORM\Column(type: 'ulid')]
-private Ulid $id;
-```
-
-**Nécessite** :
-1. Créer une table de mapping `cuid_to_ulid`
-2. Générer de nouveaux ULIDs pour chaque ligne
-3. Mettre Ă jour toutes les FK
-4. Adapter le frontend
-
-**Risque** : Complexité élevée, risque de perte de données.
-
-### 4.2 Approche RecommandĂ©e : Migration en 3 Ătapes
-
-#### Ătape 1 : CrĂ©er le schĂ©ma Doctrine en parallĂšle (SAFE)
-
-1. Générer les migrations Doctrine sans exécution :
- ```bash
- php bin/console doctrine:migrations:diff
- ```
-
-2. **NE PAS exécuter** la migration immédiatement
-
-3. Analyser le SQL généré pour détecter les différences :
- - Noms de colonnes (camelCase â snake_case)
- - Types de donnĂ©es (Decimal en Prisma â string en Doctrine)
- - Contraintes (vérifier les FK, indexes)
-
-#### Ătape 2 : Script de Validation (Dry-run)
-
-Créer un script PHP qui :
-1. Lit toutes les tables Prisma actuelles
-2. Compare avec le schéma Doctrine généré
-3. GénÚre un rapport de différences
-4. Valide que toutes les donnĂ©es peuvent ĂȘtre mappĂ©es
-
-```php
-// scripts/validate-migration.php
-use Doctrine\ORM\EntityManagerInterface;
-
-class MigrationValidator
-{
- public function validate(): array
- {
- $issues = [];
-
- // Vérifier chaque table
- foreach ($this->getTables() as $table) {
- // Comparer schéma Prisma vs Doctrine
- $schemaIssues = $this->compareSchemas($table);
-
- // VĂ©rifier les donnĂ©es peuvent ĂȘtre migrĂ©es
- $dataIssues = $this->validateData($table);
-
- $issues = array_merge($issues, $schemaIssues, $dataIssues);
- }
-
- return $issues;
- }
-}
-```
-
-#### Ătape 3 : Migration rĂ©elle (avec Backup)
-
-```bash
-# 1. BACKUP de la DB
-pg_dump -U postgres -h localhost -p 5433 inventory_db > backup_$(date +%Y%m%d_%H%M%S).sql
-
-# 2. Exécuter les migrations Doctrine
-php bin/console doctrine:migrations:migrate --no-interaction
-
-# 3. Vérifier l'intégrité
-php bin/console doctrine:schema:validate
-
-# 4. Si erreur : ROLLBACK
-psql -U postgres -h localhost -p 5433 inventory_db < backup_YYYYMMDD_HHMMSS.sql
-```
-
-### 4.3 Mapping des Noms de Colonnes
-
-Prisma â Doctrine naming strategy :
-
-| Prisma | Doctrine | Action |
-|--------|----------|--------|
-| `siteId` | `site_id` | Annotation `#[ORM\JoinColumn(name="siteId")]` ou laisser Doctrine snake_case |
-| `createdAt` | `created_at` | Idem |
-| `contactName` | `contact_name` | Idem |
-
-**Recommandation** : Utiliser `#[ORM\Column(name="xxx")]` pour garder les noms Prisma existants et éviter les ALTER TABLE.
-
-### 4.4 Checklist de Migration DB
-
-- [ ] Créer backup complet de la DB
-- [ ] Générer migration Doctrine (`doctrine:migrations:diff`)
-- [ ] Analyser SQL généré manuellement
-- [ ] Tester migration sur copie locale de la DB
-- [ ] Valider que les données sont intactes aprÚs migration
-- [ ] Créer script de rollback
-- [ ] Documenter les différences schéma Prisma vs Doctrine
-- [ ] Tester toutes les requĂȘtes via Doctrine
-- [ ] Vérifier les performances (indexes, FK)
-
----
-
-## 5. Configuration Docker
-
-### 5.1 Setup : 2 Backends en ParallĂšle
-
-**Objectif** : Faire tourner NestJS (port 3000) ET Symfony (port 8081) simultanément pendant la transition.
-
-#### Modification `docker-compose.yml`
-
-```yaml
-services:
- web:
- container_name: php-${DOCKER_APP_NAME}-apache
- build:
- context: ./docker/php
- dockerfile: Dockerfile
- args:
- DOCKER_PHP_VERSION: ${DOCKER_PHP_VERSION}
- DOCKER_NODE_VERSION: ${DOCKER_NODE_VERSION}
- CURRENT_UID: ${CURRENT_UID}
- CURRENT_GID: ${CURRENT_GID}
- environment:
- PHP_IDE_CONFIG: serverName=${DOCKER_APP_NAME}-docker
- XDEBUG_CLIENT_HOST: ${XDEBUG_CLIENT_HOST:-host.docker.internal}
- XDEBUG_CONFIG: client_host=${XDEBUG_CLIENT_HOST:-host.docker.internal} client_port=9003
- DATABASE_URL: "postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}?serverVersion=16&charset=utf8"
- # Variables pour NestJS
- NESTJS_DATABASE_URL: "postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}?schema=public"
- NESTJS_PORT: 3000
- NESTJS_CORS_ORIGIN: "http://localhost:3001"
- # Variables pour Symfony
- APP_ENV: dev
- APP_SECRET: ${APP_SECRET}
- JWT_SECRET_KEY: ${JWT_SECRET_KEY}
- JWT_PUBLIC_KEY: ${JWT_PUBLIC_KEY}
- JWT_PASSPHRASE: ${JWT_PASSPHRASE}
- volumes:
- - ./:/var/www/html
- - ~/.cache:/var/www/.cache
- - ~/.config:/var/www/.config
- - ~/.composer:/var/www/.composer
- - ./docker/php/config/php.ini:/usr/local/etc/php/php.ini
- - ./docker/php/config/vhost.conf:/etc/apache2/sites-available/000-default.conf
- - ./docker/php/config/docker-php-ext-xdebug.ini:/usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
- - ./LOG:/var/www/html/LOG
- - ./LOG/logs_apache:/var/log/apache2/
- extra_hosts:
- - "host.docker.internal:host-gateway"
- depends_on:
- - db
- ports:
- - "8081:80" # Apache/Symfony
- - "3000:3000" # NestJS backend (nouveau mapping)
- - "3001:3001" # Nuxt frontend
- restart: unless-stopped
-
- db:
- image: postgres:16-alpine
- environment:
- POSTGRES_DB: ${POSTGRES_DB}
- POSTGRES_USER: ${POSTGRES_USER}
- POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
- volumes:
- - pg_data:/var/lib/postgresql/data
- ports:
- - "${POSTGRES_PORT:-5433}:5432"
- restart: unless-stopped
-
-volumes:
- pg_data:
-```
-
-#### Configuration Apache VirtualHost
-
-```apache
-# /docker/php/config/vhost.conf
-
-
- ServerName localhost
- DocumentRoot /var/www/html/public
-
-
- AllowOverride All
- Require all granted
- FallbackResource /index.php
-
-
- # Logs
- ErrorLog /var/log/apache2/error.log
- CustomLog /var/log/apache2/access.log combined
-
-```
-
-#### Script de Démarrage des 2 Backends
-
-Créer `/docker/start-services.sh` :
-
-```bash
-#!/bin/bash
-
-# Démarrer Apache (Symfony)
-apache2-foreground &
-
-# Installer dépendances NestJS si besoin
-cd /var/www/html/Inventory_backend
-if [ ! -d "node_modules" ]; then
- npm install
-fi
-
-# Démarrer NestJS
-npm run start:dev &
-
-# Installer dépendances Nuxt si besoin
-cd /var/www/html/Inventory_frontend
-if [ ! -d "node_modules" ]; then
- npm install
-fi
-
-# Démarrer Nuxt
-npm run dev &
-
-# Garder le container actif
-wait -n
-```
-
-Modifier `Dockerfile` pour utiliser ce script :
-
-```dockerfile
-# Ă la fin du Dockerfile
-COPY start-services.sh /usr/local/bin/
-RUN chmod +x /usr/local/bin/start-services.sh
-
-CMD ["start-services.sh"]
-```
-
-### 5.2 Variables d'Environnement
-
-Créer `/docker/.env.docker.local` (déjà existant, à compléter) :
-
-```env
-# Existant
-DOCKER_APP_NAME=inventory
-DOCKER_PHP_VERSION=8.4.6
-DOCKER_NODE_VERSION=24.12.0
-CURRENT_UID=1000
-CURRENT_GID=1000
-POSTGRES_DB=inventory_db
-POSTGRES_USER=postgres
-POSTGRES_PASSWORD=postgres
-POSTGRES_PORT=5433
-
-# Nouveau : Symfony
-APP_ENV=dev
-APP_SECRET=changeme_super_secret_key_123456789
-JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem
-JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem
-JWT_PASSPHRASE=your_jwt_passphrase
-
-# Nouveau : NestJS
-NESTJS_PORT=3000
-SESSION_SECRET=changeme_session_secret
-CORS_ORIGIN=http://localhost:3001
-```
-
-### 5.3 Génération des Clés JWT
-
-Ajouter au script de démarrage :
-
-```bash
-# Générer clés JWT si inexistantes
-mkdir -p /var/www/html/config/jwt
-if [ ! -f /var/www/html/config/jwt/private.pem ]; then
- openssl genpkey -out /var/www/html/config/jwt/private.pem -aes256 -algorithm rsa -pkeyopt rsa_keybits:4096 -pass pass:${JWT_PASSPHRASE}
- openssl pkey -in /var/www/html/config/jwt/private.pem -passin pass:${JWT_PASSPHRASE} -out /var/www/html/config/jwt/public.pem -pubout
- chmod 600 /var/www/html/config/jwt/*.pem
-fi
-```
-
----
-
-## 6. Plan d'Exécution Phase par Phase
-
-### Phase 1 : Préparation (Semaine 1)
-
-#### TĂąches
-1. **Installer bundles Symfony** :
- ```bash
- composer require lexik/jwt-authentication-bundle
- composer require vich/uploader-bundle
- composer require symfony/uid
- ```
-
-2. **Configurer JWT** :
- - Générer clés RSA
- - Configurer `lexik_jwt_authentication.yaml`
- - Configurer `security.yaml`
-
-3. **Créer entité Profile enrichie** :
- ```php
- class Profile implements UserInterface
- {
- // Ajouter email, password, roles
- }
- ```
-
-4. **Analyser schéma Prisma** :
- - Documenter toutes les relations
- - Identifier les champs JSON complexes
- - Lister les contraintes de validation
-
-5. **Setup Docker** :
- - Modifier `docker-compose.yml`
- - Créer script de démarrage multi-services
- - Tester les 2 backends en parallĂšle
-
-#### Livrables
-- [ ] Symfony configuré avec tous les bundles
-- [ ] JWT fonctionnel avec endpoint `/api/login_check`
-- [ ] Docker lance NestJS (3000) + Symfony (8081) + Nuxt (3001)
-- [ ] Documentation schéma Prisma complÚte
-
----
-
-### Phase 2 : Création des Entités (Semaine 2-3)
-
-#### TĂąches
-1. **Créer toutes les entités Doctrine** (16 entités) :
- - Utiliser les attributs PHP 8 (`#[ORM\Entity]`)
- - Mapper exactement le schéma Prisma
- - Ajouter annotations API Platform
- - Implémenter les lifecycle callbacks (`#[ORM\PrePersist]`, etc.)
-
-2. **Générer repositories** :
- ```bash
- php bin/console make:entity --regenerate
- ```
-
-3. **Créer migration Doctrine** :
- ```bash
- php bin/console doctrine:migrations:diff
- ```
-
-4. **Analyser SQL généré** :
- - Comparer avec schéma Prisma actuel
- - Identifier les différences
- - Préparer script d'ajustement si besoin
-
-5. **Tester sur DB vide** :
- ```bash
- # Créer DB test
- createdb inventory_test
- # Appliquer migrations
- php bin/console doctrine:migrations:migrate --env=test
- # Valider schéma
- php bin/console doctrine:schema:validate
- ```
-
-#### Livrables
-- [ ] 16 entités Doctrine complÚtes avec relations
-- [ ] Migration Doctrine générée et validée
-- [ ] Tests sur DB vide réussis
-- [ ] Documentation des différences Prisma/Doctrine
-
----
-
-### Phase 3 : Migration de la Base de Données (Semaine 4)
-
-#### TĂąches
-1. **Backup complet** :
- ```bash
- pg_dump -U postgres -h localhost -p 5433 inventory_db > backup_avant_migration.sql
- ```
-
-2. **Créer script de validation** :
- ```php
- // scripts/validate-schema.php
- // Comparer schéma Prisma vs Doctrine
- ```
-
-3. **Exécuter migration Doctrine** :
- ```bash
- php bin/console doctrine:migrations:migrate --no-interaction
- ```
-
-4. **Valider données migrées** :
- - Compter les lignes par table (Prisma count vs Doctrine count)
- - Vérifier intégrité référentielle
- - Tester quelques requĂȘtes
-
-5. **Tests de régression** :
- - Lire toutes les entités via Doctrine
- - Vérifier les relations
- - Tester les champs JSON
-
-#### Livrables
-- [ ] Base de données migrée avec succÚs
-- [ ] Backup de sécurité créé
-- [ ] Script de validation OK (0 différences)
-- [ ] Tests de lecture/écriture via Doctrine OK
-
----
-
-### Phase 4 : Logique Métier (Semaine 5-6)
-
-#### TĂąches
-1. **Migrer services NestJS â Symfony** :
-
-**Mapping des services** :
-
-| NestJS Service | Symfony Service | Responsabilités |
-|----------------|-----------------|-----------------|
-| `SitesService` | `SiteManager` | CRUD sites |
-| `MachinesService` | `MachineManager` | CRUD machines, reconfiguration |
-| `ComposantsService` | `ComposantManager` | CRUD composants |
-| `PiecesService` | `PieceManager` | CRUD pieces |
-| `ProductsService` | `ProductManager` | CRUD products |
-| `TypesService` | `TypeMachineManager` | Gestion types machines |
-| `ModelTypeService` | `ModelTypeManager` | Gestion model types |
-| `DocumentsService` | `DocumentManager` | Upload/download documents |
-| `CustomFieldsService` | `CustomFieldManager` | Gestion custom fields |
-| `ConstructeursService` | `ConstructeurManager` | Gestion constructeurs |
-| `ProfilesService` | `ProfileManager` | Gestion profiles |
-| `SessionService` | Supprimé (JWT) | - |
-
-2. **Implémenter upload de documents** :
- - Configurer VichUploaderBundle
- - Créer endpoint `POST /api/documents`
- - Créer endpoint `GET /api/documents/{id}/download`
-
-3. **Implémenter custom fields** :
- - Service de gestion des définitions
- - Service de gestion des valeurs
- - Validation dynamique
-
-4. **Créer State Processors API Platform** :
- - Pour logique custom (ex: reconfiguration machine)
- - Pour validation complexe
-
-5. **Tests unitaires** :
- - Tester chaque service
- - Mocker les repositories
- - Vérifier logique métier
-
-#### Livrables
-- [ ] Tous les services métier migrés
-- [ ] Upload/download documents fonctionnel
-- [ ] Custom fields opérationnels
-- [ ] Tests unitaires couvrant 80%+ du code
-
----
-
-### Phase 5 : API Platform (Semaine 7)
-
-#### TĂąches
-1. **Configurer les endpoints API Platform** :
- - Vérifier routes auto-générées
- - Customiser opérations si besoin
- - Ajouter filtres de recherche
- - Configurer pagination
-
-2. **Sérialisation** :
- - Créer groupes de normalisation
- - Gérer relations circulaires
- - Optimiser avec `@Groups`
-
-3. **Validation** :
- - Ajouter contraintes de validation
- - Créer validators custom
- - Gérer messages d'erreur
-
-4. **Documentation API** :
- - Générer OpenAPI spec
- - Tester via Swagger UI
- - Documenter endpoints custom
-
-5. **Tests d'intégration** :
- - Tester tous les endpoints CRUD
- - Vérifier codes de réponse HTTP
- - Valider structure JSON
-
-#### Livrables
-- [ ] Tous les endpoints API fonctionnels
-- [ ] Documentation OpenAPI complĂšte
-- [ ] Tests d'intégration OK
-- [ ] Swagger UI accessible sur `/api/docs`
-
----
-
-### Phase 6 : Adaptation Frontend (Semaine 8)
-
-#### TĂąches
-1. **Modifier `useApi.js`** :
- ```js
- // Ancien
- const baseURL = 'http://localhost:3000';
-
- // Nouveau
- const baseURL = import.meta.env.VITE_API_URL || 'http://localhost:8081/api';
- ```
-
-2. **Implémenter gestion JWT** :
- ```js
- // composables/useAuth.js
- export const useAuth = () => {
- const token = ref(localStorage.getItem('jwt_token'));
-
- const login = async (username, password) => {
- const response = await fetch('/api/login_check', {
- method: 'POST',
- body: JSON.stringify({ username, password })
- });
- const { token } = await response.json();
- localStorage.setItem('jwt_token', token);
- token.value = token;
- };
-
- const logout = () => {
- localStorage.removeItem('jwt_token');
- token.value = null;
- };
-
- return { token, login, logout };
- };
- ```
-
-3. **Adapter les composables** :
- - `useSites.js` â vĂ©rifier format rĂ©ponse
- - `useMachines.js` â idem
- - Tous les autres composables
-
-4. **Gestion des changements d'API** :
- - Ajuster parsing des réponses JSON:API vs JSON simple
- - Gérer les nouveaux formats d'erreur
- - Adapter les IDs si changement
-
-5. **Tests fonctionnels** :
- - Tester chaque page du frontend
- - Vérifier tous les flows utilisateur
- - Tester upload/download documents
-
-#### Livrables
-- [ ] Frontend connecté au backend Symfony
-- [ ] Authentification JWT fonctionnelle
-- [ ] Tous les composables adaptés
-- [ ] Tests fonctionnels réussis
-
----
-
-### Phase 7 : Tests & Décommissionnement NestJS (Semaine 9)
-
-#### TĂąches
-1. **Tests de charge** :
- - Comparer performances NestJS vs Symfony
- - Vérifier temps de réponse
- - Tester avec données réelles
-
-2. **Tests de régression complÚte** :
- - Rejouer tous les scénarios utilisateur
- - Comparer résultats NestJS vs Symfony
- - Fixer les bugs
-
-3. **Documentation** :
- - Documenter nouvelle architecture
- - Créer guide migration pour équipe
- - Documenter différences API
-
-4. **Décommissionner NestJS** :
- - ArrĂȘter le service NestJS
- - Supprimer du docker-compose
- - Archiver le code (ne pas supprimer)
-
-5. **Cleanup** :
- - Supprimer dépendances NestJS inutilisées
- - Nettoyer fichiers de config
- - Optimiser Docker
-
-#### Livrables
-- [ ] Tests de charge validés
-- [ ] 0 bugs de régression
-- [ ] Documentation complĂšte
-- [ ] NestJS arrĂȘtĂ©, Symfony en production
-- [ ] Code NestJS archivé
-
----
-
-## 7. Gestion des Risques
-
-### 7.1 Risques Techniques
-
-| Risque | Probabilité | Impact | Mitigation |
-|--------|-------------|--------|------------|
-| **Perte de données pendant migration DB** | Faible | Critique | Backup complet avant migration, script de rollback, validation post-migration |
-| **IncompatibilitĂ© schĂ©ma Prisma/Doctrine** | Moyenne | ĂlevĂ© | Validation schema dry-run, tests sur DB copie, garder noms de colonnes Prisma |
-| **Performances Symfony < NestJS** | Faible | Moyen | Benchmarks, optimisation requĂȘtes Doctrine, cache API Platform |
-| **Bugs lors migration logique mĂ©tier** | Moyenne | ĂlevĂ© | Tests unitaires + intĂ©gration, comparison NestJS/Symfony side-by-side |
-| **JWT incompatible avec frontend** | Faible | Moyen | Tests d'auth dĂšs Phase 1, documentation claire |
-| **Upload documents ne fonctionne pas** | Faible | Moyen | Tester VichUploader tĂŽt, fallback manuel si besoin |
-| **Relations Doctrine mal configurĂ©es** | Moyenne | ĂlevĂ© | Tests avec donnĂ©es rĂ©elles, validation via `doctrine:schema:validate` |
-
-### 7.2 Risques Projet
-
-| Risque | Probabilité | Impact | Mitigation |
-|--------|-------------|--------|------------|
-| **Timeline trop optimiste** | ĂlevĂ©e | Moyen | Buffer de 2 semaines, dĂ©coupage en phases indĂ©pendantes |
-| **Manque de tests** | Moyenne | ĂlevĂ© | Ăcrire tests dĂšs Phase 2, coverage > 80% |
-| **Documentation insuffisante** | Moyenne | Moyen | Documenter au fur et Ă mesure, pas Ă la fin |
-| **Réversion nécessaire** | Faible | Critique | Garder NestJS actif en parallÚle, backup DB, script de rollback |
-
-### 7.3 Plan de Rollback
-
-Si problĂšme critique :
-
-1. **ArrĂȘter Symfony** :
- ```bash
- docker-compose stop web
- ```
-
-2. **Restaurer DB depuis backup** :
- ```bash
- psql -U postgres -h localhost -p 5433 inventory_db < backup_avant_migration.sql
- ```
-
-3. **Redémarrer NestJS** :
- ```bash
- cd Inventory_backend && npm run start:dev
- ```
-
-4. **Reconfigurer frontend** :
- ```js
- // Revert API URL
- const baseURL = 'http://localhost:3000';
- ```
-
-5. **Analyser l'erreur** :
- - Lire logs Symfony (`var/log/dev.log`)
- - Lire logs PostgreSQL
- - Identifier root cause
-
-6. **Fix et retry** :
- - Corriger le problĂšme
- - Retester en local
- - Relancer migration
-
----
-
-## 8. Checklist de Migration
-
-### Avant de Commencer
-- [ ] Lire entiĂšrement ce document
-- [ ] Comprendre schéma Prisma actuel
-- [ ] Valider que Docker fonctionne
-- [ ] Créer backup DB initial
-- [ ] Setup environnement de dev local
-
-### Phase 1 : Préparation
-- [ ] Installer bundles Symfony (JWT, VichUploader, Uid)
-- [ ] Configurer JWT avec clés RSA
-- [ ] Enrichir entité Profile (email, password, roles)
-- [ ] Modifier docker-compose.yml pour 2 backends
-- [ ] Tester démarrage des 2 backends en parallÚle
-- [ ] Documenter schéma Prisma
-
-### Phase 2 : Entités
-- [ ] Créer 16 entités Doctrine
-- [ ] Ajouter annotations API Platform
-- [ ] Générer migration Doctrine
-- [ ] Analyser SQL généré
-- [ ] Tester migration sur DB vide
-- [ ] Valider schéma Doctrine
-
-### Phase 3 : Migration DB
-- [ ] Créer backup DB production
-- [ ] Créer script de validation
-- [ ] Exécuter migration Doctrine
-- [ ] Appliquer migrations post-migration (ajouts colonnes, types JSON)
-- [ ] Valider count de lignes par table
-- [ ] Tester requĂȘtes Doctrine
-- [ ] Vérifier intégrité référentielle
-
-### Phase 4 : Services
-- [ ] Migrer les 11 services métier
-- [ ] Implémenter upload documents
-- [ ] Implémenter custom fields
-- [ ] Ăcrire tests unitaires
-- [ ] Valider logique métier
-
-### Phase 5 : API
-- [ ] Configurer endpoints API Platform
-- [ ] Implémenter sérialisation
-- [ ] Ajouter validation
-- [ ] Générer doc OpenAPI
-- [ ] Tests d'intégration
-
-### Phase 6 : Frontend
-- [ ] Modifier useApi.js (nouveau baseURL)
-- [ ] Implémenter gestion JWT
-- [ ] Adapter tous les composables
-- [ ] Tester toutes les pages
-- [ ] Valider flows utilisateur
-
-### Phase 7 : Finalisation
-- [ ] Tests de charge
-- [ ] Tests de régression complÚte
-- [ ] Documenter nouvelle architecture
-- [ ] ArrĂȘter NestJS
-- [ ] Archiver code NestJS
-- [ ] Cleanup Docker
-
----
-
-## Annexes
-
-### A. Commandes Utiles
-
-```bash
-# Symfony
-php bin/console doctrine:schema:validate
-php bin/console doctrine:migrations:diff
-php bin/console doctrine:migrations:migrate
-php bin/console doctrine:query:sql "SELECT COUNT(*) FROM sites"
-php bin/console debug:router
-php bin/console cache:clear
-
-# Docker
-docker-compose up -d --build
-docker-compose logs -f web
-docker-compose exec web bash
-docker-compose down
-
-# Base de données
-pg_dump -U postgres -h localhost -p 5433 inventory_db > backup.sql
-psql -U postgres -h localhost -p 5433 inventory_db < backup.sql
-psql -U postgres -h localhost -p 5433 inventory_db -c "SELECT COUNT(*) FROM sites"
-
-# Tests
-php bin/phpunit
-php vendor/bin/phpstan analyse src
-```
-
-### B. Structure Finale du Projet
-
-```
-/home/r-dev/Inventory/
-âââ Inventory_backend/ # Ă archiver aprĂšs migration
-âââ Inventory_frontend/ # Frontend Nuxt (Ă garder)
-â âââ app/
-â â âââ composables/
-â â â âââ useApi.js # Modifier baseURL
-â â â âââ useAuth.js # Nouveau : JWT
-â â â âââ ...
-â â âââ pages/
-âââ src/ # Backend Symfony (nouveau)
-â âââ Entity/
-â â âââ Site.php
-â â âââ Machine.php
-â â âââ ... (16 entitĂ©s)
-â âââ Repository/
-â âââ Service/
-â â âââ SiteManager.php
-â â âââ MachineManager.php
-â â âââ ... (11 services)
-â âââ Controller/ # Si besoin de custom controllers
-â âââ State/ # State providers API Platform
-âââ config/
-â âââ packages/
-â â âââ doctrine.yaml
-â â âââ api_platform.yaml
-â â âââ lexik_jwt_authentication.yaml
-â â âââ security.yaml
-â â âââ nelmio_cors.yaml
-â âââ jwt/
-â âââ private.pem
-â âââ public.pem
-âââ migrations/ # Migrations Doctrine
-âââ docker/
-â âââ php/
-â â âââ Dockerfile
-â â âââ config/
-â âââ start-services.sh # Nouveau : lance Symfony + Nuxt
-â âââ .env.docker
-â âââ .env.docker.local
-âââ docker-compose.yml # ModifiĂ© : 2 backends
-âââ composer.json # Symfony dependencies
-âââ MIGRATION_PLAN.md # Ce document
-```
-
-### C. Ressources
-
-- [API Platform Docs](https://api-platform.com/docs/)
-- [Doctrine ORM Docs](https://www.doctrine-project.org/projects/doctrine-orm/en/latest/)
-- [Lexik JWT Bundle](https://github.com/lexik/LexikJWTAuthenticationBundle/blob/2.x/Resources/doc/index.rst)
-- [Symfony Best Practices](https://symfony.com/doc/current/best_practices.html)
-- [Prisma to Doctrine Migration Guide](https://symfony.com/doc/current/doctrine.html#creating-an-entity-class)
-
----
-
-## Conclusion
-
-Ce plan de migration détaille la stratégie pour passer de NestJS/Prisma à Symfony/API Platform de maniÚre progressive et sécurisée. Les points clés :
-
-1. **Migration incrémentale** : Garder NestJS actif en parallÚle
-2. **Sécurité des données** : Backups, validation, rollback
-3. **Modernisation** : JWT auth, API Platform, documentation OpenAPI
-4. **Minimiser les risques** : Tests Ă chaque phase, validation continue
-
-**Durée estimée** : 9 semaines (avec buffer)
-**Effort requis** : 1 développeur full-time
-
-**PrĂȘt Ă dĂ©marrer ?** Validation de ce plan avant de commencer la Phase 1.
diff --git a/REFACTORING_PLAN.md b/REFACTORING_PLAN.md
deleted file mode 100644
index 5004116..0000000
--- a/REFACTORING_PLAN.md
+++ /dev/null
@@ -1,786 +0,0 @@
-# Plan de Refactoring - Inventory v1.2.0
-
-> **Date de creation :** 2026-02-03
-> **Branche de travail :** `refacto/v1.3.0`
-> **Base :** `develop` (commit `8d83076`)
-
----
-
-## Legende des statuts
-
-| Statut | Signification |
-| ------ | ---------------------- |
-| `[ ]` | A faire |
-| `[~]` | En cours |
-| `[x]` | Termine |
-| `[!]` | Bloque / besoin d'info |
-
----
-
-## Phase 1 - Securite (CRITIQUE)
-
-> **Priorite :** MAXIMALE - A traiter en premier
-
-### 1.1 Corriger la configuration de securite
-
-- **Statut :** `[ ]`
-- **Fichier :** `config/packages/security.yaml`
-- **Probleme :** `PUBLIC_ACCESS` applique a toutes les routes `/api` avant la regle `IS_AUTHENTICATED_FULLY`. Le pattern matching "first match wins" rend potentiellement tout public.
-- **Action :** Reordonner les regles `access_control` pour que les routes protegees soient listees AVANT les routes publiques.
-- **Agent :** -
-- **Notes :** -
-
-### 1.2 Ajouter les controles d'autorisation sur les controllers
-
-- **Statut :** `[ ]`
-- **Fichiers :**
- - `src/Controller/MachineSkeletonController.php`
- - `src/Controller/CustomFieldValueController.php`
- - `src/Controller/DocumentQueryController.php`
- - `src/Controller/SessionProfileController.php`
- - `src/Controller/SessionProfilesController.php`
- - Tous les `*HistoryController.php`
-- **Probleme :** Aucun attribut `#[IsGranted]` sur les controllers custom. Pas de RBAC.
-- **Action :** Ajouter `#[IsGranted('IS_AUTHENTICATED_FULLY')]` sur chaque controller (ou route). Definir des roles si necessaire.
-- **Agent :** -
-- **Notes :** -
-
-### 1.3 Securiser les secrets
-
-- **Statut :** `[ ]`
-- **Fichiers :**
- - `.env` (JWT_PASSPHRASE en dur, APP_SECRET vide)
- - `docker/.env.docker` (credentials `root:root`)
-- **Action :**
- 1. Deplacer `JWT_PASSPHRASE` dans `.env.local` (git-ignore)
- 2. Generer un `APP_SECRET` valide
- 3. Ajouter `.env.local` dans `.gitignore` si pas deja fait
- 4. Documenter la configuration des secrets pour les devs
-- **Agent :** -
-- **Notes :** -
-
----
-
-## Phase 2 - Elimination de la duplication de code
-
-> **Priorite :** HAUTE - Impact direct sur la maintenabilite
-
-### 2.1 Refactorer les 3 Audit Subscribers en un seul generique
-
-- **Statut :** `[ ]`
-- **Fichiers concernes :**
- - `src/EventSubscriber/ProductAuditSubscriber.php` (298 LOC)
- - `src/EventSubscriber/PieceAuditSubscriber.php` (300 LOC)
- - `src/EventSubscriber/ComposantAuditSubscriber.php` (300 LOC)
-- **Probleme :** ~900 LOC dupliquees a ~95%. Les methodes `onFlush()`, `buildDiffFromChangeSet()`, `resolveActorProfileId()`, `mergeDiffs()`, `normalizeCollection()` sont identiques. Seules les methodes `snapshot*()` different legerement.
-- **Action :**
- 1. Creer un `AbstractAuditSubscriber` ou un `GenericAuditSubscriber` parametrable
- 2. Extraire la logique commune (onFlush, buildDiff, resolveActor, mergeDiffs, normalizeCollection)
- 3. Utiliser un systeme de configuration par entite (map `entityClass => entityType + snapshotMethod`)
- 4. Supprimer les 3 fichiers redondants
- 5. Verifier que l'audit fonctionne toujours sur Product, Piece et Composant
-- **Agent :** -
-- **Notes :** Tester manuellement les logs d'audit apres refacto.
-
-### 2.2 Extraire un CuidGenerator utilitaire
-
-- **Statut :** `[ ]`
-- **Fichiers concernes :** 18 entites contenant `generateCuid()` en prive
-- **Probleme :** Methode `generateCuid()` dupliquee dans chaque entite. De plus, `AuditLog.php` utilise une variante differente (base_convert).
-- **Action :**
- 1. Creer `src/Util/CuidGenerator.php` avec une methode statique `generate(): string`
- 2. Uniformiser l'implementation (choisir une seule methode)
- 3. Remplacer tous les appels dans les 18 entites
- 4. Supprimer les methodes privees devenues inutiles
-- **Agent :** -
-- **Notes :** Attention a l'inconsistance entre AuditLog et les autres entites.
-
-### 2.3 Factoriser la logique de liaison dans MachineSkeletonController
-
-- **Statut :** `[ ]`
-- **Fichier :** `src/Controller/MachineSkeletonController.php` (756 LOC)
-- **Probleme :** Les methodes `applyComponentLinks()`, `applyPieceLinks()`, `applyProductLinks()` sont quasi identiques (~90 LOC chacune).
-- **Action :**
- 1. Extraire une methode generique `applyLinks(Machine $machine, array $links, string $type)`
- 2. Parametrer par le type d'entite liee (Composant, Piece, Product)
- 3. Reduire le controller a ~400 LOC max
-- **Agent :** -
-- **Notes :** -
-
----
-
-## Phase 3 - Restructuration des controllers
-
-> **Priorite :** MOYENNE - Amelioration de la lisibilite et maintenabilite
-
-### 3.1 Decouper MachineSkeletonController
-
-- **Statut :** `[ ]`
-- **Fichier :** `src/Controller/MachineSkeletonController.php` (756 LOC)
-- **Action :**
- 1. Extraire la logique metier dans un `MachineSkeletonService`
- 2. Le controller ne doit gerer que la requete/reponse HTTP
- 3. Le service gere la logique de skeleton (get, update, applyLinks)
- 4. Extraire les helpers (`resolveIdentifier`, `indexLinksById`, `applyOverrides`, `normalizeMachineSkeletonResponse`) dans le service
-- **Agent :** -
-- **Notes :** Depend de la phase 2.3 (factorisation des liens).
-
-### 3.2 Ajouter un try-catch et du logging dans les controllers
-
-- **Statut :** `[ ]`
-- **Fichiers :** Tous les controllers dans `src/Controller/`
-- **Probleme :** Aucun try-catch autour des `flush()` et `persist()`. Pas de logging d'erreurs.
-- **Action :**
- 1. Ajouter `try-catch` autour des operations Doctrine dans chaque controller
- 2. Logger les erreurs avec le `LoggerInterface` de Symfony (Monolog)
- 3. Retourner des reponses JSON coherentes en cas d'erreur serveur (500)
-- **Agent :** -
-- **Notes :** -
-
-### 3.3 Renforcer la validation des entrees
-
-- **Statut :** `[ ]`
-- **Fichiers :**
- - `src/Controller/CustomFieldValueController.php`
- - `src/Controller/MachineSkeletonController.php`
-- **Probleme :** Pas de validation de longueur max, pas de regex sur les IDs, pas de controle de profondeur JSON.
-- **Action :**
- 1. Valider le format des IDs (regex CUID : `/^cl[a-f0-9]{24}$/`)
- 2. Ajouter des limites de longueur sur les champs string
- 3. Utiliser le composant Validator de Symfony pour les DTOs si pertinent
-- **Agent :** -
-- **Notes :** -
-
----
-
-## Phase 4 - Amelioration du stockage
-
-> **Priorite :** MOYENNE - Performance et scalabilite
-
-### 4.1 Migrer le stockage PDF de base64 vers le filesystem
-
-- **Statut :** `[ ]`
-- **Fichiers :**
- - `src/Entity/Document.php`
- - `src/Command/CompressPdfCommand.php`
- - `src/Service/PdfCompressorService.php`
-- **Probleme :** Les PDFs sont stockes en base64 dans la colonne `path` (TEXT) de la BDD. Risque de DoS et mauvaise perf sur des gros fichiers.
-- **Action :**
- 1. Utiliser `vich/uploader-bundle` (deja installe) pour le stockage fichier
- 2. Configurer un repertoire de stockage (`var/uploads/documents/`)
- 3. Migrer les documents existants (script de migration)
- 4. Adapter `PdfCompressorService` pour lire/ecrire sur le filesystem
- 5. Mettre a jour l'entite Document
-- **Agent :** -
-- **Notes :** Prevoir une migration de donnees pour les documents existants.
-
-### 4.2 Corriger les types de prix (string -> decimal)
-
-- **Statut :** `[ ]`
-- **Fichiers :**
- - `src/Entity/Machine.php` (`$prix`)
- - `src/Entity/Product.php` (`$supplierPrice`)
-- **Probleme :** Les prix sont types `?string` en PHP alors que la colonne est `DECIMAL(10,2)` en BDD.
-- **Action :**
- 1. Changer le type PHP en `?float` ou utiliser `brick/money`
- 2. Adapter les getters/setters
- 3. Verifier la serialisation API Platform
-- **Agent :** -
-- **Notes :** Impact potentiel sur le frontend (format des nombres).
-
----
-
-## Phase 5 - Utilisation du Process Component
-
-> **Priorite :** BASSE - Bonne pratique
-
-### 5.1 Remplacer exec() par Symfony Process
-
-- **Statut :** `[ ]`
-- **Fichiers :**
- - `src/Command/CompressPdfCommand.php` (lignes 42, 98-101)
- - `src/Service/PdfCompressorService.php` (lignes 37-41)
-- **Probleme :** Utilisation de `exec()` directe pour appeler `qpdf`.
-- **Action :**
- 1. Remplacer par `Symfony\Component\Process\Process`
- 2. Gerer le timeout et les erreurs proprement
- 3. Tester que la compression fonctionne toujours
-- **Agent :** -
-- **Notes :** `escapeshellarg()` est deja utilise, donc pas de faille de securite immediate.
-
----
-
-## Phase 6 - Tests
-
-> **Priorite :** HAUTE - Indispensable avant toute refacto majeure
-
-### 6.1 Mettre en place les tests unitaires
-
-- **Statut :** `[ ]`
-- **Fichiers a creer :**
- - `tests/Unit/Util/CuidGeneratorTest.php`
- - `tests/Unit/Entity/MachineTest.php`
- - `tests/Unit/Entity/ProductTest.php`
- - `tests/Unit/Service/PdfCompressorServiceTest.php`
-- **Action :**
- 1. Tester le CuidGenerator (format, unicite)
- 2. Tester les entites (validation, lifecycle callbacks)
- 3. Tester le PdfCompressorService
-- **Agent :** -
-- **Notes :** -
-
-### 6.2 Mettre en place les tests fonctionnels (API)
-
-- **Statut :** `[ ]`
-- **Fichiers a creer :**
- - `tests/Functional/Api/MachineTest.php`
- - `tests/Functional/Api/ProductTest.php`
- - `tests/Functional/Api/AuthenticationTest.php`
- - `tests/Functional/Api/MachineSkeletonTest.php`
-- **Action :**
- 1. Configurer une base de test (SQLite ou PostgreSQL de test)
- 2. Creer des fixtures de test
- 3. Tester les endpoints CRUD
- 4. Tester l'authentification JWT
- 5. Tester les endpoints custom (skeleton, custom fields)
-- **Agent :** -
-- **Notes :** Utiliser `ApiTestCase` de API Platform.
-
-### 6.3 Tests des Audit Subscribers
-
-- **Statut :** `[ ]`
-- **Fichiers a creer :**
- - `tests/Unit/EventSubscriber/AuditSubscriberTest.php`
-- **Action :**
- 1. Tester la creation de logs sur insert/update/delete
- 2. Tester le format des diffs et snapshots
- 3. Tester la resolution de l'acteur
-- **Agent :** -
-- **Notes :** A faire APRES la phase 2.1 (refacto des subscribers).
-
----
-
-## Phase 7 - Nett oyage et conventions
-
-> **Priorite :** BASSE - Polish final
-
-### 7.1 Supprimer les fichiers inutiles
-
-- **Statut :** `[ ]`
-- **Fichiers a verifier :**
- - `frontend/` (dossier legacy ? vs `Inventory_frontend/`)
- - `src/ApiResource/` (repertoire vide)
- - Fichiers SQL a la racine (`backup_v1.0.0.sql`, `data_norm.sql`, `fullasse.sql`, `fulldata.sql`)
-- **Action :** Confirmer avec l'equipe quels fichiers sont obsoletes et les supprimer.
-- **Agent :** -
-- **Notes :** Ne pas supprimer sans validation.
-
-### 7.2 Uniformiser la gestion des null
-
-- **Statut :** `[ ]`
-- **Fichiers :** Toutes les entites dans `src/Entity/`
-- **Action :** S'assurer que les types nullable sont coherents entre PHP et la BDD (colonnes NOT NULL vs nullable).
-- **Agent :** -
-- **Notes :** -
-
----
-
----
-
-# FRONTEND (`Inventory_frontend/`)
-
----
-
-## Phase F1 - Decoupage des mega-composants (CRITIQUE)
-
-> **Priorite :** MAXIMALE - Les fichiers actuels sont inmaintenables
-
-### F1.1 Decouper `machine/[id].vue` (2989 LOC â 219 LOC)
-
-- **Statut :** `[x]`
-- **Fichier :** `Inventory_frontend/app/pages/machine/[id].vue`
-- **Resultat :** Page decomposee en 2 composables + 7 composants. Orchestrateur = 219 LOC.
-- **Fichiers crees :**
- - `composables/useMachineDetailData.ts` (1404 LOC) â state + logique metier
- - `composables/useMachineSkeletonEditor.ts` (843 LOC) â logique skeleton
- - `components/machine/MachineDetailHeader.vue` (76 LOC)
- - `components/machine/MachineInfoCard.vue` (185 LOC)
- - `components/machine/MachineDocumentsCard.vue` (116 LOC)
- - `components/machine/MachineProductsCard.vue` (62 LOC)
- - `components/machine/MachineComponentsCard.vue` (53 LOC)
- - `components/machine/MachinePiecesCard.vue` (34 LOC)
- - `components/machine/MachineSkeletonSummary.vue` (199 LOC)
-- **Pattern :** Props + Events (pas de provide/inject). Composables avec injection de dependances (interface Deps).
-- **Notes :** Typecheck 0 erreurs. Lint OK.
-
-### F1.2 Decouper `machines/new.vue` (1231 LOC â 196 LOC)
-
-- **Statut :** `[x]`
-- **Fichier :** `Inventory_frontend/app/pages/machines/new.vue`
-- **Resultat :** Page decomposee en 1 composable + 5 composants. Orchestrateur = 196 LOC.
-- **Fichiers crees :**
- - `composables/useMachineCreatePage.ts` (460 LOC) â state, entity lookups, options, creation
- - `components/machine/create/RequirementComponentSelector.vue` (126 LOC)
- - `components/machine/create/RequirementPieceSelector.vue` (130 LOC)
- - `components/machine/create/RequirementProductSelector.vue` (142 LOC)
- - `components/machine/create/MachineCreatePreview.vue` (205 LOC)
- - `components/machine/create/PreviewRequirementGroup.vue` (59 LOC)
-- **Pattern :** Props + Events. Composable consolide entity lookups, options, label helpers, creation.
-- **Notes :** Typecheck 0 erreurs. Lint OK. Corrige aussi un bug F1.1 (defineProps dans mauvais script block de MachineSkeletonSummary.vue).
-
-### F1.3 Decouper les pages de creation/edition (Piece, Component, Product)
-
-- **Statut :** `[x]`
-- **Fichiers :**
- - `pages/component/create.vue` (1282 LOC)
- - `pages/component/[id]/edit.vue` (1629 LOC)
- - `pages/pieces/create.vue` (817 LOC)
- - `pages/pieces/[id]/edit.vue` (1327 LOC)
- - `pages/product/[id]/edit.vue` (936 LOC)
-- **Probleme :** Formulaires monolithiques avec sections multiples (infos generales, fournisseurs, documents, custom fields, etc.).
-- **Action :**
- 1. Identifier les sections communes entre create/edit (factoriser)
- 2. Extraire chaque section en composant reutilisable :
- - `EntityFormGeneral.vue` (nom, reference, description)
- - `EntityFormSuppliers.vue` (constructeurs)
- - `EntityFormDocuments.vue` (documents)
- - `EntityFormCustomFields.vue` (champs personnalises)
- 3. Objectif par page : <400 LOC
-- **Agent :** -
-- **Notes :** Les formulaires create et edit partagent beaucoup de code. Factoriser.
-- **Sous-taches :**
- - [x] F1.3a Extraire `customFieldFormUtils.ts` (duplique dans 5 fichiers)
- - [x] F1.3b Extraire `documentDisplayUtils.ts` (duplique dans 3 pages edit)
- - [x] F1.3c Extraire `historyDisplayUtils.ts` (duplique dans 3 pages edit)
- - [x] F1.3d Rewire les 5 pages create/edit sur les modules extraits
- - [x] F1.3e Typecheck + commit F1.3 (erreurs F1.3 corrigees, 120 erreurs preexistantes documentees)
-
-### F1.4 Reduire PieceItem.vue (1588 LOC) et ComponentItem.vue (1336 LOC)
-
-- **Statut :** `[x]`
-- **Fichiers :**
- - `Inventory_frontend/app/components/PieceItem.vue` (1588 â 740 LOC)
- - `Inventory_frontend/app/components/ComponentItem.vue` (1336 â 585 LOC)
-- **Probleme :** ~700 LOC de logique dupliquee entre les deux composants (champs personnalises, documents, affichage produit).
-- **Action realisee :**
- 1. Extraction de la logique pure custom fields dans `shared/utils/entityCustomFieldLogic.ts` (~350 LOC)
- 2. Creation de `composables/useEntityCustomFields.ts` (composable reactif, ~180 LOC)
- 3. Creation de `composables/useEntityDocuments.ts` (CRUD documents + preview, ~120 LOC)
- 4. Creation de `composables/useEntityProductDisplay.ts` (affichage produit, ~100 LOC)
- 5. Import des helpers document depuis `shared/utils/documentDisplayUtils.ts` (existant)
- 6. Rewrite des deux composants pour utiliser les modules partages
- 7. Typecheck 0 erreurs, lint 0 erreurs
-- **Sous-taches :**
- - [x] F1.4a Extraire `entityCustomFieldLogic.ts` (fonctions pures)
- - [x] F1.4b Creer `useEntityCustomFields.ts` (composable reactif)
- - [x] F1.4c Creer `useEntityDocuments.ts` (composable documents)
- - [x] F1.4d Creer `useEntityProductDisplay.ts` (composable produit)
- - [x] F1.4e Rewrite ComponentItem.vue (1336 â 585 LOC, script 900 â 150 LOC)
- - [x] F1.4f Rewrite PieceItem.vue (1588 â 740 LOC, script 1100 â 255 LOC)
- - [x] F1.4g Typecheck + lint (0 erreurs)
-- **Notes :** Les templates restent volumineux (~430-480 LOC) car le contenu UI est dense. Une extraction en sous-composants (DocumentList, ProductDisplay, CustomFieldForm) serait une etape future optionnelle.
-
----
-
-## Phase F2 - Elimination de la duplication frontend
-
-> **Priorite :** HAUTE - DRY
-
-### F2.1 Extraire `extractCollection()` dans un utilitaire partage
-
-- **Statut :** `[x]`
-- **Fichiers concernes :**
- - `composables/useSites.ts`
- - `composables/useProducts.ts`
- - `composables/usePieces.ts`
- - `composables/useComposants.ts`
- - `composables/useMachineTypesApi.js`
- - `composables/useConstructeurs.ts`
- - `composables/useDocuments.ts`
- - `composables/useMachineCreateSelections.ts`
- - `components/ComponentStructureAssignmentNode.vue`
- - `components/model-types/ManagementView.vue`
-- **Probleme :** La fonction `extractCollection()` (parsing `hydra:member` / `member` / `items` / `data` / array) etait dupliquee dans 10 fichiers.
-- **Action :**
- 1. [x] Creer `shared/utils/apiHelpers.ts` avec `extractCollection()` generique
- 2. [x] Remplacer les 10 implementations locales par un import
-- **Agent :** -
-- **Notes :** Gere aussi `items` (utilise par ManagementView.vue). `extractRelationId()` et `normalizeRelationIds()` restent dans `shared/apiRelations.ts` (deja partages).
-
-### F2.2 Fusionner les 3 composables d'historique
-
-- **Statut :** `[x]`
-- **Fichiers concernes :**
- - `composables/useComponentHistory.ts` (67 â 13 LOC, thin wrapper)
- - `composables/usePieceHistory.ts` (67 â 13 LOC, thin wrapper)
- - `composables/useProductHistory.ts` (67 â 13 LOC, thin wrapper)
- - `composables/useEntityHistory.ts` (NEW, 65 LOC, logique generique)
-- **Probleme :** 3 fichiers quasi identiques (seul le endpoint differait).
-- **Action :**
- 1. [x] Creer `composables/useEntityHistory.ts` parametrable par type d'entite
- 2. [x] Reecrire les 3 fichiers specifiques en wrappers backward-compatible
-- **Agent :** -
-- **Notes :** Les wrappers preservent l'API existante (types + fonction), aucun consommateur a modifier.
-
-### F2.3 Factoriser les composables de types (Component/Piece/Product)
-
-- **Statut :** `[x]`
-- **Fichiers concernes :**
- - `composables/useComponentTypes.ts` (165 â 30 LOC, thin wrapper)
- - `composables/usePieceTypes.ts` (165 â 30 LOC, thin wrapper)
- - `composables/useProductTypes.ts` (160 â 28 LOC, thin wrapper)
- - `composables/useEntityTypes.ts` (NEW, 172 LOC, logique generique)
-- **Probleme :** 3 composables tres similaires pour gerer les categories/types.
-- **Action :**
- 1. [x] Creer `composables/useEntityTypes.ts` generique (CRUD + singleton state par categorie)
- 2. [x] Reecrire les 3 fichiers specifiques en wrappers avec renommage des champs
-- **Agent :** -
-- **Notes :** Les wrappers renomment `types` â `componentTypes`/`pieceTypes`/`productTypes`, preservent `getXxxTypes()` et `isXxxTypeLoading()`. Etat partage via `stateByCategory` map module-level.
-
----
-
-## Phase F3 - Migration TypeScript
-
-> **Priorite :** HAUTE - Securite du typage
-
-### F3.1 Definir les types pour les reponses API
-
-- **Statut :** `[x]` (partiellement â types definis dans chaque composable + `ApiResponse` dans useApi.ts)
-- **Fichiers :**
- - `composables/useApi.ts` â `ApiResponse` generique (success/data/error/status)
- - `composables/useMachines.ts` â `Machine` interface
- - `composables/useMachineTypesApi.ts` â `MachineType`, `MachineTypeRequirement` interfaces
- - `composables/useToast.ts` â `Toast`, `ToastType` types
- - `composables/useProfiles.ts` â `Profile` interface
- - `composables/useCustomFields.ts` â `CustomFieldValue` interface
-- **Notes :** Les types sont definis dans chaque composable (colocation). Types entite existants : `Product`, `Piece`, `Composant`, `Constructeur`, `Site`, `Document` dans leurs composables respectifs (.ts). `shared/types/inventory.ts` contient les types de structure de modele.
-
-### F3.2 Convertir les composables JS en TS
-
-- **Statut :** `[x]`
-- **Fichiers convertis (7 fichiers JS â TS) :**
- - [x] `useToast.js` â `useToast.ts` (72 LOC, types: `Toast`, `ToastType`)
- - [x] `useProfiles.js` â `useProfiles.ts` (68 LOC, type: `Profile`)
- - [x] `useProfileSession.js` â `useProfileSession.ts` (85 LOC, importe `Profile`)
- - [x] `useApi.js` â `useApi.ts` (106 LOC â 120 LOC, types: `ApiResponse`, `ApiCallOptions`, ajout `put()`)
- - [x] `useCustomFields.js` â `useCustomFields.ts` (105 LOC, type: `CustomFieldValue`)
- - [x] `useMachineTypesApi.js` â `useMachineTypesApi.ts` (173 â 188 LOC, types: `MachineType`, `MachineTypeRequirement`)
- - [x] `useMachines.js` â `useMachines.ts` (267 LOC, type: `Machine`, utilise `extractCollection`)
-- **Fichiers deja TS :** `useProducts.ts`, `usePieces.ts`, `useComposants.ts`, `useConstructeurs.ts`, `useSites.ts`, `useDocuments.ts`
-- **Fichiers JS restants (deprecated) :** `useComponentModels.js`, `usePieceModels.js` (stubs deprecated, a supprimer)
-- **Notes :** `ApiResponse` par defaut `any` pour backward-compat. Les callers existants fonctionnent sans changement ; le nouveau code peut opt-in strict via `get()`.
-
-### F3.3 Eliminer les `any` restants
-
-- **Statut :** `[x]`
-- **Fichiers concernes :**
- - `components/ProductSelect.vue` â 1 `any` restant (slot template, incompressible)
- - `components/model-types/ManagementView.vue` â remplace `data?: any` â `Record`, `error: any` â `error: unknown`, `item: any` â `item: unknown`
- - `components/ComponentStructureAssignmentNode.vue` â 12 casts `(definition as any).typePiece/typeProduct` elimines grace a l'extension des types
- - `components/ComponentModelStructureEditor.vue` â `Promise` â `Promise`
- - `components/model-types/ModelTypeForm.vue` â `(incoming as any).description` â cast `Record`
- - `shared/types/inventory.ts` â `ComponentModelPiece.typePiece?` et `ComponentModelProduct.typeProduct?` ajoutes, 3 casts `(value as any)` supprimes
-- **Probleme :** 20+ usages de `any` type identifies.
-- **Action :** Etendre les interfaces de types pour supporter les formes alternatives de l'API. Remplacer les `any` par `unknown` ou `Record` la ou possible.
-- **Agent :** Claude
-- **Notes :** ~15 casts `any` elimines. Les `Record` restants dans ComponentModelStructureEditor sont justifies (manipulation dynamique interne de custom fields). Typecheck 0 erreurs.
-
----
-
-## Phase F4 - Qualite du code frontend
-
-> **Priorite :** MOYENNE
-
-### F4.1 Activer les regles ESLint critiques
-
-- **Statut :** `[x]` DONE
-- **Fichier :** `Inventory_frontend/eslint.config.mjs`
-- **Probleme :** Presque toutes les regles etaient desactivees (`no-console: off`, `no-unused-vars: off`, `no-explicit-any: off`).
-- **Action realisee :**
- 1. [x] Active `@typescript-eslint/no-explicit-any: warn` (526 warnings â amelioration progressive)
- 2. [x] Active `no-console: warn` avec `allow: ['error']` â 0 violations (deja nettoye en F4.2)
- 3. [x] Active `@typescript-eslint/no-unused-vars: warn` avec ignore `^_` â 0 violations (26 corrigees)
- 4. [x] Corrige les 26 violations `no-unused-vars` : imports inutilises supprimes, variables prefixees `_`, destructurations nettoyees
-- **Agent :** Claude
-- **Notes :** 16 fichiers modifies. Regles organisees par categorie (vue, console, typescript, formatting). 0 erreurs, 526 warnings `no-explicit-any` restants (warn, pas bloquant).
-
-### F4.2 Nettoyer les console.log/console.error
-
-- **Statut :** `[x]` (console.log supprime, console.error conserve)
-- **Fichiers modifies :** 8 fichiers (useMachineTypesApi.ts, useSites.ts, type/[id].vue, type/edit/[id].vue, TypeEditPieceRequirementsSection.vue, SearchSelect.vue, app.vue)
-- **Probleme :** 19 appels `console.log` de debug laisses dans le code de production.
-- **Action :**
- 1. [x] Supprimer les 19 `console.log` de debug (normalizeRequirementList, page loading, route params, etc.)
- 2. [ ] Les 72 `console.error` restants sont conserves (gestion d'erreur legitime). Migration vers un logger centralise a faire en F4.3.
-- **Agent :** Claude
-- **Notes :** 0 `console.log/warn/debug/info` restants dans le frontend.
-
-### F4.3 Centraliser la gestion d'erreurs API
-
-- **Statut :** `[ ]`
-- **Fichier :** `Inventory_frontend/app/composables/useApi.js` (105 LOC)
-- **Probleme :** Gestion d'erreur basique (juste un toast). Pas de retry, pas d'intercepteur, erreurs silencieuses dans certains composables.
-- **Action :**
- 1. Ajouter un systeme de retry configurable (1-3 tentatives)
- 2. Centraliser la gestion des erreurs HTTP (401 -> redirect login, 500 -> message explicite)
- 3. Ajouter des intercepteurs request/response
- 4. Uniformiser le pattern dans tous les composables
-- **Agent :** -
-- **Notes :** -
-
----
-
-## Phase F5 - Reduire le fichier modelUtils.ts (1017 LOC)
-
-> **Priorite :** MOYENNE
-
-### F5.1 Decouper `shared/modelUtils.ts`
-
-- **Statut :** `[x]`
-- **Fichier :** `Inventory_frontend/app/shared/modelUtils.ts` (1017 LOC â 37 LOC barrel)
-- **Probleme :** Fichier utilitaire monolithique de 1017 lignes regroupant toute la logique de manipulation de modeles.
-- **Action :**
- 1. Identifier les groupes de fonctions (structure, custom fields, requirements, serialization)
- 2. Decouper en 3 modules thematiques :
- - `shared/model/componentStructure.ts` (~590 LOC) â helpers, sanitize, hydrate, normalize, extract, format pour composants
- - `shared/model/pieceProductStructure.ts` (~155 LOC) â structure piece/produit (clone, sanitize, hydrate, format)
- - `shared/model/definitionOverrides.ts` (~50 LOC) â sanitization des overrides de definition
- 3. Re-exporter depuis `shared/modelUtils.ts` (barrel) pour ne pas casser les imports
-- **Agent :** Claude
-- **Notes :** 11 fichiers consommateurs inchanges (barrel preserve la retro-compat). Typecheck 0 erreurs.
-
----
-
-## Phase F6 - Tests frontend
-
-> **Priorite :** HAUTE - Aucun test actuellement
-
-### F6.1 Configurer Vitest
-
-- **Statut :** `[x]` DONE
-- **Fichiers crees :**
- - `vitest.config.ts` â config Vitest avec happy-dom, alias `~` et `#imports`
- - `tests/__mocks__/imports.ts` â mock des auto-imports Nuxt (useRuntimeConfig, useRoute, etc.)
- - `tests/shared/inventory-types.test.ts` â 9 tests smoke (validator, empty structures)
-- **Action realisee :**
- 1. [x] Installe `vitest`, `@vue/test-utils`, `happy-dom`
- 2. [x] Configure Vitest avec environment happy-dom et resolution d'alias
- 3. [x] Ajoute scripts `test` et `test:watch` dans `package.json`
- 4. [x] Premier test suite : `componentModelStructureValidator` (9 tests, 100% pass)
-- **Agent :** Claude
-- **Notes :** `npm test` â 9 tests, 0 failures, <1s. Alias `#imports` pointe vers un mock minimal extensible.
-
-### F6.2 Tests unitaires des composables
-
-- **Statut :** `[x]` DONE (base)
-- **Fichiers crees :**
- - `tests/shared/apiHelpers.test.ts` â 10 tests (extractCollection, tous formats API)
- - `tests/shared/modelUtils.test.ts` â 18 tests (isPlainObject, clone, stats, format, piece/product)
- - `tests/shared/inventory-types.test.ts` â 9 tests (validator, empty structures)
- - `tests/composables/useToast.test.ts` â 9 tests (add, types, max limit, clear, singleton)
- - `tests/composables/useConfirm.test.ts` â 8 tests (open, confirm, cancel, options, singleton)
-- **Action realisee :**
- 1. [x] Teste `extractCollection()` : array, hydra:member, member, items, data, null, undefined
- 2. [x] Teste `useToast` : ajout, types, max 3 toasts, clearAll, removeToast, singleton
- 3. [x] Teste `useConfirm` : open/close, resolve true/false, custom options, singleton state
- 4. [x] Teste `modelUtils` : clone, stats, preview, isPlainObject, piece/product variants
- 5. [x] Teste `componentModelStructureValidator` : valid/invalid, custom fields, subcomponents
-- **Agent :** Claude
-- **Notes :** 54 tests, 5 fichiers, 100% pass, <2s. Tests `useApi` et CRUD composables necessitent mock fetch (phase ulterieure).
-
-### F6.3 Tests de composants
-
-- **Statut :** `[ ]`
-- **Fichiers a creer :**
- - `tests/components/Pagination.test.ts`
- - `tests/components/SearchSelect.test.ts`
- - `tests/components/MachineHeader.test.ts` (apres F1.1)
-- **Action :**
- 1. Tester les composants communs (Pagination, SearchSelect)
- 2. Tester le rendu conditionnel et les events
-- **Agent :** -
-- **Notes :** -
-
----
-
-## Phase F7 - Ameliorations UX/DX
-
-> **Priorite :** BASSE - Polish
-
-### F7.1 Reduire le props drilling
-
-- **Statut :** `[ ]`
-- **Probleme :** Props passees sur 3+ niveaux (ex: machine data dans les sous-composants).
-- **Action :**
- 1. Identifier les cas de props drilling >2 niveaux
- 2. Utiliser `provide/inject` ou des composables partages
- 3. Documenter le pattern choisi
-- **Agent :** -
-- **Notes :** A traiter apres F1 (decoupage des composants).
-
-### F7.2 Remplacer `confirm()` natif par des modales DaisyUI
-
-- **Statut :** `[x]` DONE
-- **Probleme :** Les confirmations de suppression utilisaient `window.confirm()` (UI native, non-stylee).
-- **Action realisee :**
- 1. [x] Cree `composables/useConfirm.ts` â composable promise-based avec etat reactif partage
- 2. [x] Cree `components/common/ConfirmModal.vue` â modale DaisyUI teleportee (backdrop blur, btn-error)
- 3. [x] Monte `ConfirmModal` globalement dans `app.vue`
- 4. [x] Remplace les 10 `confirm()` natifs dans 10 fichiers :
- - `constructeurs.vue`, `profiles/manage.vue`, `ManagementView.vue`
- - `product-catalog.vue`, `index.vue`, `machines/index.vue`
- - `machine-skeleton/index.vue`, `pieces-catalog.vue`, `component-catalog.vue`
- - `useSiteManagement.ts` (composable â import explicite)
-- **Agent :** Claude
-- **Notes :** API : `const { confirm } = useConfirm(); const ok = await confirm({ message: '...' })`. Auto-import Nuxt pour les SFC, import explicite pour les composables.
-
-### F7.3 Nettoyer `app.vue` (861 LOC)
-
-- **Statut :** `[x]` DONE
-- **Fichier :** `Inventory_frontend/app/app.vue` (861 â 49 LOC)
-- **Probleme :** Le fichier racine contenait le layout principal, la navbar (~676 LOC dupliquee mobile/desktop), et du state management.
-- **Action realisee :**
- 1. Cree `composables/useNavDropdown.ts` (~65 LOC) â gestion etat dropdowns navbar
- 2. Cree `components/layout/AppNavbar.vue` (~310 LOC) â navbar data-driven avec `v-for` eliminant duplication mobile/desktop
- 3. `app.vue` reecrit en orchestrateur minimal (49 LOC) + converti en TypeScript
- 4. Supprime 4 imports d'icones inutilises
-- **Agent :** Claude
-- **Notes :** Approche data-driven : liens et groupes definis comme tableaux types (`NavLink[]`, `NavGroup[]`), rendus par `v-for` pour mobile et desktop
-
----
-
-## Ordre d'execution recommande
-
-```
-=== BACKEND === === FRONTEND ===
-
-Phase 6.1 (Tests unitaires) Phase F6.1 (Config Vitest)
- | |
- v v
-Phase 1 (Securite) Phase F1 (Decoupage mega-composants)
- | |
- v v
-Phase 2 (Duplication backend) Phase F2 (Duplication frontend)
- | |
- v v
-Phase 3 (Controllers) Phase F3 (Migration TypeScript)
- | |
- v v
-Phase 6.2 (Tests API) Phase F4 (Qualite code) + Phase F5 (modelUtils)
- | |
- v v
-Phase 4 (Stockage) Phase F6.2-F6.3 (Tests frontend)
- | |
- v v
-Phase 5 + Phase 7 (Nettoyage) Phase F7 (UX/DX polish)
- |
- v
-Phase 6.3 (Tests audit)
-```
-
-> Les colonnes backend et frontend peuvent etre executees **en parallele** par des agents differents.
-
----
-
-## Journal des modifications
-
-| Date | Phase | Tache | Agent | Statut | Notes |
-| ---------- | ----- | ------------------------- | --------------- | ------- | ---------------------------------------------- |
-| 2026-02-03 | - | Creation du plan backend | Claude Opus 4.5 | Termine | Analyse initiale backend (7 phases, 17 taches) |
-| 2026-02-03 | - | Creation du plan frontend | Claude Opus 4.5 | Termine | Analyse frontend (7 phases, 22 taches) |
-| | | | | | |
-
----
-
-## Commandes de verification
-
-> **Contexte :** Le backend tourne dans Docker (`docker compose`), le frontend est en local.
-> Les commandes ci-dessous sont executees **depuis la racine du projet** (`/home/matthieu/dev_malio/Inventory/`).
-
-### Frontend (Nuxt 3 / Vue 3 / TypeScript)
-
-| Commande | Description | Quand l'utiliser |
-| -------------------- | ----------------------------------------------- | ------------------------------------------------------------------------------------------------- |
-| `npx nuxi typecheck` | Verification des types TypeScript via `vue-tsc` | Apres chaque modification de fichier `.vue` ou `.ts`. C'est la commande principale de validation. |
-| `npm run lint` | ESLint (config dans `eslint.config.mjs`) | Apres chaque modification pour verifier le style et les erreurs statiques. |
-| `npm run lint:fix` | ESLint avec auto-fix | Pour corriger automatiquement les erreurs de formatage. |
-| `npm run build` | Build de production Nuxt (inclut le typecheck) | Avant un commit pour s'assurer que tout compile. Plus lent que `typecheck` seul. |
-| `npx nuxi prepare` | Regenerer les types auto-generes (`.nuxt/`) | Si les imports auto (composables, components) ne sont pas reconnus par le typecheck. |
-
-> **Toutes les commandes frontend** sont executees depuis `Inventory_frontend/` :
->
-> ```bash
-> cd Inventory_frontend && npx nuxi typecheck
-> ```
-
-> **Note sur les erreurs pre-existantes :** Il y a ~120 erreurs TypeScript pre-existantes documentees
-> (anterieures a la refacto). L'objectif est de ne pas en ajouter de nouvelles.
-> Pour verifier : comparer le nombre d'erreurs avant/apres modification.
-
-### Backend (Symfony 8 / PHP 8.4)
-
-| Commande | Description | Quand l'utiliser |
-| ---------------------------------------------- | ----------------------------------------------------- | ------------------------------------------------------------------- |
-| `vendor/bin/php-cs-fixer fix --dry-run --diff` | Verifie le style PHP (PSR-12 + Symfony) sans modifier | Apres chaque modification PHP. |
-| `vendor/bin/php-cs-fixer fix` | Corrige automatiquement le style PHP | Avant chaque commit. |
-| `bin/phpunit` | Lance les tests PHPUnit | Apres chaque modification backend. |
-| `php bin/console cache:clear` | Vide le cache Symfony | Si des erreurs bizarres apparaissent apres un changement de config. |
-
-> **Les commandes backend** sont executees **dans le conteneur Docker** :
->
-> ```bash
-> docker compose exec web vendor/bin/php-cs-fixer fix --dry-run --diff
-> docker compose exec web bin/phpunit
-> ```
-
-### Workflow de verification (checklist par tache)
-
-```
-1. Lire les fichiers concernes (AVANT toute modification)
-2. Effectuer les modifications
-3. Frontend : npx nuxi typecheck â verifier pas de nouvelles erreurs
-4. Frontend : npm run lint:fix â corriger le formatage
-5. Backend : php-cs-fixer fix â corriger le style PHP
-6. Backend : bin/phpunit â verifier la non-regression
-7. Commit si tout est OK
-```
-
----
-
-## Regles pour les agents
-
-1. **Avant de commencer une tache :**
- - Mettre le statut a `[~]` dans ce fichier
- - Inscrire son nom/ID dans la colonne "Agent"
- - Lire les fichiers concernes AVANT de modifier quoi que ce soit
-
-2. **Pendant le travail :**
- - Ne modifier QUE les fichiers listes dans la tache
- - Respecter les conventions existantes (PSR-12, strict_types)
- - Ne pas introduire de nouvelles dependances sans justification
- - Lancer `php-cs-fixer` apres les modifications
-
-3. **Apres avoir termine :**
- - Mettre le statut a `[x]`
- - Ajouter une entree dans le "Journal des modifications"
- - Lancer les tests existants (`make test`) pour verifier la non-regression
- - Decrire brievement les changements effectues dans "Notes"
-
-4. **En cas de blocage :**
- - Mettre le statut a `[!]`
- - Documenter le blocage dans "Notes"
- - Ne PAS passer a une autre tache sans signaler le blocage
-
-5. **Regles specifiques au frontend :**
- - Ecrire en TypeScript (pas de JS pour les nouveaux fichiers)
- - Pas de `any` - utiliser des types concrets
- - Pas de `console.log` - utiliser le logger ou `useToast`
- - Composants Vue : max 400 LOC par fichier
- - Utiliser les composants DaisyUI existants (pas de CSS custom)
- - Tester avec Vitest quand la config est en place
-
-6. **Regles specifiques au backend :**
- - `declare(strict_types=1)` obligatoire
- - Respecter PSR-12 + regles Symfony (php-cs-fixer)
- - Pas de `exec()` direct - utiliser Symfony Process
- - Tester avec PHPUnit
diff --git a/migratebdd.md b/migratebdd.md
deleted file mode 100644
index 65f3ae6..0000000
--- a/migratebdd.md
+++ /dev/null
@@ -1,35 +0,0 @@
-# Migration DB (manuel)
-
-Ce guide explique comment importer un dump SQL venant de pgAdmin dans la base Docker.
-
-## 1) Export pgAdmin
-
-Dans pgAdmin:
-
-- Format: Plain
-- Options: Use INSERT commands + Use column inserts
-- Fichier: `data.sql`
-
-## 2) Normaliser le dump
-
-Convertit les colonnes camelCase en lowercase compact.
-
-```bash
-python3 scripts/normalize-dump.py data.sql data_norm.sql --lower
-```
-
-## 3) Importer dans la base Docker
-
-Utilise `session_replication_role` pour eviter les erreurs de contraintes circulaires.
-
-```bash
-docker compose --env-file docker/.env.docker.local exec -T db psql -U root -d inventory -v ON_ERROR_STOP=1 -c "SET session_replication_role = replica;"
-docker compose --env-file docker/.env.docker.local exec -T db psql -U root -d inventory -v ON_ERROR_STOP=1 < data_norm.sql
-docker compose --env-file docker/.env.docker.local exec -T db psql -U root -d inventory -v ON_ERROR_STOP=1 -c "SET session_replication_role = DEFAULT;"
-```
-
-## 4) Verifier
-
-```bash
-docker compose --env-file docker/.env.docker.local exec -T db psql -U $POSTGRES_USER -d $POSTGRES_DB -c "\\dt"
-```