Address review findings: drop Groups attribute, add clone logic, specify PATCH payload format, list frontend functions to update, add validation and test cases. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
5.3 KiB
Piece Quantity — Design Spec
Context
L'application gère des machines composées de composants et de pièces. Une même pièce (catalogue) peut apparaître dans plusieurs contextes avec des quantités différentes. La quantité doit être portée par la relation, pas par l'entité catalogue.
Scope
- Quantité sur les pièces directement liées à une machine (
MachinePieceLink) - Quantité sur les pièces d'un composant (JSON
structure.piecesduComposant) - Hors scope : quantité sur
MachineComponentLink,MachineProductLink, override de quantité composant au niveau machine, audit logging
Règles métier
| Contexte | Stockage | Éditable depuis | Visible sur machine |
|---|---|---|---|
| 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, 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
1. MachinePieceLink — Nouvelle colonne
Ajout d'un champ quantity sur l'entité MachinePieceLink :
#[ORM\Column(type: Types::INTEGER, options: ['default' => 1])]
private int $quantity = 1;
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
ALTER TABLE machine_piece_link ADD COLUMN IF NOT EXISTS quantity INTEGER NOT NULL DEFAULT 1;
Idempotente avec IF NOT EXISTS.
3. Composant.structure JSON
Le tableau pieces dans le JSON structure du Composant accepte une nouvelle clé quantity. Pas de migration DB nécessaire — c'est un champ JSON libre.
Avant :
{ "typePieceId": "...", "role": "Filtration" }
Après :
{ "typePieceId": "...", "role": "Filtration", "quantity": 4 }
Les entrées existantes sans quantity sont traitées comme quantity = 1 (défaut côté frontend et backend).
4. MachineStructureController
Normalisation (GET)
normalizePieceLinks() : inclure quantity dans la réponse JSON :
- Pièce machine directe (parentLink = null) :
quantitydepuisMachinePieceLink.quantity - Pièce sous composant :
quantitydepuis lestructure.piecesdu composant source. Résolution :- Naviguer
MachinePieceLink→parentLink(MachineComponentLink) →composant→structure['pieces'] - 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) - Fallback :
quantity = 1si non trouvé
- Naviguer
PATCH structure
Dans applyPieceLinks(), accepter quantity au même niveau que pieceId dans le payload :
{
"pieceLinks": [
{ "pieceId": "cl...", "quantity": 4, "overrides": { "nameOverride": "..." } }
]
}
quantityest appliqué uniquement pour les pièces directement sur la machine (pas deparentComponentLinkId)- Si
parentComponentLinkIdest présent,quantityest ignoré silencieusement (la valeur vient du composant)
Clone
clonePieceLinks() doit copier quantity depuis le lien source :
$newLink->setQuantity($link->getQuantity());
Sans cela, les machines clonées perdraient les quantités (reset à 1).
5. Tests
Ajouter dans MachinePieceLinkTest.php :
- POST avec
quantityexplicite → vérifier la valeur - POST sans
quantity→ vérifier défaut = 1 - PATCH
quantitysur pièce directe → vérifier mise à jour - GET structure → vérifier
quantitydans la réponse normalisée - Clone → vérifier que
quantityest préservé
Frontend
1. Types TypeScript
Mise à jour de ComponentModelPiece dans shared/types/inventory.ts — ajout du champ quantity :
quantity?: number // défaut 1
2. Fonctions de sanitization/normalisation à mettre à jour
Ces fonctions énumèrent explicitement les champs à conserver et doivent inclure quantity :
normalizeStructureForSave()dansshared/model/componentStructure.ts— inclurequantitydans le payload backend des piècessanitizePieceDefinition()dansshared/utils/structureAssignmentHelpers.ts— préserverquantitysanitizePieces()dansshared/model/componentStructureSanitize.ts— préserverquantitydans la sortiehydratePieces()/mapComponentPieces()— préserverquantitylors 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 - Valeur par défaut : 1
- Style :
input input-bordered input-sm md:input-md(DaisyUI) - Position : à côté des champs existants (reference, role)
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)
- Si quantité = 1 : rien n'est affiché (pas de bruit visuel)
- Style du label : texte secondaire (
text-base-content/60ou classe équivalente)