Files
Inventory/CLAUDE.md
T
Matthieu 477295c400
Auto Tag Develop / tag (push) Successful in 9s
docs(claude) : frontend dans le même repo (plus de submodule)
2026-05-29 15:49:49 +02:00

17 KiB

CLAUDE.md — Inventory Project

Project Overview

Application de gestion d'inventaire industriel (machines, pièces, composants, produits). Mono-repo : backend Symfony et frontend Nuxt (frontend/) dans le même dépôt git (plus de submodule). Un seul commit/push couvre backend + frontend.

Stack

Layer Tech Version
Backend Symfony + API Platform 8.0 / ^4.2
PHP PHP >=8.4
Database PostgreSQL 16
Frontend Nuxt (SPA, SSR off) 4
UI Vue 3 Composition API + TypeScript 3.5 / 5.7
CSS TailwindCSS 4 + DaisyUI 5
Auth Session-based (cookies, pas JWT)
Containers Docker Compose

Glossaire Métier

Voir docs/GLOSSAIRE_METIER.md — glossaire complet du domaine métier (concepts, workflows utilisateur, correspondance métier↔code). À consulter pour comprendre le "pourquoi" derrière le code.

Project Structure

Inventory/                      # Backend Symfony (repo principal)
├── src/Entity/                 # Entités Doctrine (annotations PHP 8 attributes)
│   └── Trait/                  # CuidEntityTrait (génération d'ID CUID)
├── src/Controller/             # Controllers custom (session, comments, audit…)
├── src/EventSubscriber/        # Audit subscribers (onFlush)
├── src/Service/               # Services métier (sync, conversion, storage…)
├── src/Enum/                  # Enums PHP (DocumentType, ModelCategory)
├── src/DTO/                   # Data Transfer Objects (sync workflow)
├── src/Filter/                # Filtres API Platform custom
├── src/Command/               # Commandes Symfony CLI (compress-pdf, create-profile…)
├── config/                     # Config Symfony
├── migrations/                 # Migrations Doctrine (raw SQL PostgreSQL)
├── docker/                     # Dockerfile + .env.docker
├── scripts/                    # release.sh, normalize-dump.py
├── fixtures/                   # SQL fixtures
├── tests/                      # PHPUnit
├── pre-commit, commit-msg      # Git hooks
├── makefile                    # Commandes Docker/dev
├── VERSION                     # Source unique de version (semver)
├── frontend/         # ← Frontend Nuxt (DANS le même repo, pas un submodule)
│   ├── app/pages/              # Pages Nuxt (file-based routing)
│   ├── app/components/         # Composants Vue (auto-imported)
│   ├── app/composables/        # Composables Vue
│   ├── app/shared/             # Types, utils, validation
│   ├── app/middleware/          # Auth middleware global
│   └── app/services/           # Service layer (wrappers useApi)

Key Commands

# Docker
make start                    # Démarrer les containers
make stop                     # Arrêter
make shell                    # Shell interactif (nécessite un TTY)
make install                  # Install complet (composer + npm + build)

# Backend
make test                     # PHPUnit (tous les tests)
make test FILES=tests/Api/Entity/MachineTest.php   # Un test spécifique
make php-cs-fixer-allow-risky # Linter PHP (cs-fixer)
docker exec -u www-data php-inventory-apache php bin/console doctrine:migrations:migrate

# Frontend (dans frontend/)
npm run dev                   # Dev server (port 3001)
npm run build                 # Build production
npm run lint:fix              # ESLint fix
npx nuxi typecheck            # TypeScript check (0 errors attendu)

# Database / Fixtures
make db-reset                 # Reset database (drop + recreate schema)
make fixtures-dump            # Dump la DB vers fixtures/data.sql
make fixtures-load            # Charger les fixtures SQL (désactive FK)
make fixtures-reset           # Reset DB + recharger fixtures
make import-data              # Importer les dumps SQL normalisés
make cache-clear              # Clear cache Symfony

# Import fournisseurs (customer.json → Constructeur + ConstructeurCategorie + ConstructeurTelephone)
docker exec -u www-data php-inventory-apache php bin/console app:import-fournisseurs            # dry-run (par défaut)
docker exec -u www-data php-inventory-apache php bin/console app:import-fournisseurs --force    # applique
# Non destructif : find-or-create par nom normalisé, ne change jamais un ID existant, n'ajoute que les téléphones/catégories manquants

