feat(catalog) : categories multi-types (M:N) + bouton Filtres liste #75

Merged
malio merged 3 commits from feature/categories-multi-types-filters into develop 2026-06-08 09:47:15 +00:00
Owner

Contexte

Une Category ne pouvait appartenir qu'à un seul CategoryType (ManyToOne). Le besoin métier : plusieurs types par catégorie. Cette MR fait passer la relation en ManyToMany et ajoute le bouton « Filtres » à droite de la liste des catégories (modèle Répertoire Clients).

Slice vertical complet (le passage M:N casse mécaniquement le contrat inter-module CategoryInterface, consommé par la RG-2.10 fournisseurs).

Volet A — Relation M:N

  • Category.categoryType (ManyToOne) → categoryTypes (ManyToMany, jonction category_category_type). Au moins un type obligatoire (Assert\\Count(min:1)).
  • Unicité du nom GLOBALE parmi les actifs (uq_category_name_active, remplace uq_category_name_type_active). Message 409 reformulé.
  • Migration : table de jonction + backfill + drop colonne category_type_id + nouvel index. Validée rejouable sur base fraîche.
  • Contrat Shared : getCategoryTypeCode()getCategoryTypeCodes(): array. Supplier/SupplierAddress/SupplierFixtures revalident « contient FOURNISSEUR » (RG-2.10).
  • Provider/Repository : filtre type via sous-requête EXISTS (ne tronque pas la collection embarquée), eager-load anti-N+1.

Volet B — Bouton « Filtres »

  • Drawer recherche par nom + types multi (OR). Compteur de filtres actifs. État local, jamais persisté en URL.
  • Back : filtres ?name= et ?typeId[]= sur la collection.

Front

  • Multi-select MalioSelectCheckbox, useCategoryForm en categoryTypeIds[], colonne « Types », clés i18n.

Tests / vérifs

  • make test : 582 tests, 2474 assertions, 0 échec
  • make nuxt-test : 236 tests
  • make php-cs-fixer-allow-risky
  • Migration rejouée sur base fraîche (make db-reset)
  • Nouveau CategoryFilterTest (name partiel + typeId[] OR + multi-type non dupliqué)
## Contexte Une `Category` ne pouvait appartenir qu'à **un seul** `CategoryType` (ManyToOne). Le besoin métier : plusieurs types par catégorie. Cette MR fait passer la relation en **ManyToMany** et ajoute le bouton **« Filtres »** à droite de la liste des catégories (modèle Répertoire Clients). Slice vertical complet (le passage M:N casse mécaniquement le contrat inter-module `CategoryInterface`, consommé par la RG-2.10 fournisseurs). ## Volet A — Relation M:N - `Category.categoryType` (ManyToOne) → `categoryTypes` (ManyToMany, jonction `category_category_type`). Au moins un type obligatoire (`Assert\\Count(min:1)`). - **Unicité du nom GLOBALE** parmi les actifs (`uq_category_name_active`, remplace `uq_category_name_type_active`). Message 409 reformulé. - Migration : table de jonction + backfill + drop colonne `category_type_id` + nouvel index. Validée **rejouable sur base fraîche**. - Contrat Shared : `getCategoryTypeCode()` → `getCategoryTypeCodes(): array`. `Supplier`/`SupplierAddress`/`SupplierFixtures` revalident « contient FOURNISSEUR » (RG-2.10). - Provider/Repository : filtre type via sous-requête `EXISTS` (ne tronque pas la collection embarquée), eager-load anti-N+1. ## Volet B — Bouton « Filtres » - Drawer recherche par nom + types multi (OR). Compteur de filtres actifs. État local, jamais persisté en URL. - Back : filtres `?name=` et `?typeId[]=` sur la collection. ## Front - Multi-select `MalioSelectCheckbox`, `useCategoryForm` en `categoryTypeIds[]`, colonne « Types », clés i18n. ## Tests / vérifs - `make test` : **582 tests, 2474 assertions, 0 échec** ✅ - `make nuxt-test` : **236 tests** ✅ - `make php-cs-fixer-allow-risky` ✅ - Migration rejouée sur base fraîche (`make db-reset`) ✅ - Nouveau `CategoryFilterTest` (name partiel + typeId[] OR + multi-type non dupliqué)
matthieu added 1 commit 2026-06-08 09:26:32 +00:00
feat(catalog) : categories multi-types (M:N) + filtres liste
Pull Request — Quality gate / Backend (PHP CS + PHPUnit) (pull_request) Failing after 51s
Pull Request — Quality gate / Frontend (lint + Vitest + build) (pull_request) Successful in 1m9s
f813c1732e
Volet A — relation Category <-> CategoryType passee de ManyToOne a ManyToMany
(jonction category_category_type). Au moins un type obligatoire (Assert\Count),
unicite du nom desormais GLOBALE parmi les actifs (uq_category_name_active).
Migration avec backfill + drop de l'ancienne colonne. Contrat Shared
CategoryInterface : getCategoryTypeCode() -> getCategoryTypeCodes(): array ;
RG-2.10 fournisseurs (Supplier / SupplierAddress / fixtures) revalident
« contient FOURNISSEUR ». Provider/Repository : filtre type via sous-requete
EXISTS (sans tronquer la collection embarquee), eager-load anti-N+1.

