feat(commercial) : revue de la validation front clients + RG type d'adresse (ERP-119)

- Boutons « Valider » toujours actifs (retrait du gating de validite) : le back renvoie les 422 mappees en rouge par champ.
- Champs requis a colonne non-nullable : la cle est omise du payload si vide (companyName, RIB, adresse) -> 422 NotBlank au lieu d'un 400 de type a la deserialisation.
- Onglet Contact : au moins un contact requis -> l'amorce vide est soumise pour declencher la 422 RG-1.05 quand aucun contact n'est nomme.
- Adresse : affichage inline des erreurs type / sites / categories, et nouvelle RG back « au moins un type d'adresse obligatoire » (Callback sur isProspect).
This commit is contained in:
2026-06-09 13:13:50 +02:00
parent b3ab23ee8f
commit 982f501b94
11 changed files with 267 additions and 111 deletions
@@ -146,6 +146,7 @@ final class ClientAddressTest extends AbstractCommercialApiTestCase
$client->request('POST', '/api/clients/'.$seed->getId().'/addresses', [
'headers' => ['Content-Type' => self::LD],
'json' => [
'isDelivery' => true,
'isBilling' => false,
'billingEmail' => 'parasite@test.fr',
'postalCode' => '86100',
@@ -174,6 +175,7 @@ final class ClientAddressTest extends AbstractCommercialApiTestCase
$client->request('POST', '/api/clients/'.$seed->getId().'/addresses', [
'headers' => ['Content-Type' => self::LD],
'json' => [
'isDelivery' => true,
'isBilling' => false,
'billingEmail' => '',
'postalCode' => '86100',
@@ -201,6 +203,7 @@ final class ClientAddressTest extends AbstractCommercialApiTestCase
$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',
@@ -229,6 +232,7 @@ final class ClientAddressTest extends AbstractCommercialApiTestCase
$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',
@@ -253,6 +257,7 @@ final class ClientAddressTest extends AbstractCommercialApiTestCase
$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',
@@ -277,6 +282,7 @@ final class ClientAddressTest extends AbstractCommercialApiTestCase
$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',
@@ -301,6 +307,7 @@ final class ClientAddressTest extends AbstractCommercialApiTestCase
$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',
@@ -311,6 +318,39 @@ final class ClientAddressTest extends AbstractCommercialApiTestCase
self::assertResponseStatusCodeSame(422);
}
/**
* RG (ERP-119) : au moins un type d'adresse (Prospection / Livraison /
* Facturation) est obligatoire. POST sans aucun drapeau de type -> 422, avec
* une violation portee sur `isProspect` (mappee sous le select « Type
* d'adresse » cote front via ClientAddressBlock).
*/
public function testAddressRequiresAtLeastOneType(): void
{
$this->skipIfSitesModuleDisabled();
$client = $this->createAdminClient();
$seed = $this->seedClient('Address No Type');
$category = $this->createCategory('SECTEUR');
$body = $client->request('POST', '/api/clients/'.$seed->getId().'/addresses', [
'headers' => ['Content-Type' => self::LD, 'Accept' => self::LD],
'json' => [
'postalCode' => '86100',
'city' => 'Châtellerault',
'street' => '1 rue du Test',
'sites' => [$this->firstSiteIri()],
'categories' => ['/api/categories/'.$category->getId()],
],
])->toArray(false);
self::assertResponseStatusCodeSame(422);
$byPath = [];
foreach ($body['violations'] ?? [] as $v) {
$byPath[$v['propertyPath']] = $v['message'];
}
self::assertArrayHasKey('isProspect', $byPath);
self::assertSame('Le type d\'adresse est obligatoire.', $byPath['isProspect']);
}
/**
* Retourne l'IRI du premier site seede (fixtures Sites).
*/
@@ -237,6 +237,7 @@ final class ClientSubResourceApiTest extends AbstractCommercialApiTestCase
$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',
@@ -258,6 +259,7 @@ final class ClientSubResourceApiTest extends AbstractCommercialApiTestCase
$client->request('POST', '/api/clients/'.$seed->getId().'/addresses', [
'headers' => ['Content-Type' => self::LD],
'json' => [
'isDelivery' => true,
'postalCode' => '123',
'city' => 'Châtellerault',
'street' => '1 rue du Test',
@@ -287,6 +289,7 @@ final class ClientSubResourceApiTest extends AbstractCommercialApiTestCase
$client->request('POST', '/api/clients/'.$seed->getId().'/addresses', [
'headers' => ['Content-Type' => self::LD, 'Accept' => self::LD],
'json' => [
'isDelivery' => true,
'postalCode' => '75001',
'city' => 'Paris',
'street' => '2 rue Neuve',
@@ -313,6 +316,7 @@ final class ClientSubResourceApiTest extends AbstractCommercialApiTestCase
$client->request('POST', '/api/clients/999999/addresses', [
'headers' => ['Content-Type' => self::LD, 'Accept' => self::LD],
'json' => [
'isDelivery' => true,
'postalCode' => '75001',
'city' => 'Paris',
'street' => '2 rue Neuve',