feat(catalog) : M6 — Catalogue produits (ERP-197 → ERP-203) #154

Merged
matthieu merged 9 commits from feat/erp-203-tests-product-rg into develop 2026-06-25 12:50:16 +00:00
Owner

Module M6 — Catalogue produits (ERP-197 → ERP-203), pile consolidée en une seule MR vers develop pour un CI unique.

Contenu (commits) :

  • ERP-197 — permissions catalog.products.* + sidebar + 3 miroirs RBAC
  • ERP-198 — migration schéma M6 (storage_type, product, jonctions, type PRODUIT)
  • ERP-199 — entités Product + StorageType + repositories + contrat de sérialisation
  • ERP-200 — ProductProvider + ProductProcessor (unicité code, RG-6.03/05/06, normalisation)
  • ERP-201 — référentiel StorageType exposé (filtre site) + seed Figma + catégories PRODUIT
  • ERP-202 — export XLSX du catalogue produits (filtres liste)
  • ERP-203 — tests PHPUnit RG-6.01→6.10 + capture du contrat JSON produit
  • fix review M6 — default jsonb mort (states) + constante préfixe storage-type de test

Remplace et clôt les MR #148, #149, #150, #151, #152, #153 (commits intégralement inclus ici).

Module **M6 — Catalogue produits** (ERP-197 → ERP-203), pile consolidée en une seule MR vers `develop` pour un CI unique. Contenu (commits) : - ERP-197 — permissions `catalog.products.*` + sidebar + 3 miroirs RBAC - ERP-198 — migration schéma M6 (storage_type, product, jonctions, type PRODUIT) - ERP-199 — entités Product + StorageType + repositories + contrat de sérialisation - ERP-200 — ProductProvider + ProductProcessor (unicité code, RG-6.03/05/06, normalisation) - ERP-201 — référentiel StorageType exposé (filtre site) + seed Figma + catégories PRODUIT - ERP-202 — export XLSX du catalogue produits (filtres liste) - ERP-203 — tests PHPUnit RG-6.01→6.10 + capture du contrat JSON produit - fix review M6 — default jsonb mort (states) + constante préfixe storage-type de test Remplace et clôt les MR #148, #149, #150, #151, #152, #153 (commits intégralement inclus ici).
matthieu added the backM6-Produittype/feat labels 2026-06-25 10:55:20 +00:00
Author
Owner

Review back — ERP-203 (tests PHPUnit RG + contrat JSON), read-only.

Périmètre : 17 tests couvrant RG-6.01→6.06 + RBAC + contrat de sérialisation, spec-back.md.

