feat(catalog) : categories multi-types (M:N) + filtres liste
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.
This commit is contained in:
@@ -55,6 +55,17 @@ final class SupplierValidationTest extends TestCase
|
||||
self::assertContains('categories', $this->violationPaths($supplier));
|
||||
}
|
||||
|
||||
public function testMultiTypeCategoryContainingFournisseurIsAccepted(): void
|
||||
{
|
||||
// RG-2.10 sous ManyToMany : une categorie qui PORTE FOURNISSEUR (parmi
|
||||
// d'autres types) reste autorisee sur un fournisseur.
|
||||
$supplier = new Supplier();
|
||||
$supplier->setCompanyName('Recycla SAS');
|
||||
$supplier->addCategory($this->category('CLIENT', 'FOURNISSEUR'));
|
||||
|
||||
self::assertNotContains('categories', $this->violationPaths($supplier));
|
||||
}
|
||||
|
||||
// === RG-2.07 : Virement impose une banque ===
|
||||
|
||||
public function testVirementWithoutBankIsRejectedOnBankPath(): void
|
||||
@@ -131,13 +142,17 @@ final class SupplierValidationTest extends TestCase
|
||||
}
|
||||
|
||||
/**
|
||||
* Double minimal de CategoryInterface (pas d'acces base) renvoyant le code de
|
||||
* type de categorie voulu — seul element regarde par validateCategoryType.
|
||||
* Double minimal de CategoryInterface (pas d'acces base) PORTANT les codes de
|
||||
* type voulus — seul element regarde par validateCategoryType. Variadic pour
|
||||
* couvrir le cas multi-types (ManyToMany).
|
||||
*
|
||||
* @return list<string> n'est pas le type de retour : helper renvoyant un double
|
||||
*/
|
||||
private function category(string $typeCode): CategoryInterface
|
||||
private function category(string ...$typeCodes): CategoryInterface
|
||||
{
|
||||
return new class($typeCode) implements CategoryInterface {
|
||||
public function __construct(private readonly string $typeCode) {}
|
||||
return new class(array_values($typeCodes)) implements CategoryInterface {
|
||||
/** @param list<string> $typeCodes */
|
||||
public function __construct(private readonly array $typeCodes) {}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
@@ -154,9 +169,10 @@ final class SupplierValidationTest extends TestCase
|
||||
return 'TEST';
|
||||
}
|
||||
|
||||
public function getCategoryTypeCode(): ?string
|
||||
/** @return list<string> */
|
||||
public function getCategoryTypeCodes(): array
|
||||
{
|
||||
return $this->typeCode;
|
||||
return $this->typeCodes;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user