Files
Inventory/docs/superpowers/specs/2026-03-12-piece-quantity-design.md
Matthieu b8edf1ea95 docs : update piece quantity spec after review
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>
2026-03-12 11:48:01 +01:00

5.3 KiB
Raw Blame History

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.pieces du Composant)
  • 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

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) : quantity depuis MachinePieceLink.quantity
  • Pièce sous composant : quantity depuis le structure.pieces du composant source. Résolution :
    1. Naviguer MachinePieceLinkparentLink (MachineComponentLink) → composantstructure['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 :

{
  "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 :

$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 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() 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
  • 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/60 ou classe équivalente)