fix(back,front) : adresse client — au moins une categorie obligatoire
Spec-front § Adresse : la categorie est obligatoire sur une adresse, mais n'etait enforced ni au back ni au front. - Back : ClientAddress::$categories porte desormais Assert\Count(min: 1) (POST/PATCH sans categorie -> 422). Test testAddressRequiresAtLeastOneCategory ; deux tests existants qui creaient une adresse sans categorie recoivent une categorie SECTEUR. - Front : canValidateAddresses (creation + modification) exige >= 1 categorie par adresse -> bouton Enregistrer desactive tant qu'aucune categorie n'est choisie (meme gating que les sites).
This commit is contained in:
@@ -743,7 +743,9 @@ const canValidateAddresses = computed(() =>
|
|||||||
addresses.value.length > 0
|
addresses.value.length > 0
|
||||||
&& addresses.value.every((a) => {
|
&& addresses.value.every((a) => {
|
||||||
const filledBillingEmail = a.billingEmail !== null && a.billingEmail.trim() !== ''
|
const filledBillingEmail = a.billingEmail !== null && a.billingEmail.trim() !== ''
|
||||||
return a.siteIris.length >= 1 && (!isBillingEmailRequired(a) || filledBillingEmail)
|
return a.siteIris.length >= 1
|
||||||
|
&& a.categoryIris.length >= 1
|
||||||
|
&& (!isBillingEmailRequired(a) || filledBillingEmail)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -755,7 +755,9 @@ const canValidateAddresses = computed(() =>
|
|||||||
addresses.value.length > 0
|
addresses.value.length > 0
|
||||||
&& addresses.value.every((a) => {
|
&& addresses.value.every((a) => {
|
||||||
const filledBillingEmail = a.billingEmail !== null && a.billingEmail.trim() !== ''
|
const filledBillingEmail = a.billingEmail !== null && a.billingEmail.trim() !== ''
|
||||||
return a.siteIris.length >= 1 && (!isBillingEmailRequired(a) || filledBillingEmail)
|
return a.siteIris.length >= 1
|
||||||
|
&& a.categoryIris.length >= 1
|
||||||
|
&& (!isBillingEmailRequired(a) || filledBillingEmail)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -177,12 +177,14 @@ class ClientAddress implements TimestampableInterface, BlamableInterface
|
|||||||
#[Groups(['client_address:read', 'client_address:write'])]
|
#[Groups(['client_address:read', 'client_address:write'])]
|
||||||
private Collection $contacts;
|
private Collection $contacts;
|
||||||
|
|
||||||
|
// Au moins une categorie est obligatoire sur une adresse (spec-front § Adresse).
|
||||||
// RG-1.29 : categories de code DISTRIBUTEUR/COURTIER interdites (validateCategoryCodes).
|
// RG-1.29 : categories de code DISTRIBUTEUR/COURTIER interdites (validateCategoryCodes).
|
||||||
/** @var Collection<int, CategoryInterface> */
|
/** @var Collection<int, CategoryInterface> */
|
||||||
#[ORM\ManyToMany(targetEntity: CategoryInterface::class)]
|
#[ORM\ManyToMany(targetEntity: CategoryInterface::class)]
|
||||||
#[ORM\JoinTable(name: 'client_address_category')]
|
#[ORM\JoinTable(name: 'client_address_category')]
|
||||||
#[ORM\JoinColumn(name: 'client_address_id', referencedColumnName: 'id', onDelete: 'CASCADE')]
|
#[ORM\JoinColumn(name: 'client_address_id', referencedColumnName: 'id', onDelete: 'CASCADE')]
|
||||||
#[ORM\InverseJoinColumn(name: 'category_id', referencedColumnName: 'id', onDelete: 'RESTRICT')]
|
#[ORM\InverseJoinColumn(name: 'category_id', referencedColumnName: 'id', onDelete: 'RESTRICT')]
|
||||||
|
#[Assert\Count(min: 1, minMessage: 'Au moins une catégorie est obligatoire.')]
|
||||||
#[Groups(['client_address:read', 'client_address:write'])]
|
#[Groups(['client_address:read', 'client_address:write'])]
|
||||||
private Collection $categories;
|
private Collection $categories;
|
||||||
|
|
||||||
|
|||||||
@@ -167,8 +167,9 @@ final class ClientAddressTest extends AbstractCommercialApiTestCase
|
|||||||
public function testNonBillingAddressAcceptsEmptyBillingEmail(): void
|
public function testNonBillingAddressAcceptsEmptyBillingEmail(): void
|
||||||
{
|
{
|
||||||
$this->skipIfSitesModuleDisabled();
|
$this->skipIfSitesModuleDisabled();
|
||||||
$client = $this->createAdminClient();
|
$client = $this->createAdminClient();
|
||||||
$seed = $this->seedClient('Non Billing Empty Email');
|
$seed = $this->seedClient('Non Billing Empty Email');
|
||||||
|
$category = $this->createCategory('SECTEUR');
|
||||||
|
|
||||||
$client->request('POST', '/api/clients/'.$seed->getId().'/addresses', [
|
$client->request('POST', '/api/clients/'.$seed->getId().'/addresses', [
|
||||||
'headers' => ['Content-Type' => self::LD],
|
'headers' => ['Content-Type' => self::LD],
|
||||||
@@ -179,6 +180,7 @@ final class ClientAddressTest extends AbstractCommercialApiTestCase
|
|||||||
'city' => 'Châtellerault',
|
'city' => 'Châtellerault',
|
||||||
'street' => '1 rue du Test',
|
'street' => '1 rue du Test',
|
||||||
'sites' => [$this->firstSiteIri()],
|
'sites' => [$this->firstSiteIri()],
|
||||||
|
'categories' => ['/api/categories/'.$category->getId()],
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -286,6 +288,29 @@ final class ClientAddressTest extends AbstractCommercialApiTestCase
|
|||||||
self::assertResponseStatusCodeSame(201);
|
self::assertResponseStatusCodeSame(201);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spec-front § Adresse : au moins une categorie est obligatoire sur une
|
||||||
|
* adresse. POST sans categorie (mais avec site) -> 422.
|
||||||
|
*/
|
||||||
|
public function testAddressRequiresAtLeastOneCategory(): void
|
||||||
|
{
|
||||||
|
$this->skipIfSitesModuleDisabled();
|
||||||
|
$client = $this->createAdminClient();
|
||||||
|
$seed = $this->seedClient('Address No Cat');
|
||||||
|
|
||||||
|
$client->request('POST', '/api/clients/'.$seed->getId().'/addresses', [
|
||||||
|
'headers' => ['Content-Type' => self::LD],
|
||||||
|
'json' => [
|
||||||
|
'postalCode' => '86100',
|
||||||
|
'city' => 'Châtellerault',
|
||||||
|
'street' => '1 rue du Test',
|
||||||
|
'sites' => [$this->firstSiteIri()],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
self::assertResponseStatusCodeSame(422);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retourne l'IRI du premier site seede (fixtures Sites).
|
* Retourne l'IRI du premier site seede (fixtures Sites).
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -110,9 +110,10 @@ final class ClientSubResourceApiTest extends AbstractCommercialApiTestCase
|
|||||||
public function testPostAddressNormalizesBillingEmail(): void
|
public function testPostAddressNormalizesBillingEmail(): void
|
||||||
{
|
{
|
||||||
$this->skipIfSitesModuleDisabled();
|
$this->skipIfSitesModuleDisabled();
|
||||||
$client = $this->createAdminClient();
|
$client = $this->createAdminClient();
|
||||||
$seed = $this->seedClient('Address Host');
|
$seed = $this->seedClient('Address Host');
|
||||||
$siteIri = $this->firstSiteIri();
|
$siteIri = $this->firstSiteIri();
|
||||||
|
$category = $this->createCategory('SECTEUR');
|
||||||
|
|
||||||
$data = $client->request('POST', '/api/clients/'.$seed->getId().'/addresses', [
|
$data = $client->request('POST', '/api/clients/'.$seed->getId().'/addresses', [
|
||||||
'headers' => ['Content-Type' => self::LD],
|
'headers' => ['Content-Type' => self::LD],
|
||||||
@@ -123,6 +124,7 @@ final class ClientSubResourceApiTest extends AbstractCommercialApiTestCase
|
|||||||
'city' => 'Châtellerault',
|
'city' => 'Châtellerault',
|
||||||
'street' => '1 rue du Test',
|
'street' => '1 rue du Test',
|
||||||
'sites' => [$siteIri],
|
'sites' => [$siteIri],
|
||||||
|
'categories' => ['/api/categories/'.$category->getId()],
|
||||||
],
|
],
|
||||||
])->toArray();
|
])->toArray();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user