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
@@ -15,8 +15,8 @@ use App\Module\Sites\Domain\Entity\Site;
* - RG-1.06 / RG-1.07 / RG-1.08 : exclusivite is_prospect vs
* is_delivery / is_billing ;
* - RG-1.11 : billing_email obligatoire ssi is_billing ;
* - RG-1.29 (ERP-78) : les categories de code DISTRIBUTEUR / COURTIER sont
* interdites sur une adresse (-> 422) ; toute autre categorie est acceptee.
* - categorie d'adresse : seules les categories de type ADRESSE sont acceptees
* (-> 422 sinon), au moins une est obligatoire.
*
* Depuis ERP-76, ces regles sont portees par des Assert\Callback sur l'entite
* ClientAddress (mirror applicatif des CHECK Postgres) : la combinaison invalide
@@ -170,7 +170,7 @@ final class ClientAddressTest extends AbstractCommercialApiTestCase
$this->skipIfSitesModuleDisabled();
$client = $this->createAdminClient();
$seed = $this->seedClient('Non Billing Empty Email');
$category = $this->createCategory('SECTEUR');
$category = $this->createAddressCategory();
$client->request('POST', '/api/clients/'.$seed->getId().'/addresses', [
'headers' => ['Content-Type' => self::LD],
@@ -197,7 +197,7 @@ final class ClientAddressTest extends AbstractCommercialApiTestCase
$this->skipIfSitesModuleDisabled();
$client = $this->createAdminClient();
$seed = $this->seedClient('Billing Two Emails');
$category = $this->createCategory('SECTEUR');
$category = $this->createAddressCategory();
$client->request('POST', '/api/clients/'.$seed->getId().'/addresses', [
'headers' => ['Content-Type' => self::LD],
@@ -225,7 +225,7 @@ final class ClientAddressTest extends AbstractCommercialApiTestCase
$this->skipIfSitesModuleDisabled();
$client = $this->createAdminClient();
$seed = $this->seedClient('Secondary Email Non Billing');
$category = $this->createCategory('SECTEUR');
$category = $this->createAddressCategory();
$body = $client->request('POST', '/api/clients/'.$seed->getId().'/addresses', [
'headers' => ['Content-Type' => self::LD, 'Accept' => self::LD],
@@ -246,15 +246,16 @@ final class ClientAddressTest extends AbstractCommercialApiTestCase
}
/**
* RG-1.29 : poster une categorie de type DISTRIBUTEUR sur une adresse -> 422
* avec violation sur le champ `categories`.
* Une categorie qui n'est PAS de type ADRESSE (ici une categorie CLIENT) est
* refusee sur une adresse -> 422 avec violation sur le champ `categories`.
*/
public function testAddressRejectsDistributorCategory(): void
public function testAddressRejectsNonAddressCategory(): void
{
$this->skipIfSitesModuleDisabled();
$client = $this->createAdminClient();
$seed = $this->seedClient('Address Distributor Cat');
$category = $this->createCategory('DISTRIBUTEUR');
$client = $this->createAdminClient();
$seed = $this->seedClient('Address Non Address Cat');
// Categorie de type CLIENT (et non ADRESSE) -> doit etre refusee sur l'adresse.
$category = $this->createCategory('SECTEUR');
$client->request('POST', '/api/clients/'.$seed->getId().'/addresses', [
'headers' => ['Content-Type' => self::LD],
@@ -270,70 +271,20 @@ final class ClientAddressTest extends AbstractCommercialApiTestCase
self::assertResponseStatusCodeSame(422);
self::assertStringContainsString(
'Type de catégorie non autorisé sur une adresse.',
'Type de catégorie non autorisé (ADRESSE attendu).',
(string) $client->getResponse()->getContent(false),
);
}
/**
* RG-1.29 : poster une categorie de type COURTIER sur une adresse -> 422.
* Une categorie de type ADRESSE est acceptee sur une adresse -> 201.
*/
public function testAddressRejectsBrokerCategory(): void
public function testAddressAcceptsAddressCategory(): void
{
$this->skipIfSitesModuleDisabled();
$client = $this->createAdminClient();
$seed = $this->seedClient('Address Broker Cat');
$category = $this->createCategory('COURTIER');
$client->request('POST', '/api/clients/'.$seed->getId().'/addresses', [
'headers' => ['Content-Type' => self::LD],
'json' => [
'isDelivery' => true,
'postalCode' => '86100',
'city' => 'Châtellerault',
'street' => '1 rue du Test',
'sites' => [$this->firstSiteIri()],
'categories' => ['/api/categories/'.$category->getId()],
],
]);
self::assertResponseStatusCodeSame(422);
}
/**
* RG-1.29 : une categorie de type SECTEUR est autorisee sur une adresse.
*/
public function testAddressAcceptsSectorCategory(): void
{
$this->skipIfSitesModuleDisabled();
$client = $this->createAdminClient();
$seed = $this->seedClient('Address Sector Cat');
$category = $this->createCategory('SECTEUR');
$client->request('POST', '/api/clients/'.$seed->getId().'/addresses', [
'headers' => ['Content-Type' => self::LD],
'json' => [
'isDelivery' => true,
'postalCode' => '86100',
'city' => 'Châtellerault',
'street' => '1 rue du Test',
'sites' => [$this->firstSiteIri()],
'categories' => ['/api/categories/'.$category->getId()],
],
]);
self::assertResponseStatusCodeSame(201);
}
/**
* RG-1.29 : une categorie de type AUTRE est autorisee sur une adresse.
*/
public function testAddressAcceptsOtherCategory(): void
{
$this->skipIfSitesModuleDisabled();
$client = $this->createAdminClient();
$seed = $this->seedClient('Address Other Cat');
$category = $this->createCategory('AUTRE');
$seed = $this->seedClient('Address Address Cat');
$category = $this->createAddressCategory();
$client->request('POST', '/api/clients/'.$seed->getId().'/addresses', [
'headers' => ['Content-Type' => self::LD],
@@ -385,7 +336,7 @@ final class ClientAddressTest extends AbstractCommercialApiTestCase
$this->skipIfSitesModuleDisabled();
$client = $this->createAdminClient();
$seed = $this->seedClient('Address No Type');
$category = $this->createCategory('SECTEUR');
$category = $this->createAddressCategory();
$body = $client->request('POST', '/api/clients/'.$seed->getId().'/addresses', [
'headers' => ['Content-Type' => self::LD, 'Accept' => self::LD],
@@ -413,7 +364,7 @@ final class ClientAddressTest extends AbstractCommercialApiTestCase
$this->skipIfSitesModuleDisabled();
$client = $this->createAdminClient();
$seed = $this->seedClient('Address Broker Type');
$category = $this->createCategory('SECTEUR');
$category = $this->createAddressCategory();
$client->request('POST', '/api/clients/'.$seed->getId().'/addresses', [
'headers' => ['Content-Type' => self::LD],
@@ -435,7 +386,7 @@ final class ClientAddressTest extends AbstractCommercialApiTestCase
$this->skipIfSitesModuleDisabled();
$client = $this->createAdminClient();
$seed = $this->seedClient('Address Distributor Type');
$category = $this->createCategory('SECTEUR');
$category = $this->createAddressCategory();
$client->request('POST', '/api/clients/'.$seed->getId().'/addresses', [
'headers' => ['Content-Type' => self::LD],
@@ -462,7 +413,7 @@ final class ClientAddressTest extends AbstractCommercialApiTestCase
$this->skipIfSitesModuleDisabled();
$client = $this->createAdminClient();
$seed = $this->seedClient('Address Broker Mix');
$category = $this->createCategory('SECTEUR');
$category = $this->createAddressCategory();
$body = $client->request('POST', '/api/clients/'.$seed->getId().'/addresses', [
'headers' => ['Content-Type' => self::LD, 'Accept' => self::LD],