Vérifié conforme :

  • RG-6.01 (409 doublon + réutilisation d'un code soft-deleted), RG-6.02 (states ≥ 1), RG-6.03 (conditionnels forcés false hors SALE), RG-6.05 (catégorie PRODUIT → 422), RG-6.06 (storageTypes ⊆ sites → 422), matrice RBAC (Admin OK / autres 403).
  • ProductSerializationContractTest asserte sur le corps JSON réel (member/totalItems sans préfixe hydra:, category objet embarqué) — DoD § 4.0.bis.
  • Getter booléen renommé isContainsMolasses() (convention serializer) + correctif helper d'auth (authView).
  • Exécution isolée (BDD fraîche) : OK (116 tests, 459 assertions) sur tout le module Catalog.

⚠️ Note hors périmètre M6 : la suite globale make test présente une instabilité pré-existante (violations FK provider_category non déterministes sur des modules antérieurs M2-M5, sans isolation transactionnelle). Reproduite sans aucun code Catalog (module Technique seul, BDD fraîche : 6 puis 5 failures sur deux exécutions identiques). À traiter séparément — piste : DAMADoctrineTestBundle pour isoler chaque test dans une transaction rollback.

Verdict : MR finie, tests verts en isolation.

**Review back — ERP-203 (tests PHPUnit RG + contrat JSON), read-only.** Périmètre : 17 tests couvrant RG-6.01→6.06 + RBAC + contrat de sérialisation, spec-back.md. Vérifié conforme : - RG-6.01 (409 doublon + réutilisation d'un code soft-deleted), RG-6.02 (states ≥ 1), RG-6.03 (conditionnels forcés `false` hors SALE), RG-6.05 (catégorie PRODUIT → 422), RG-6.06 (storageTypes ⊆ sites → 422), matrice RBAC (Admin OK / autres 403). - `ProductSerializationContractTest` asserte sur le **corps JSON réel** (`member`/`totalItems` sans préfixe `hydra:`, `category` objet embarqué) — DoD § 4.0.bis. - Getter booléen renommé `isContainsMolasses()` (convention serializer) + correctif helper d'auth (`authView`). - **Exécution isolée (BDD fraîche) : `OK (116 tests, 459 assertions)`** sur tout le module Catalog. ⚠️ Note hors périmètre M6 : la suite **globale** `make test` présente une instabilité **pré-existante** (violations FK `provider_category` non déterministes sur des modules antérieurs M2-M5, sans isolation transactionnelle). Reproduite **sans aucun code Catalog** (module Technique seul, BDD fraîche : 6 puis 5 failures sur deux exécutions identiques). À traiter séparément — piste : `DAMADoctrineTestBundle` pour isoler chaque test dans une transaction rollback. Verdict : ✅ MR finie, tests verts en isolation.
Author
Owner

Review M6 « Produit » — relecture croisée (couche données + couche application) sur le diff cumulé develop…HEAD.

Verdict : 0 bloquant, 0 important.

  • Garde-fous archi verts : COMMENT ON COLUMN, Timestampable/Blamable, Auditable + i18n, pagination (règle absolue n°13), contraintes Assert\* en FR.
  • RBAC 3 miroirs alignés (sidebar / personas / SeedE2ECommand).
  • Export : priority:1 + #[IsGranted('catalog.products.view')], mêmes filtres que la liste — conforme.
  • 119 tests Catalog + garde-fous archi verts en isolation (BDD fraîche).

Nits relevés (non bloquants) :

  • product.states : DEFAULT '[]'::jsonb contredisait le CHECK (jsonb_array_length(states) >= 1) (default mort, jamais atteignable) → corrigé (commit 30e7839).
  • ProductExportControllerTest : le tearDown réutilisait la constante des category-types (TEST_CATEGORY_TYPE_PREFIX) pour purger des storage-types → constante dédiée TEST_STORAGE_PREFIX (commit 30e7839).
  • Import Site (cross-module) du controller utilisé seulement dans un commentaire @var ; DQL p.id != :id au lieu de <> : cosmétiques, laissés tels quels.
  • Provider ?pagination=false renvoyant un array : conforme (pattern établi ClientProvider/SupplierProvider, échappatoire documentée pour alimenter les selects).
**Review M6 « Produit »** — relecture croisée (couche données + couche application) sur le diff cumulé `develop…HEAD`. **Verdict : 0 bloquant, 0 important.** - Garde-fous archi verts : COMMENT ON COLUMN, Timestampable/Blamable, Auditable + i18n, pagination (règle absolue n°13), contraintes `Assert\*` en FR. - RBAC 3 miroirs alignés (sidebar / personas / SeedE2ECommand). - Export : `priority:1` + `#[IsGranted('catalog.products.view')]`, mêmes filtres que la liste — conforme. - 119 tests Catalog + garde-fous archi verts en isolation (BDD fraîche). **Nits relevés (non bloquants) :** - `product.states` : `DEFAULT '[]'::jsonb` contredisait le `CHECK (jsonb_array_length(states) >= 1)` (default mort, jamais atteignable) → corrigé (commit `30e7839`). - `ProductExportControllerTest` : le `tearDown` réutilisait la constante des category-types (`TEST_CATEGORY_TYPE_PREFIX`) pour purger des storage-types → constante dédiée `TEST_STORAGE_PREFIX` (commit `30e7839`). - Import `Site` (cross-module) du controller utilisé seulement dans un commentaire `@var` ; DQL `p.id != :id` au lieu de `<>` : cosmétiques, laissés tels quels. - Provider `?pagination=false` renvoyant un `array` : conforme (pattern établi `ClientProvider`/`SupplierProvider`, échappatoire documentée pour alimenter les selects).
matthieu changed target branch from feat/erp-202-product-export-xlsx to develop 2026-06-25 12:35:07 +00:00
matthieu added 8 commits 2026-06-25 12:35:07 +00:00
feat(catalog) : ERP-197 — permissions catalog.products.* + item sidebar + 3 miroirs RBAC
Pull Request — Quality gate / Frontend (lint + Vitest + build) (pull_request) Successful in 1m53s
Pull Request — Quality gate / Backend (PHP CS + PHPUnit) (pull_request) Successful in 3m31s
e9f8b0bc45
- CatalogModule::permissions() : ajout catalog.products.view / .manage (admin-only, C7)
- config/sidebar.php : item « Catalogue produit » (/admin/products) sous « Repertoire transporteurs » (section Administration)
- personas.ts + SeedE2ECommand.php : persona admin gagne view/manage + lien products (3 miroirs RBAC alignes)
- i18n : cle sidebar.catalog.products
Entité Product (#[Auditable], TimestampableBlamable, soft-delete préparé non
exposé) et référentiel StorageType (lecture seule, provisoire) dans le module
Catalog, avec le contrat de sérialisation posé une fois (read-groups par
propriété affichée — RETEX M1→M5, 3 maillons spec § 4.0).

- Product : code (unique global RG-6.01), name, states (json multi-select
  PURCHASE/SALE/OTHER ≥1, RG-6.02), manufactured/containsMolasses (RG-6.03),
  category ManyToOne (PRODUIT, RG-6.05), sites + storageTypes ManyToMany (≥1).
  Messages FR sur toutes les contraintes, Length calée colonnes. Opérations
  Get/GetCollection (.view) + Post/Patch (.manage), pas de Delete. Provider/
  Processor référencés (implémentés en ERP-200).
- StorageType : code/label + sites ManyToMany (filtrage par site, ERP-201).
  Référentiel statique → whitelist EntitiesAreTimestampableBlamableTest.
- Repositories Product/StorageType (interfaces Domain + impl Doctrine).
- Validation états via Assert\Choice(multiple) plutôt qu'Assert\All (seul
  Choice est géré par EntityConstraintsHaveFrenchMessageTest).
- Garde-fous schema:update : 5 tables M6 ajoutées à ColumnCommentsCatalog,
  index partiel uq_product_code_active rejoué dans makefile test-db-setup.
- i18n audit.entity.catalog_product.
Provider de lecture (liste paginée Hydra filtrée + item) :
- exclut les produits soft-deleted (RG-6.09), tri name ASC ;
- filtres ?search (code+name), ?categoryId/?categoryCode, ?state (JSONB @>), ?siteId[] (EXISTS) ;
- Get item : 404 sur soft-deleted (non exposé au M6, § 2.7) ;
- pagination obligatoire via Paginator ORM (règle n°13), échappatoire ?pagination=false.

Processor d'écriture (POST/PATCH) :
- normalisation serveur code trim+UPPER, name trim (RG-6.07, ProductFieldNormalizer) ;
- RG-6.03 : manufactured/containsMolasses forcés false si states sans SALE ;
- RG-6.01 : unicité globale du code parmi les actifs -> 409 (pré-check + filet anti-race index partiel), propertyPath code côté front.

Entité Product : Assert\Callback RG-6.05 (catégorie de type PRODUIT) et RG-6.06
(types de stockage disponibles sur au moins un site choisi), atPath pour mapping
inline 422 ; constantes d'états.

Repository : createListQueryBuilder (filtres + eager-load category/sites/storageTypes)
+ existsActiveByCode déjà en place.

make test vert (873 tests), php-cs-fixer OK.
Endpoint StorageType (lecture seule) :
- StorageTypeProvider : tri label ASC, filtre ?siteId[]= (EXISTS corrélé, RG-6.06),
  pagination Hydra + échappatoire ?pagination=false (référentiel borné) ;
- createListQueryBuilder ajouté au repository (interface + impl) ;
- provider câblé sur GetCollection + Get de l'entité StorageType.

Seed (fixtures idempotentes par lookup code, miroir CategoryTypeFixtures) :
- StorageTypeFixtures : 10 types Figma (PROVISOIRE HP-M6-02), rattachés aux 3 sites
  (86/17/82) via le contrat Shared SiteProviderInterface (pas d'import inter-module) ;
- CategoryTypeFixtures : ajout du type PRODUIT (réaligne dev/test sur le seed migration ERP-198) ;
- CategoryFixtures : 4 catégories PRODUIT de démo (Céréales, Oléagineux, Aliments du bétail, Engrais).

Fix dette ERP-198/199 : mapping ORM de product.states aligné sur la colonne JSONB
de la migration (options jsonb=true). Sans ça, schema:update tentait ALTER states
TYPE JSON et cassait le CHECK jsonb_array_length -> make db-reset / test-db-setup en échec.

make db-reset OK (fixtures idempotentes, données vérifiées), make test vert (873), php-cs-fixer OK.
Couvre les RG produit (M6) en tests fonctionnels API et capture le contrat de
serialisation reel (DoD spec-back § 4.0.bis).

Tests (tests/Module/Catalog/Api/) :
- AbstractProductApiTestCase : base commune (productType find-or-create PRODUIT,
  productCategory, seedStorageType par sites, validProductPayload, dump DoD).
- ProductSerializationContractTest : POST reel + GET liste/detail ; category en
  objet embarque, sites/storageTypes en tableaux d'objets, states en tableau,
  manufactured/containsMolasses booleens presents. Dump regenerable via
  PRODUCT_DOD_DUMP=1.
- ProductCodeUniquenessTest (RG-6.01) : 409 doublon actif, collision sur forme
  normalisee, reutilisation d'un code soft-deleted (index partiel).
- ProductStatesValidationTest (RG-6.02) : >=1 etat requis, valeur hors enum 422.
- ProductConditionalFieldsTest (RG-6.03) : manufactured/containsMolasses forces
  false sans SALE (POST et PATCH), conserves avec SALE.
- ProductCategoryTypeTest (RG-6.05) : 422 si categorie non-PRODUIT.
- ProductStorageTypeBySiteTest (RG-6.06) : 422 si storageType hors sites choisis.
- ProductRBACMatrixTest : Admin 200/201 ; Bureau/Compta/Commerciale/Usine 403
  (view + manage) ; view lit mais ne gere pas.

Fix contrat de serialisation : le getter containsMolasses() ne respectait pas la
convention d'accesseur (get/is/has) -> le serialiseur n'exposait PAS le champ
dans le JSON, alors que le DoD l'exige. Renomme en isContainsMolasses() (+ unique
appelant dans ProductExportController). Defaut capte par le test de contrat.

Spec : JSON reel colle dans spec-back § 4.0.bis (remplace l'esquisse). Le contrat
reel est plus riche : la liste porte deja sites/storageTypes embarques, category
embarque categoryTypes + audit, createdBy/updatedBy en IRI, sites avec adresse.

make test vert (897 tests) ; php-cs-fixer conforme.
fix(catalog) : retours review M6 — default jsonb mort (states) + constante prefixe storage-type de test
Pull Request — Quality gate / Frontend (lint + Vitest + build) (pull_request) Successful in 49s
Pull Request — Quality gate / Backend (PHP CS + PHPUnit) (pull_request) Successful in 2m23s
30e7839615
matthieu changed title from test(catalog) : ERP-203 — tests PHPUnit RG-6.01→6.10 + capture du contrat JSON produit to feat(catalog) : M6 — Catalogue produits (ERP-197 → ERP-203) 2026-06-25 12:35:54 +00:00
matthieu closed this pull request 2026-06-25 12:37:21 +00:00
matthieu reopened this pull request 2026-06-25 12:37:23 +00:00
malio added 1 commit 2026-06-25 12:43:24 +00:00
Merge branch 'develop' into feat/erp-203-tests-product-rg
Pull Request — Quality gate / Frontend (lint + Vitest + build) (pull_request) Successful in 1m50s
Pull Request — Quality gate / Backend (PHP CS + PHPUnit) (pull_request) Successful in 5m33s
1ee48555bd
matthieu merged commit 4207a4ae12 into develop 2026-06-25 12:50:16 +00:00
matthieu deleted branch feat/erp-203-tests-product-rg 2026-06-25 12:50:17 +00:00
Sign in to join this conversation.