fix(commercial) : rendre obligatoires les 6 champs comptables — 422 back par champ + bouton « Valider » grisé tant que l'onglet est incomplet (ERP-110)
spec-front marquait SIREN / N° compte / Mode TVA / N° TVA / Délai / Type de règlement obligatoires, mais ni le back (colonnes nullable, aucun NotBlank) ni le front (canValidateAccounting ne gardait que bank/RIB) ne l'imposaient : on validait un onglet vide. - Back : ClientAccountingCompletenessValidator (calqué sur RG-1.04) invoqué par le ClientProcessor quand les 6 champs sont présents dans le payload (validation d'onglet) → 422 par propertyPath. PATCH partiel ciblé non impacté. - Front : helper hasAllRequiredAccountingFields + canValidateAccounting étendu dans new.vue / edit.vue (cohérent avec les onglets Contact/Adresse). - Spec-back : RG-1.30 documente la règle et résout l'incohérence spec-front/spec-back.
This commit is contained in:
@@ -8,11 +8,14 @@ use ApiPlatform\Metadata\Operation;
|
||||
use ApiPlatform\State\ProcessorInterface;
|
||||
use ApiPlatform\Validator\Exception\ValidationException;
|
||||
use App\Module\Commercial\Application\Service\ClientFieldNormalizer;
|
||||
use App\Module\Commercial\Application\Validator\ClientAccountingCompletenessValidator;
|
||||
use App\Module\Commercial\Application\Validator\ClientInformationCompletenessValidator;
|
||||
use App\Module\Commercial\Domain\Entity\Bank;
|
||||
use App\Module\Commercial\Domain\Entity\Client;
|
||||
use App\Module\Commercial\Domain\Entity\ClientRib;
|
||||
use App\Module\Commercial\Domain\Entity\PaymentDelay;
|
||||
use App\Module\Commercial\Domain\Entity\PaymentType;
|
||||
use App\Module\Commercial\Domain\Entity\TvaMode;
|
||||
use App\Module\Commercial\Infrastructure\ApiPlatform\State\Processor\ClientProcessor;
|
||||
use App\Shared\Domain\Contract\BusinessRoleAwareInterface;
|
||||
use App\Shared\Domain\Security\BusinessRoles;
|
||||
@@ -280,6 +283,65 @@ final class ClientProcessorTest extends TestCase
|
||||
self::assertInstanceOf(Client::class, $processor->process($client, $this->operation()));
|
||||
}
|
||||
|
||||
public function testFullAccountingSubmitWithEmptyFieldsIsUnprocessable(): void
|
||||
{
|
||||
// spec-front § Onglet Comptabilite : une validation complete de l'onglet
|
||||
// (les 6 champs presents dans le payload) avec des valeurs vides -> 422.
|
||||
// C'est le bug corrige : avant, le back acceptait un onglet tout vide.
|
||||
$client = $this->minimalClient(); // aucun champ comptable renseigne
|
||||
|
||||
$processor = $this->makeProcessor(
|
||||
granted: ['commercial.clients.accounting.manage'],
|
||||
payload: $this->emptyAccountingPayload(),
|
||||
);
|
||||
|
||||
$this->expectException(ValidationException::class);
|
||||
$processor->process($client, $this->operation());
|
||||
}
|
||||
|
||||
public function testFullAccountingSubmitWithAllFieldsPasses(): void
|
||||
{
|
||||
// Les 6 champs obligatoires renseignes + type de reglement neutre
|
||||
// (ni VIREMENT ni LCR -> ni banque ni RIB requis) -> 200.
|
||||
$client = $this->minimalClient();
|
||||
$client->setSiren('123456789');
|
||||
$client->setAccountNumber('00012345678');
|
||||
$client->setTvaMode(new TvaMode());
|
||||
$client->setNTva('FR12345678901');
|
||||
$client->setPaymentDelay(new PaymentDelay());
|
||||
$client->setPaymentType($this->paymentType('CHEQUE'));
|
||||
|
||||
$processor = $this->makeProcessor(
|
||||
granted: ['commercial.clients.accounting.manage'],
|
||||
payload: $this->emptyAccountingPayload(),
|
||||
);
|
||||
|
||||
self::assertInstanceOf(Client::class, $processor->process($client, $this->operation()));
|
||||
}
|
||||
|
||||
public function testPartialAccountingPatchSkipsCompleteness(): void
|
||||
{
|
||||
// Un PATCH ciblant un seul champ comptable n'est pas une validation
|
||||
// d'onglet : la completude n'est pas exigee (les autres champs restent
|
||||
// vides) -> 200. Preserve l'edition ponctuelle (ex. Compta corrige le SIREN).
|
||||
$client = $this->minimalClient();
|
||||
$client->setSiren('999999999');
|
||||
|
||||
$processor = $this->makeProcessor(
|
||||
granted: ['commercial.clients.accounting.manage'],
|
||||
payload: ['siren' => '999999999'],
|
||||
managed: true,
|
||||
originalData: [
|
||||
'siren' => '111111111',
|
||||
'companyName' => 'TEST CO',
|
||||
'triageService' => false,
|
||||
'isArchived' => false,
|
||||
],
|
||||
);
|
||||
|
||||
self::assertInstanceOf(Client::class, $processor->process($client, $this->operation()));
|
||||
}
|
||||
|
||||
public function testCommercialeIncompleteInformationIsUnprocessable(): void
|
||||
{
|
||||
// RG-1.04 : role Commerciale + onglet Information incomplet -> 422.
|
||||
@@ -379,6 +441,7 @@ final class ClientProcessorTest extends TestCase
|
||||
$persist,
|
||||
new ClientFieldNormalizer(),
|
||||
new ClientInformationCompletenessValidator(),
|
||||
new ClientAccountingCompletenessValidator(),
|
||||
$security,
|
||||
$requestStack,
|
||||
$em,
|
||||
@@ -398,6 +461,25 @@ final class ClientProcessorTest extends TestCase
|
||||
return $client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Payload simulant une validation complete de l'onglet Comptabilite : les 6
|
||||
* champs obligatoires presents (le front les envoie toujours ensemble). Les
|
||||
* valeurs importent peu — la completude est evaluee sur l'etat de l'entite.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function emptyAccountingPayload(): array
|
||||
{
|
||||
return [
|
||||
'siren' => null,
|
||||
'accountNumber' => null,
|
||||
'tvaMode' => null,
|
||||
'nTva' => null,
|
||||
'paymentDelay' => null,
|
||||
'paymentType' => null,
|
||||
];
|
||||
}
|
||||
|
||||
private function paymentType(string $code): PaymentType
|
||||
{
|
||||
$type = new PaymentType();
|
||||
|
||||
Reference in New Issue
Block a user