feat(commercial) : catégories de type Adresse pour les blocs adresse (client + fournisseur) (#147)
Auto Tag Develop / tag (push) Successful in 12s

## Objectif

Introduit un `CategoryType` dédié **ADRESSE** (module Catalog) consommé par le champ « Catégorie » des blocs adresse, en remplacement de la réutilisation détournée des types CLIENT / FOURNISSEUR.

## Changements

**Backend**
- Migration de seed du type ADRESSE + 6 catégories : Siège, Contact issues, Facturation, Livraison, Approvisionnement, Méthaniseur (idempotente, réversible) ; fixtures alignées.
- `ClientAddress` : validation blacklist (DISTRIBUTEUR/COURTIER) remplacée par une whitelist « catégories de type ADRESSE uniquement ».
- `SupplierAddress` : type requis FOURNISSEUR → ADRESSE (le bloc principal fournisseur reste en FOURNISSEUR).

**Frontend**
- Ref dédiée `addressCategories` (`?typeCode=ADRESSE`) dans les composables référentiels client et fournisseur.
- Pages new/edit client et fournisseur câblées sur les blocs adresse.

**Tests**
- `CategoryAdresseSeedTest` (miroir du test PRESTATAIRE).
- Adaptation des tests d'adresse client/fournisseur (sémantique whitelist ADRESSE) + helper `createAddressCategory()`.

## Vérifications
- Back : suites Catalog + Architecture + adresse/fournisseur vertes (le flake JWT connu du hook est sans rapport, tests verts en isolation).
- Front : Vitest vert (composables référentiels + ciblés).
- php-cs-fixer : 0 correction ; eslint : OK.

Reviewed-on: #147
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
This commit was merged in pull request #147.
This commit is contained in:
2026-06-25 07:26:21 +00:00
committed by Autin
parent 2e50a760c6
commit efded9fd40
22 changed files with 444 additions and 168 deletions
@@ -40,7 +40,7 @@ use Symfony\Component\Validator\Context\ExecutionContextInterface;
* un site obligatoire (RG-2.06, Assert\Count). Site n'a pas de `code`.
* - contacts : SupplierContact (meme module).
* - categories : CategoryInterface (module Catalog) via resolve_target_entities —
* type FOURNISSEUR attendu (RG-2.10, Assert\Callback validateCategoryType).
* type ADRESSE attendu (Assert\Callback validateCategoryType).
*
* Embarquee sous `supplier.addresses` au detail (groupe supplier:item:read,
* maillon (a)).
@@ -110,11 +110,11 @@ class SupplierAddress implements TimestampableInterface, BlamableInterface, Supp
public const array ADDRESS_TYPES = ['PROSPECT', 'DEPART', 'RENDU'];
/**
* RG-2.10 : seules les categories PORTANT ce type sont autorisees sur une
* adresse fournisseur. S'appuie sur CategoryInterface::getCategoryTypeCodes()
* (pas d'import du module Catalog — regle ABSOLUE n°1).
* Seules les categories PORTANT ce type sont autorisees sur une adresse
* fournisseur. S'appuie sur CategoryInterface::getCategoryTypeCodes() (pas
* d'import du module Catalog — regle ABSOLUE n°1).
*/
private const string REQUIRED_CATEGORY_TYPE_CODE = 'FOURNISSEUR';
private const string REQUIRED_CATEGORY_TYPE_CODE = 'ADRESSE';
#[ORM\Id]
#[ORM\GeneratedValue]
@@ -208,8 +208,8 @@ class SupplierAddress implements TimestampableInterface, BlamableInterface, Supp
#[Groups(['supplier:item:read', 'supplier:write:addresses'])]
private Collection $contacts;
// RG-2.10 : au moins une categorie de type FOURNISSEUR par adresse (le type est
// controle par validateCategoryType ; le minimum par Assert\Count, miroir sites).
// Au moins une categorie de type ADRESSE par adresse (le type est controle par
// validateCategoryType ; le minimum par Assert\Count, miroir sites).
/** @var Collection<int, CategoryInterface> */
#[ORM\ManyToMany(targetEntity: CategoryInterface::class)]
#[ORM\JoinTable(name: 'supplier_address_category')]
@@ -227,12 +227,12 @@ class SupplierAddress implements TimestampableInterface, BlamableInterface, Supp
}
/**
* RG-2.10 : toute categorie posee sur une adresse fournisseur doit etre de
* type FOURNISSEUR -> sinon 422 avec violation sur le champ `categories`
* (propertyPath aligne ERP-101, message FR ERP-107). S'appuie sur
* Toute categorie posee sur une adresse fournisseur doit etre de type ADRESSE
* -> sinon 422 avec violation sur le champ `categories` (propertyPath aligne
* ERP-101, message FR ERP-107). S'appuie sur
* CategoryInterface::getCategoryTypeCodes() (multi-type — la categorie est
* acceptee des qu'elle PORTE le type FOURNISSEUR ; pas d'import du module
* Catalog, regle ABSOLUE n°1). Joue avant la base via la validation API Platform.
* acceptee des qu'elle PORTE le type ADRESSE ; pas d'import du module Catalog,
* regle ABSOLUE n°1). Joue avant la base via la validation API Platform.
*/
#[Assert\Callback]
public function validateCategoryType(ExecutionContextInterface $context): void
@@ -240,7 +240,7 @@ class SupplierAddress implements TimestampableInterface, BlamableInterface, Supp
foreach ($this->categories as $category) {
if ($category instanceof CategoryInterface
&& !in_array(self::REQUIRED_CATEGORY_TYPE_CODE, $category->getCategoryTypeCodes(), true)) {
$context->buildViolation('Type de catégorie non autorisé (FOURNISSEUR attendu).')
$context->buildViolation('Type de catégorie non autorisé (ADRESSE attendu).')
->atPath('categories')
->addViolation()
;