Volet B — bouton « Filtres » sur la liste des categories (recherche nom +
types multi en OR), sur le modele du Repertoire Clients ; etat local, jamais
persiste en URL. Filtres back ?name= et ?typeId[]= sur la collection.

Front : multi-select MalioSelectCheckbox, useCategoryForm en categoryTypeIds[],
colonne « Types », i18n. ColumnCommentsCatalog + makefile test-db-setup alignes
sur le nouvel index partiel. Tests Catalog/Commercial adaptes + CategoryFilterTest.
matthieu added the backbreakingdbfrontM0-Categorietype/feat labels 2026-06-08 09:26:56 +00:00
matthieu added 1 commit 2026-06-08 09:29:37 +00:00
ci(catalog) : aligner le bootstrap test DB sur uq_category_name_active (M:N)
Pull Request — Quality gate / Backend (PHP CS + PHPUnit) (pull_request) Has been cancelled
Pull Request — Quality gate / Frontend (lint + Vitest + build) (pull_request) Has been cancelled
5376d245fb
matthieu added 1 commit 2026-06-08 09:31:02 +00:00
docs(catalog) : commentaire makefile test-db-setup sur uq_category_name_active
Pull Request — Quality gate / Backend (PHP CS + PHPUnit) (pull_request) Successful in 2m2s
Pull Request — Quality gate / Frontend (lint + Vitest + build) (pull_request) Successful in 1m11s
709d9d58f4
Author
Owner

Review — 👍 LGTM, mergeable en l'état

Slice vertical propre, complet et cohérent de bout en bout. Aucun bug bloquant. Les deux points d'infra sensibles sont correctement traités (table de jonction mirorée dans ColumnCommentsCatalog + makefile/CI réalignés sur uq_category_name_active).

Points solides

  • Migration : ordre critique commenté, backfill avant drop, COMMENT ON table + colonnes, down() best-effort honnête.
  • Repository : filtres type via sous-requête EXISTS corrélée → ne tronque pas la collection categoryTypes embarquée et évite les doublons. addSelect('cte') + Paginator(fetchJoinCollection: true) → pas de troncature SQL sur le fetch-join to-many (le piège classique, bien évité).
  • Contrat Shared : getCategoryTypeCode(): ?stringgetCategoryTypeCodes(): array propagé aux 3 consommateurs (Supplier, SupplierAddress, SupplierFixtures), RG-2.10 revalidée en in_array(..., true).
  • Tests : CategoryFilterTest couvre le cas critique (catégorie multi-types non dupliquée + types tous embarqués) ; CategoryUniqueTest correctement inversé pour l'unicité globale.
  • Front cohérent (types, categoryTypeIds[], drawer Filtres double état brouillon/appliqué, jamais persisté en URL).

Unicité globale du nom assumée comme volontaire ; sur base seedée pas de collision, donc OK.

## Review — 👍 LGTM, mergeable en l'état Slice vertical propre, complet et cohérent de bout en bout. Aucun bug bloquant. Les deux points d'infra sensibles sont correctement traités (table de jonction mirorée dans `ColumnCommentsCatalog` + makefile/CI réalignés sur `uq_category_name_active`). ### Points solides - **Migration** : ordre critique commenté, backfill avant drop, `COMMENT ON` table + colonnes, `down()` best-effort honnête. - **Repository** : filtres type via sous-requête `EXISTS` corrélée → ne tronque pas la collection `categoryTypes` embarquée et évite les doublons. `addSelect('cte')` + `Paginator(fetchJoinCollection: true)` → pas de troncature SQL sur le fetch-join to-many (le piège classique, bien évité). - **Contrat Shared** : `getCategoryTypeCode(): ?string` → `getCategoryTypeCodes(): array` propagé aux 3 consommateurs (`Supplier`, `SupplierAddress`, `SupplierFixtures`), RG-2.10 revalidée en `in_array(..., true)`. - **Tests** : `CategoryFilterTest` couvre le cas critique (catégorie multi-types non dupliquée + types tous embarqués) ; `CategoryUniqueTest` correctement inversé pour l'unicité globale. - **Front** cohérent (types, `categoryTypeIds[]`, drawer Filtres double état brouillon/appliqué, jamais persisté en URL). Unicité globale du nom assumée comme volontaire ; sur base seedée pas de collision, donc OK.
malio merged commit a9c14704b7 into develop 2026-06-08 09:47:15 +00:00
malio deleted branch feature/categories-multi-types-filters 2026-06-08 09:47:15 +00:00
Sign in to join this conversation.