feat(catalog) : ERP-200 — ProductProvider + ProductProcessor (unicité code, RG-6.03/05/06) #151
Reference in New Issue
Block a user
Delete Branch "feat/erp-200-product-provider-processor"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Stack sur ERP-199 (base =
feat/erp-199-entites-product-storagetype).ERP-200 (1.4) — Logique métier du produit (M6)
ProductProvider (lecture)
name ASC.?search(code+name, LIKE échappé),?categoryId/?categoryCode,?state(containment JSONB@>),?siteId[](EXISTS corrélé).?pagination=falserespectée.ProductProcessor (écriture POST/PATCH)
codetrim+UPPER,nametrim (ProductFieldNormalizer, miroir CarrierFieldNormalizer).manufactured/containsMolassesforcésfalsesistatesne contient pasSALE.codecôté front.catalog.products.manage(admin-only § 5.2) → 403 global porté par la security d'opération, aucun guard de champ nécessaire.Entité Product
Assert\CallbackRG-6.05 (catégorie de type PRODUIT) + RG-6.06 (types de stockage disponibles sur au moins un site choisi),->atPath()pour mapping inline 422 (useFormErrors / ERP-101).Repository
createListQueryBuilder(filtres + eager-load category/sites/storageTypes).Vérifications
make test✅ 873 tests, 6541 assertions (dontCollectionsArePaginatedTest,EntityConstraintsHaveFrenchMessageTest).make php-cs-fixer-allow-risky✅Review back — ERP-200 (ProductProvider + ProductProcessor), read-only.
Périmètre : provider liste/détail, processor création/édition, RG-6.03/05/06, normalisation.
Vérifié conforme :
ProductProviderenveloppeApiPlatform\…\Paginator(fetchJoinCollection: true),?pagination=falsegéré viapagination->isEnabled()— aucun array brut. Garde-fouCollectionsArePaginatedTestvert.category+sites+storageTypes.codeparmi les actifs → 409, exclusion du produit courant en PATCH + filet anti-race via l'index partiel. Le 409 (ConflictHttpException) est mappé front surcode(même contrat queCategoryProcessor/RG-1.07).codetrim+UPPER /nametrim appliquée avant la vérification d'unicité.#[Assert\Callback]avec->atPath()+ messages FR (consommable paruseFormErrors).Verdict : ✅ aucun retour obligatoire.
Review M6 « Produit » — relecture croisée (couche données + couche application) sur le diff cumulé
develop…HEAD.Verdict : 0 bloquant, 0 important.
Assert\*en FR.priority:1+#[IsGranted('catalog.products.view')], mêmes filtres que la liste — conforme.Nits relevés (non bloquants) :
product.states:DEFAULT '[]'::jsonbcontredisait leCHECK (jsonb_array_length(states) >= 1)(default mort, jamais atteignable) → corrigé (commit30e7839).ProductExportControllerTest: letearDownréutilisait la constante des category-types (TEST_CATEGORY_TYPE_PREFIX) pour purger des storage-types → constante dédiéeTEST_STORAGE_PREFIX(commit30e7839).Site(cross-module) du controller utilisé seulement dans un commentaire@var; DQLp.id != :idau lieu de<>: cosmétiques, laissés tels quels.?pagination=falserenvoyant unarray: conforme (pattern établiClientProvider/SupplierProvider, échappatoire documentée pour alimenter les selects).Consolidée dans #154 : la pile M6 a été reciblée sur
developen une seule MR pour un CI unique. Commits intégralement inclus dans #154 — fermée sans merge individuel.Pull request closed