[ERP-78] Refonte taxonomie Catégories : type unique CLIENT + Category.code + RG-1.03/1.29 par code (#42)
Auto Tag Develop / tag (push) Successful in 8s
Auto Tag Develop / tag (push) Successful in 8s
Refonte de la taxonomie Catégories (décision produit 01/06) : le modèle est inversé. ## Modèle - **UN SEUL `category_type` : CLIENT**. `Distributeur` / `Courtier` / `Secteur` / `Autre` (+ catégories métier) deviennent des `Category` rattachées à CLIENT. - Filtrage métier sur un **`code` stable porté par `Category`** (NOT NULL, unique partiel `uq_category_code`), slug MAJUSCULE auto-généré du nom (`CategoryCodeGenerator`), figé à la création, exposé en **lecture seule**. ## Contenu - **M0** : `Category.code` (entité + migration corrective `Version20260602100000` au namespace racine + `COMMENT ON COLUMN` + catalogue + ligne `test-db-setup`). Retrofit `Version20260528120000` rendu conscient des colonnes. - **Seed** : type unique CLIENT, catégories codées (`Distributeur→DISTRIBUTEUR`, etc.), anciens types supprimés. Fixtures `CategoryType`/`Category`/`Client` alignées. - **RG-1.03** : `ClientProcessor::hasCategoryCode` — un distributor/broker doit porter la `Category` de code `DISTRIBUTEUR`/`COURTIER`. Filtre liste/export `categoryType` → `categoryCode`. - **RG-1.29** : `ClientAddress::validateCategoryCodes` — denylist des codes `DISTRIBUTEUR`/`COURTIER` sur une adresse (toute autre catégorie autorisée). - **Specs** M0/M1 (back + front) amendées. ## Tests `make php-cs-fixer-allow-risky` OK ; `make db-reset` OK (type unique CLIENT + 11 catégories codées, idempotent) ; `make test` **443 vert**. Ajouts : RG-1.03 courtier, génération/unicité/lecture-seule du code (`CategoryCodeTest`). ## Coordination - #76 (#500) : RG-1.29 réécrite ici sur le nouveau modèle ; #76 ne garde que le gap 2 (mapping CHECK adresse → 422), indépendant de la taxonomie. - ERP-68 (#486) : fixtures démo (déjà mergées via #41) adaptées ici au type unique CLIENT + codes. - Front #480–483 : selects Catégorie / distributeur / courtier basés sur le `code` (`?categoryCode=`), plus le type. Décisions actées avec le PO : `code` NOT NULL auto-généré (slug) ; périmètre complet (réécriture RG + fixtures déjà mergées). --------- Co-authored-by: Matthieu <contact@malio.fr> Reviewed-on: #42 Co-authored-by: THOLOT DECHENE Matthieu <matthieu@yuno.malio.fr> Co-committed-by: THOLOT DECHENE Matthieu <matthieu@yuno.malio.fr>
This commit was merged in pull request #42.
This commit is contained in:
@@ -39,7 +39,7 @@ use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
* - sites : SiteInterface (module Sites) via resolve_target_entities
|
||||
* - contacts : ClientContact (meme module)
|
||||
* - categories : CategoryInterface (module Catalog) via resolve_target_entities
|
||||
* — limitees aux types SECTEUR/AUTRE (RG-1.29, validateCategoryTypes, ERP-76)
|
||||
* — codes DISTRIBUTEUR/COURTIER interdits (RG-1.29, validateCategoryCodes, ERP-78)
|
||||
*
|
||||
* Audite (#[Auditable]) + Timestampable/Blamable.
|
||||
*
|
||||
@@ -87,8 +87,12 @@ class ClientAddress implements TimestampableInterface, BlamableInterface
|
||||
{
|
||||
use TimestampableBlamableTrait;
|
||||
|
||||
/** RG-1.29 : seuls ces types de categorie qualifient une adresse physique. */
|
||||
private const array ALLOWED_CATEGORY_TYPES = ['SECTEUR', 'AUTRE'];
|
||||
/**
|
||||
* RG-1.29 (ERP-78) : ces codes de categorie decrivent une relation entre
|
||||
* clients (distributeur / courtier) et n'ont pas de sens sur une adresse.
|
||||
* Toute autre categorie du type CLIENT est autorisee.
|
||||
*/
|
||||
private const array FORBIDDEN_CATEGORY_CODES = ['DISTRIBUTEUR', 'COURTIER'];
|
||||
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
@@ -165,7 +169,7 @@ class ClientAddress implements TimestampableInterface, BlamableInterface
|
||||
#[Groups(['client_address:read', 'client_address:write'])]
|
||||
private Collection $contacts;
|
||||
|
||||
// RG-1.29 : categories de type SECTEUR/AUTRE uniquement (validateCategoryTypes).
|
||||
// RG-1.29 : categories de code DISTRIBUTEUR/COURTIER interdites (validateCategoryCodes).
|
||||
/** @var Collection<int, CategoryInterface> */
|
||||
#[ORM\ManyToMany(targetEntity: CategoryInterface::class)]
|
||||
#[ORM\JoinTable(name: 'client_address_category')]
|
||||
@@ -232,18 +236,19 @@ class ClientAddress implements TimestampableInterface, BlamableInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* RG-1.29 : seules les categories de type SECTEUR / AUTRE qualifient une
|
||||
* adresse physique. Les types DISTRIBUTEUR / COURTIER decrivent une relation
|
||||
* entre clients (RG-1.03) et n'ont pas de sens sur une adresse -> 422 avec
|
||||
* violation sur le champ `categories`. S'appuie sur
|
||||
* CategoryInterface::getCategoryTypeCode() (pas d'import du module Catalog).
|
||||
* RG-1.29 (ERP-78) : une adresse interdit les categories de code
|
||||
* DISTRIBUTEUR / COURTIER — elles decrivent une relation entre clients
|
||||
* (RG-1.03) et n'ont pas de sens sur une adresse physique -> 422 avec
|
||||
* violation sur le champ `categories`. Toute autre categorie (type unique
|
||||
* CLIENT) est acceptee. S'appuie sur CategoryInterface::getCode() (pas
|
||||
* d'import du module Catalog — regle ABSOLUE n°1).
|
||||
*/
|
||||
#[Assert\Callback]
|
||||
public function validateCategoryTypes(ExecutionContextInterface $context): void
|
||||
public function validateCategoryCodes(ExecutionContextInterface $context): void
|
||||
{
|
||||
foreach ($this->categories as $category) {
|
||||
if ($category instanceof CategoryInterface
|
||||
&& !in_array($category->getCategoryTypeCode(), self::ALLOWED_CATEGORY_TYPES, true)) {
|
||||
&& in_array($category->getCode(), self::FORBIDDEN_CATEGORY_CODES, true)) {
|
||||
$context->buildViolation('Type de catégorie non autorisé sur une adresse.')
|
||||
->atPath('categories')
|
||||
->addViolation()
|
||||
|
||||
@@ -20,8 +20,9 @@ interface ClientRepositoryInterface
|
||||
* - Tri par defaut : companyName ASC (RG-1.26).
|
||||
* - $search : recherche fuzzy insensible a la casse sur companyName +
|
||||
* lastName + email (metacaracteres LIKE echappes). Ignore si null/vide.
|
||||
* - $categoryType : restreint aux clients possedant au moins une categorie
|
||||
* du type donne (code). Ignore si null/vide.
|
||||
* - $categoryCode : restreint aux clients possedant au moins une categorie
|
||||
* du code donne (ERP-78 : filtrage par code de Category, plus par type).
|
||||
* Ignore si null/vide.
|
||||
*
|
||||
* Filtrage centralise ICI (et non dans les providers/controllers) pour que
|
||||
* la liste paginee (ClientProvider) et l'export (ClientExportController)
|
||||
@@ -30,6 +31,6 @@ interface ClientRepositoryInterface
|
||||
public function createListQueryBuilder(
|
||||
bool $includeArchived = false,
|
||||
?string $search = null,
|
||||
?string $categoryType = null,
|
||||
?string $categoryCode = null,
|
||||
): QueryBuilder;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user