diff --git a/docs/superpowers/specs/2026-03-12-piece-quantity-design.md b/docs/superpowers/specs/2026-03-12-piece-quantity-design.md index 2a008bd..943f3af 100644 --- a/docs/superpowers/specs/2026-03-12-piece-quantity-design.md +++ b/docs/superpowers/specs/2026-03-12-piece-quantity-design.md @@ -8,7 +8,7 @@ L'application gère des machines composées de composants et de pièces. Une mê - Quantité sur les **pièces directement liées à une machine** (`MachinePieceLink`) - Quantité sur les **pièces d'un composant** (JSON `structure.pieces` du `Composant`) -- **Hors scope** : quantité sur `MachineComponentLink`, `MachineProductLink`, override de quantité composant au niveau machine +- **Hors scope** : quantité sur `MachineComponentLink`, `MachineProductLink`, override de quantité composant au niveau machine, audit logging ## Règles métier @@ -17,8 +17,9 @@ L'application gère des machines composées de composants et de pièces. Une mê | Pièce directement sur machine | `MachinePieceLink.quantity` | Page machine | Oui, éditable | | Pièce d'un composant | `Composant.structure.pieces[].quantity` | Page composant (création + édition) | Oui, lecture seule | -- Type : entier, valeur par défaut = 1 +- Type : entier, valeur par défaut = 1, minimum = 1 - Affichage : "×N" après le nom de la pièce, masqué si N = 1 +- Quantité = 0 n'est pas valide (utiliser la suppression du lien à la place) ## Backend @@ -28,11 +29,12 @@ Ajout d'un champ `quantity` sur l'entité `MachinePieceLink` : ```php #[ORM\Column(type: Types::INTEGER, options: ['default' => 1])] -#[Groups(['machine_piece_link:read', 'machine_piece_link:write'])] private int $quantity = 1; ``` -Getter/setter standard. +Getter/setter standard. **Pas de `#[Groups]`** — cohérent avec les autres champs de l'entité qui n'en déclarent pas (l'entité n'a pas de `normalizationContext`). + +Validation : `#[Assert\GreaterThanOrEqual(1)]` ### 2. Migration SQL @@ -60,29 +62,69 @@ Les entrées existantes sans `quantity` sont traitées comme `quantity = 1` (dé ### 4. MachineStructureController -- `normalizePieceLinks()` : inclure `quantity` dans la réponse JSON - - Pièce machine directe (parentLink = null) : `quantity` depuis `MachinePieceLink.quantity` - - Pièce sous composant : `quantity` depuis le `structure.pieces` du composant source -- **PATCH** : accepter `quantity` pour les pièces directement sur la machine uniquement +#### Normalisation (GET) + +`normalizePieceLinks()` : inclure `quantity` dans la réponse JSON : +- **Pièce machine directe** (parentLink = null) : `quantity` depuis `MachinePieceLink.quantity` +- **Pièce sous composant** : `quantity` depuis le `structure.pieces` du composant source. Résolution : + 1. Naviguer `MachinePieceLink` → `parentLink` (MachineComponentLink) → `composant` → `structure['pieces']` + 2. Matcher par index de position dans le tableau `pieces` (l'ordre des pièces dans la structure correspond à l'ordre de création des liens) + 3. Fallback : `quantity = 1` si non trouvé + +#### PATCH structure + +Dans `applyPieceLinks()`, accepter `quantity` au même niveau que `pieceId` dans le payload : + +```json +{ + "pieceLinks": [ + { "pieceId": "cl...", "quantity": 4, "overrides": { "nameOverride": "..." } } + ] +} +``` + +- `quantity` est appliqué uniquement pour les pièces directement sur la machine (pas de `parentComponentLinkId`) +- Si `parentComponentLinkId` est présent, `quantity` est **ignoré silencieusement** (la valeur vient du composant) + +#### Clone + +`clonePieceLinks()` doit copier `quantity` depuis le lien source : + +```php +$newLink->setQuantity($link->getQuantity()); +``` + +Sans cela, les machines clonées perdraient les quantités (reset à 1). + +### 5. Tests + +Ajouter dans `MachinePieceLinkTest.php` : +- POST avec `quantity` explicite → vérifier la valeur +- POST sans `quantity` → vérifier défaut = 1 +- PATCH `quantity` sur pièce directe → vérifier mise à jour +- GET structure → vérifier `quantity` dans la réponse normalisée +- Clone → vérifier que `quantity` est préservé ## Frontend ### 1. Types TypeScript -Mise à jour de `ComponentModelPiece` : +Mise à jour de `ComponentModelPiece` dans `shared/types/inventory.ts` — ajout du champ `quantity` : ```typescript -interface ComponentModelPiece { - typePieceId?: string - typePieceLabel?: string - reference?: string - familyCode?: string - role?: string - quantity?: number // défaut 1 -} +quantity?: number // défaut 1 ``` -### 2. Pages composant (création + édition) +### 2. Fonctions de sanitization/normalisation à mettre à jour + +Ces fonctions énumèrent explicitement les champs à conserver et doivent inclure `quantity` : + +- `normalizeStructureForSave()` dans `shared/model/componentStructure.ts` — inclure `quantity` dans le payload backend des pièces +- `sanitizePieceDefinition()` dans `shared/utils/structureAssignmentHelpers.ts` — préserver `quantity` +- `sanitizePieces()` dans `shared/model/componentStructureSanitize.ts` — préserver `quantity` dans la sortie +- `hydratePieces()` / `mapComponentPieces()` — préserver `quantity` lors de l'hydratation + +### 3. Pages composant (création + édition) Dans l'éditeur de structure, chaque pièce du tableau `structure.pieces` affiche un champ input : - Type : `number`, min = 1, step = 1 @@ -90,7 +132,7 @@ Dans l'éditeur de structure, chaque pièce du tableau `structure.pieces` affich - Style : `input input-bordered input-sm md:input-md` (DaisyUI) - Position : à côté des champs existants (reference, role) -### 3. Page machine (détail/structure) +### 4. Page machine (détail/structure) - **Pièce directe** (parentLink = null) : affiche "×N" à côté du nom, quantité éditable (input entier) - **Pièce de composant** : affiche "×N" à côté du nom, lecture seule (pas d'input)