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.
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.
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.