refactor : simplification globale (vague 1 + 2) + fix visibilité ActorProfileResolver #2

Merged
matthieu merged 6 commits from refactor/simplification-globale into develop 2026-05-06 14:54:54 +00:00
Owner

Résumé

Refactor de simplification du code Inventory (vagues 1 + 2) consolidé avec un fix de régression et des tests de couverture supplémentaires.

Backend — Extractions et déduplication

  • ActorProfileResolver (nouveau service) : unifie 3 implémentations dupliquées et divergentes du résolveur d'acteur (session → fallback Security::getUser), réutilisé par AbstractAuditSubscriber, EntityVersionService et ModelTypeCategoryConversionService.
  • Bug latent corrigé : EntityVersionService::resolveActorProfileId() n'avait pas le fallback Security::getUser, loggant actor=null hors contexte session sur les restaurations de version.
  • MachineStructureController : intégration du clonage des contextFieldValues directement dans cloneComponentLinks / clonePieceLinks ; suppression de cloneContextFieldValues() et de son find() en boucle.
  • Helpers extraits : serializeProductSlots() (EntityVersionService), updateModelTypeCategory() (ModelTypeCategoryConversionService).
  • AbstractAuditSubscriber : suppression de collectCollectionUpdate() (no-op) et de ses appels.

Frontend — Nettoyage

  • useMachineDetailData.ts : suppression d'un debug ref orphelin, des componentTypeLabelMap / pieceTypeLabelMap non consommés en aval, et d'une double assignation productLinks.
  • PieceItem.vue : suppression d'inits pieceData redondants dans onMounted (déjà couverts par reactive() initial + watch sur les props).

Fix de régression (commit dédié)

AbstractAuditSubscriber déclarait $actorProfileResolver en private readonly via promoted property. MachineAuditSubscriber surcharge onFlush() et y accède — private n'est pas hérité, donc PHP voyait nullCall to a member function resolve() on null sur chaque flush touchant des link entities. Le passage à protected suit la convention déjà en place dans la classe (autres méthodes partagées parent/sous-classe sont protected).

Tests ajoutés

  • testCloneMachineCopiesPieceContextFieldValues — symétrique au test composant existant, comble le gap de couverture côté pièce.
  • testCloneMachineLeavesSourceContextFieldValuesIntact — vérifie l'invariant que la machine source garde ses CFV context après clone.

Vérification

  • make test345/345 OK, 864 assertions, 1 deprecation pré-existante non liée.
  • make php-cs-fixer-allow-risky → clean.

Test plan

  • Vérifier en local que les flush Doctrine touchant des link entities (création/suppression de MachineComponentLink, MachinePieceLink, MachineProductLink) ne lèvent plus l'erreur sur ActorProfileResolver::resolve().
  • Test manuel UI : ouvrir une page machine dont une pièce a un productId pré-sélectionné mais sans product chargé en cache, vérifier que le nom du produit s'affiche dès le rendu initial. Si le produit ne s'affiche qu'après une interaction, c'est une régression du retrait de ensureProductLoaded(pieceData.productId) dans onMounted de PieceItem.vue — il faudra restaurer cette ligne.
  • Tester un clone de machine avec composants ET pièces ayant des context custom field values pour vérifier que toutes les CFV sont bien dupliquées et que la source reste intacte.
  • Tester une restauration de version d'entité (machine, composant, pièce, produit) en vérifiant que l'AuditLog créé porte bien un actorProfileId non null.