# Release
./scripts/release.sh patch    # Bump patch version (ou minor/major)

Git Conventions

Branches

  • master — production
  • develop — branche principale de dev (cible des PR)
  • feat/xxx, fix/xxx, refactor/xxx — branches de travail

Commit Message Format (enforced by hook)

<type>(<scope optionnel>) : <message>

Espace obligatoire autour du :. Types autorisés (minuscules) : build, chore, ci, docs, feat, fix, perf, refactor, revert, style, test, wip

Exemples :

  • feat(auth) : add login page
  • fix(machines) : prevent null crash on skeleton creation

Pre-commit Hook

  1. php-cs-fixer sur les fichiers PHP stagés
  2. PHPUnit — bloque le commit si tests échouent

Workflow commit (backend + frontend dans le même repo)

Le frontend n'est pas un submodule : frontend/ est versionné dans le dépôt principal. Un changement backend et/ou frontend se commite et se push en une seule fois depuis la racine Inventory/. Pas de double commit ni de pointeur de submodule à gérer.

  • Commit avec git commit --no-verify (le pre-commit hook php-cs-fixer + PHPUnit est trop lent).
  • Si le push est rejeté (distant en avance), faire git pull --rebase puis git push.

Architecture Backend

Entités Principales

Machine, Piece, Composant, Product, Constructeur, ConstructeurCategorie, ConstructeurTelephone, Site, ModelType, CustomField, CustomFieldValue, Document, AuditLog, Comment, Profile, MachineComponentLink, MachinePieceLink, MachineProductLink