## Résumé Refactor de simplification du code Inventory (vagues 1 + 2) consolidé avec un fix de régression et des tests de couverture supplémentaires. ### Backend — Extractions et déduplication - **`ActorProfileResolver`** (nouveau service) : unifie 3 implémentations dupliquées et divergentes du résolveur d'acteur (session → fallback `Security::getUser`), réutilisé par `AbstractAuditSubscriber`, `EntityVersionService` et `ModelTypeCategoryConversionService`. - **Bug latent corrigé** : `EntityVersionService::resolveActorProfileId()` n'avait pas le fallback `Security::getUser`, loggant `actor=null` hors contexte session sur les restaurations de version. - **`MachineStructureController`** : intégration du clonage des `contextFieldValues` directement dans `cloneComponentLinks` / `clonePieceLinks` ; suppression de `cloneContextFieldValues()` et de son `find()` en boucle. - **Helpers extraits** : `serializeProductSlots()` (`EntityVersionService`), `updateModelTypeCategory()` (`ModelTypeCategoryConversionService`). - **`AbstractAuditSubscriber`** : suppression de `collectCollectionUpdate()` (no-op) et de ses appels. ### Frontend — Nettoyage - **`useMachineDetailData.ts`** : suppression d'un `debug` ref orphelin, des `componentTypeLabelMap` / `pieceTypeLabelMap` non consommés en aval, et d'une double assignation `productLinks`. - **`PieceItem.vue`** : suppression d'inits `pieceData` redondants dans `onMounted` (déjà couverts par `reactive()` initial + watch sur les props). ### Fix de régression (commit dédié) `AbstractAuditSubscriber` déclarait `$actorProfileResolver` en `private readonly` via promoted property. `MachineAuditSubscriber` surcharge `onFlush()` et y accède — `private` n'est pas hérité, donc PHP voyait `null` → `Call to a member function resolve() on null` sur chaque flush touchant des link entities. Le passage à `protected` suit la convention déjà en place dans la classe (autres méthodes partagées parent/sous-classe sont `protected`). ### Tests ajoutés - `testCloneMachineCopiesPieceContextFieldValues` — symétrique au test composant existant, comble le gap de couverture côté pièce. - `testCloneMachineLeavesSourceContextFieldValuesIntact` — vérifie l'invariant que la machine source garde ses CFV context après clone. ## Vérification - `make test` → **345/345 OK**, 864 assertions, 1 deprecation pré-existante non liée. - `make php-cs-fixer-allow-risky` → clean. ## Test plan - [ ] Vérifier en local que les flush Doctrine touchant des link entities (création/suppression de `MachineComponentLink`, `MachinePieceLink`, `MachineProductLink`) ne lèvent plus l'erreur sur `ActorProfileResolver::resolve()`. - [ ] **Test manuel UI** : ouvrir une page machine dont une pièce a un `productId` pré-sélectionné mais sans `product` chargé en cache, vérifier que le nom du produit s'affiche dès le rendu initial. Si le produit ne s'affiche qu'après une interaction, c'est une régression du retrait de `ensureProductLoaded(pieceData.productId)` dans `onMounted` de `PieceItem.vue` — il faudra restaurer cette ligne. - [ ] Tester un clone de machine avec composants ET pièces ayant des context custom field values pour vérifier que toutes les CFV sont bien dupliquées et que la source reste intacte. - [ ] Tester une restauration de version d'entité (machine, composant, pièce, produit) en vérifiant que l'`AuditLog` créé porte bien un `actorProfileId` non null.
matthieu added 3 commits 2026-05-06 13:42:27 +00:00
- ActorProfileResolver : service unique partage par AbstractAuditSubscriber, EntityVersionService et ModelTypeCategoryConversionService (3 implementations dupliquees+divergentes)
- corrige un bug latent : EntityVersionService restoraitsans le fallback Security::getUser, loggant actor=null hors session
- machine-clone : clonage des contextFieldValues integre dans cloneComponentLinks/clonePieceLinks, supprime cloneContextFieldValues et son find() en boucle
- helpers extraits : serializeProductSlots (EntityVersionService), updateModelTypeCategory (ModelTypeCategoryConversionService)
- supprime collectCollectionUpdate() vide + ses appels (AbstractAuditSubscriber)
- useMachineDetailData : retire debug ref couplee a isEditMode, componentTypeLabelMap/pieceTypeLabelMap jamais consommes, double assignation machine.productLinks
- PieceItem : retire l'init pieceData dans onMounted (deja couvert par reactive() et le watcher)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
AbstractAuditSubscriber déclarait $actorProfileResolver en private readonly
via promoted property. MachineAuditSubscriber surcharge onFlush() et accède
à $this->actorProfileResolver, mais private n'est pas hérité — PHP voyait
null et levait "Call to a member function resolve() on null" sur chaque
flush Doctrine touchant des link entities.

Le passage à protected suit la convention déjà en place dans la classe
(safeGet, normalizeValue, persistAuditLog, etc. sont protected). readonly
préserve l'immutabilité de la dépendance DI.

Ajoute aussi deux tests de régression pour le clone des contextFieldValues
(symétrique au test composant existant) et nettoie deux lignes vides
cosmétiques laissées par le refactor précédent.

- testCloneMachineCopiesPieceContextFieldValues : vérifie que les CFV
  context d'un MachinePieceLink sont bien rattachées au nouveau lien
  après clone.
- testCloneMachineLeavesSourceContextFieldValuesIntact : vérifie que la
  machine source garde ses CFV context après clone (invariant implicite).
matthieu added 3 commits 2026-05-06 14:51:16 +00:00
Les entités Doctrine déclaraient déjà onDelete: CASCADE pour ces deux
relations, mais les contraintes correspondantes étaient absentes en base.
Conséquence : la suppression d'un composant pouvait laisser des documents
ou des links machine orphelins. La migration nettoie les orphelins
existants (avec trace dans audit_logs) puis ajoute les deux FK.
Remplace l'exemple "pompe avec position sur la machine" par un palier
de tete vs palier de pied : exemple plus concret et plus universellement
compris pour illustrer la difference entre champs catalogue et champs
contextuels (custom field values).
Les toasts d'erreur etaient persistants (duration force a 0) et restaient
affiches jusqu'a fermeture manuelle, ce qui pouvait empiler des messages
obsoletes a l'ecran. Aligne le comportement sur les autres types : duree
par defaut 8s (plus que warning a 6s pour laisser le temps de lire). Une
erreur critique peut toujours etre rendue persistante en passant
explicitement showError(msg, 0).
matthieu merged commit e6ba2259cb into develop 2026-05-06 14:54:54 +00:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: MALIO-DEV/Inventory#2