Constructeur (Fournisseur) : possède name, email, une collection telephones (1-N → ConstructeurTelephone, cascade/orphanRemoval) et categories (M2M → ConstructeurCategorie, table constructeur_categories). Sérialisation API Platform via les groupes constructeur:read / constructeur:write (téléphones & catégories embarqués). ⚠️ L'adder M2M s'appelle addCategory()/removeCategory() (l'inflector singularise categoriescategory), pas addCategorie. ConstructeurCategorie et ConstructeurTelephone sont aussi des ApiResource à part entière (/api/constructeur_categories, /api/constructeur_telephones).

Entités de normalisation (slots & skeleton requirements)

Remplacent les anciennes colonnes JSON structure et productIds par des tables relationnelles :

  • Slots composant (données réelles d'un composant) : ComposantPieceSlot, ComposantSubcomponentSlot, ComposantProductSlot
  • Slots pièce (données réelles d'une pièce) : PieceProductSlot
  • Skeleton Requirements (définitions du ModelType) : SkeletonPieceRequirement, SkeletonProductRequirement, SkeletonSubcomponentRequirement

Patterns

  • IDs : CUID-like strings ('cl' + bin2hex(random_bytes(12))), pas d'auto-increment
  • ORM : Attributs PHP 8 (#[ORM\Column(...)], #[Groups([...])])
  • Lifecycle : #[ORM\HasLifecycleCallbacks] avec PrePersist/PreUpdate pour createdAt/updatedAt
  • Sécurité : security: "is_granted('ROLE_...')" sur chaque opération API Platform
  • Audit : Subscribers Doctrine onFlush capturent diff + snapshot complet
  • Migrations : Raw SQL PostgreSQL avec IF NOT EXISTS/IF EXISTS pour idempotence

Custom Controllers (pas API Platform)

  • MachineStructureController/api/machines/{id}/structure (GET/PATCH), /api/machines/{id}/clone (POST) : hiérarchie complète machine avec normalisation JSON manuelle. Source principale de données pour la page détail machine.
  • MachineCustomFieldsController/api/machines/{id}/add-custom-fields (POST) : initialise les CustomFieldValue manquants pour une machine.
  • CustomFieldValueController/api/custom-fields/values/* : CRUD + upsert pour les valeurs de champs perso.
  • ComposantPieceSlotController/api/composant-piece-slots/{id} (PATCH) : mise à jour des slots pièce d'un composant.
  • ComposantProductSlotController/api/composant-product-slots/{id} (PATCH) : mise à jour des slots produit d'un composant.
  • ComposantSubcomponentSlotController/api/composant-subcomponent-slots/{id} (PATCH) : mise à jour des slots sous-composant d'un composant.
  • SessionProfileController/api/session/profile (GET/POST/DELETE) : auth session (login/logout/current user).
  • SessionProfilesController/api/session/profiles (GET) : liste des profils disponibles pour la session.
  • AdminProfileController/api/admin/profiles : CRUD profils, gestion rôles et mots de passe (ROLE_ADMIN).
  • CommentController/api/comments : création, résolution, compteur non-résolus.
  • ActivityLogController/api/activity-logs (GET) : journal d'activité global.
  • EntityHistoryController/api/{entity}/{id}/history (GET) : historique audit par entité (machines, pièces, composants, produits).
  • DocumentQueryController/api/documents/{entity}/{id} (GET) : documents par site/machine/composant/pièce/produit.
  • DocumentServeController/api/documents/{id}/file|download (GET) : servir/télécharger fichiers.
  • ModelTypeConversionController/api/model_types/{id}/conversion-check|convert : vérification et conversion de ModelType.
  • ModelTypeSyncController/api/model_types/{id}/sync-preview|sync-confirm (POST) : prévisualisation et application de sync ModelType→Composants.
  • EntityVersionController/api/{entity}/{id}/versions (GET), /api/{entity}/{id}/versions/{version}/restore (POST) : historique de versions numérotées et restauration.
  • HealthCheckController/api/health (GET) : health check.

Custom Fields — Architecture

  • Composants/Pièces/Produits : définitions dans les entités SkeletonPieceRequirement, SkeletonProductRequirement, SkeletonSubcomponentRequirement du ModelType (anciennement JSON structure, normalisé en tables relationnelles). Les custom fields de ces entités sont définis dans customFields JSON sur chaque Skeleton*Requirement.
  • Machines : définitions = entités CustomField liées directement via machineId FK (pas de ModelType)
  • Les deux partagent la même entité CustomFieldValue pour stocker les valeurs

Enums (src/Enum/)

  • DocumentType — types de documents (photo, schéma, facture, etc.)
  • ModelCategory — catégories de ModelType

Services (src/Service/)

  • ModelTypeSyncService — synchronise les skeleton requirements d'un ModelType vers les composants existants
  • ModelTypeCategoryConversionService — conversion de catégorie d'un ModelType
  • SkeletonStructureService — gestion de la structure skeleton (requirements)
  • DocumentStorageService — stockage et gestion des fichiers documents
  • PdfCompressorService — compression des PDFs uploadés
  • EntityVersionService — gestion des versions numérotées (snapshot, restore) pour machines, pièces, composants, produits
  • ReferenceAutoGenerator — génération automatique de références pour pièces et composants à partir de formules ModelType
  • src/Service/Sync/ — stratégies de sync par type de slot (tagged app.sync_strategy)

DTOs (src/DTO/)

  • SyncConfirmation, SyncPreviewResult, SyncExecutionResult — objets de transfert pour le workflow de sync ModelType

Filters (src/Filter/)

  • MultiSearchFilter — filtre API Platform pour recherche OR sur plusieurs champs (ex: name + reference)

EventSubscribers notables (non-audit)

  • PieceProductSyncSubscriber — sync automatique des PieceProductSlots
  • UniqueConstraintSubscriber — traduit les erreurs de contrainte unique PG en messages utilisateur lisibles
  • ReferenceAutoSubscriber — recalcule les références auto des pièces/composants quand les CustomFieldValues changent (onFlush)

Rôles (hiérarchie)

ROLE_ADMIN → ROLE_GESTIONNAIRE → ROLE_VIEWER → ROLE_USER

PostgreSQL — ATTENTION

  • Les noms de colonnes sont TOUJOURS EN MINUSCULES dans PG
  • Doctrine utilise camelCase (typePieceId) mais PG stocke typepieceid
  • Le SQL brut doit utiliser les noms lowercase
  • Tables de jointure many-to-many : colonnes a et b (ex: _piececonstructeurs)

Architecture Frontend

Patterns

  • Composables : interface Deps { ... } + export function useXxx(deps: Deps)
  • Communication composants : Props + Events uniquement (pas de provide/inject)
  • API : useApi.ts wraps fetch avec credentials: 'include' pour les cookies session
  • ⚠️ Préfixe /api : useApi() prepend déjà apiBaseUrl (= /api par défaut, cf. nuxt.config.ts). Les appels doivent donc utiliser des chemins sans /api au début. Ex : api.get('/custom-fields/names') et PAS api.get('/api/custom-fields/names') (sinon 404 sur /api/api/...).
  • Content-Type : application/ld+json pour POST/PUT, application/merge-patch+json pour PATCH
  • Auth : useProfileSession + middleware global profile.global.ts
  • Permissions : usePermissions.ts miroir de la hiérarchie backend côté client
  • Auto-imports : Nuxt auto-importe composants (components/) et composables (composables/)

DaisyUI Classes

  • Input : input input-bordered input-sm md:input-md
  • Textarea : textarea textarea-bordered textarea-sm md:textarea-md
  • Select : select select-bordered select-sm md:select-md
  • Button : btn btn-sm md:btn-md btn-primary

Règles Importantes

CLAUDE.md — Maintenance obligatoire

  • Toujours consulter ce fichier en début de conversation pour respecter les conventions
  • Mettre à jour ce fichier quand une nouvelle convention, pattern ou décision architecturale est établie
  • Utiliser comme source de vérité pour les commandes, patterns et règles du projet

Toujours faire AVANT de modifier du code

  1. Lire le fichier avant de l'éditer — ne jamais proposer de changements sur du code non lu
  2. Comprendre le pattern existant — reproduire le style du fichier (noms, indentation, structure)
  3. Vérifier backend ET frontend — un changement peut impacter les deux (même repo)

Après chaque modification

  1. Backend PHP : make php-cs-fixer-allow-risky
  2. Frontend : npm run lint:fix puis npx nuxi typecheck si fichiers TS modifiés

Ne jamais faire

  • Ajouter des features non demandées, du code mort, ou des abstractions prématurées
  • Utiliser provide/inject — le codebase utilise Props + Events
  • Utiliser JWT/tokens — l'auth est session-based
  • Écrire du SQL avec des noms camelCase — PostgreSQL = lowercase
  • Committer sans que l'utilisateur le demande explicitement
  • Force push sans confirmation explicite
  • Modifier la config git

Synchronisation master ↔ develop

Un seul repo (backend + frontend). Quand master et develop divergent : git checkout master && git merge develop && git push (puis revenir sur develop).

Tests

Stack de test

  • PHPUnit 12 + API Platform Test (ApiTestCase)
  • DAMA DoctrineTestBundle — wrappe chaque test dans une transaction avec rollback automatique (pas de TRUNCATE)
  • Base de test : même PG, env test

Commandes

Voir section "Key Commands". Commande additionnelle :

make test-setup               # Créer/mettre à jour le schéma test

Pattern de test

  • Hériter de AbstractApiTestCase (helpers auth + factories)
  • Ne PAS faire de TRUNCATE/cleanup dans tearDown — DAMA s'en occupe par rollback
  • Factories : createProfile(), createMachine(), createSite(), createComposant(), createPiece(), createProduct(), createConstructeur(), createCustomField(), createCustomFieldValue(), createModelType(), createMachineComponentLink(), createMachinePieceLink(), createMachineProductLink(), createComposantPieceSlot(), createComposantSubcomponentSlot(), createComposantProductSlot(), createPieceProductSlot(), createConstructeurCategorie(), createConstructeurTelephone()
  • Auth : createViewerClient(), createGestionnaireClient(), createAdminClient(), createUnauthenticatedClient()

URLs Locales

  • API Symfony : http://localhost:8081/api
  • Nuxt dev : http://localhost:3001
  • Adminer (PG) : http://localhost:5050
  • PG direct : localhost:5433 (user: root, pass: root, db: inventory)

Delegation Codex

Pour les taches mecaniques (tests, boilerplate, renommages, refacto repetitif), delegue a Codex via le plugin codex. Garde Claude pour la reflexion, l'architecture et la verification.

  • Codex = junior dev rapide et pas cher (executions mecaniques)
  • Claude = senior dev qui verifie et reflechit (design, review, decisions)

C'est le meilleur ratio qualite/credits.