e265a008bc
Auto Tag Develop / tag (push) Successful in 7s
Étape 4/7 du M2 fournisseurs — stackée sur #67 (ERP-88). ## Périmètre (RG-2.03 / 2.07 / 2.08 / 2.10) Décision figée ERP-89 : les RG inter-champs passent par `Assert\Callback` + `->atPath()` sur l'entité Supplier (et non dans le Processor), pour que chaque 422 porte un `propertyPath` consommable par `extractApiViolations` (mapping inline, pas un toast — ERP-101). - **RG-2.10** — `Supplier::validateCategoryType()` → `atPath('categories')` : catégories de type FOURNISSEUR uniquement sur `supplier.categories` (miroir d'ERP-88 côté adresse). - **RG-2.07** — `Supplier::validatePaymentTypeConsistency()` → `atPath('bank')` : VIREMENT impose une banque. - **RG-2.08** — même Callback → `atPath('ribs')` : LCR impose ≥ 1 RIB (le 409 sur DELETE du dernier RIB en LCR reste porté par ERP-88). - **RG-2.03** — `SupplierInformationCompletenessValidator` (8 champs Information dont `volumeForecast`), invoqué par le `SupplierProcessor` après détection back du rôle Commerciale via `BusinessRoleAwareInterface`. Le Processor ne porte que rôle / mode strict / gating. ## Tests (16, verts) - `SupplierValidationTest` — Callbacks RG-2.07/2.08/2.10, assertion par propertyPath. - `SupplierInformationCompletenessValidatorTest` — complétude / champs manquants / zéros valides. - `SupplierProcessorTest` — détection rôle RG-2.03 (POST + PATCH main-only + non-Commerciale). `make test` : 499 tests OK. `php-cs-fixer` : clean. --------- Co-authored-by: admin malio <malio@yuno.malio.fr> Co-authored-by: Matthieu <contact@malio.fr> Reviewed-on: #68 Co-authored-by: THOLOT DECHENE Matthieu <matthieu@yuno.malio.fr> Co-committed-by: THOLOT DECHENE Matthieu <matthieu@yuno.malio.fr>
130 lines
4.3 KiB
PHP
130 lines
4.3 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Tests\Module\Commercial\Unit;
|
|
|
|
use ApiPlatform\Validator\Exception\ValidationException;
|
|
use App\Module\Commercial\Application\Validator\SupplierInformationCompletenessValidator;
|
|
use App\Module\Commercial\Domain\Entity\Supplier;
|
|
use DateTimeImmutable;
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
/**
|
|
* Tests unitaires du SupplierInformationCompletenessValidator (RG-2.03) : pour le
|
|
* role Commerciale, TOUS les champs de l'onglet Information sont obligatoires.
|
|
* Chaque champ manquant produit une violation portant son propertyPath (ERP-101).
|
|
*
|
|
* @internal
|
|
*/
|
|
final class SupplierInformationCompletenessValidatorTest extends TestCase
|
|
{
|
|
public function testCompleteInformationPasses(): void
|
|
{
|
|
$supplier = $this->completeSupplier();
|
|
|
|
$this->validator()->validate($supplier);
|
|
|
|
// Aucune exception levee : la completude est satisfaite.
|
|
$this->addToAssertionCount(1);
|
|
}
|
|
|
|
public function testEmptyInformationListsEveryMissingField(): void
|
|
{
|
|
$supplier = new Supplier();
|
|
$supplier->setCompanyName('Recycla SAS'); // onglet principal, hors Information
|
|
|
|
try {
|
|
$this->validator()->validate($supplier);
|
|
self::fail('Une ValidationException etait attendue (onglet Information vide).');
|
|
} catch (ValidationException $e) {
|
|
$paths = [];
|
|
foreach ($e->getConstraintViolationList() as $violation) {
|
|
$paths[] = $violation->getPropertyPath();
|
|
}
|
|
|
|
// Les 8 champs Information (dont volumeForecast, NEW vs Client) sont
|
|
// tous signales d'un coup, chacun sous son propre propertyPath.
|
|
sort($paths);
|
|
self::assertSame([
|
|
'competitors',
|
|
'description',
|
|
'directorName',
|
|
'employeesCount',
|
|
'foundedAt',
|
|
'profitAmount',
|
|
'revenueAmount',
|
|
'volumeForecast',
|
|
], $paths);
|
|
}
|
|
}
|
|
|
|
public function testPartialInformationReportsOnlyMissingFields(): void
|
|
{
|
|
$supplier = $this->completeSupplier();
|
|
$supplier->setDirectorName(null);
|
|
$supplier->setVolumeForecast(null);
|
|
|
|
try {
|
|
$this->validator()->validate($supplier);
|
|
self::fail('Une ValidationException etait attendue (2 champs manquants).');
|
|
} catch (ValidationException $e) {
|
|
$paths = [];
|
|
foreach ($e->getConstraintViolationList() as $violation) {
|
|
$paths[] = $violation->getPropertyPath();
|
|
}
|
|
|
|
sort($paths);
|
|
self::assertSame(['directorName', 'volumeForecast'], $paths);
|
|
}
|
|
}
|
|
|
|
public function testZeroNumericValuesAreNotMissing(): void
|
|
{
|
|
// employeesCount = 0, profitAmount = "0.00", volumeForecast = 0 sont des
|
|
// valeurs valides (un zero n'est pas une absence) -> pas de violation.
|
|
$supplier = $this->completeSupplier();
|
|
$supplier->setEmployeesCount(0);
|
|
$supplier->setProfitAmount('0.00');
|
|
$supplier->setVolumeForecast(0);
|
|
|
|
$this->validator()->validate($supplier);
|
|
|
|
$this->addToAssertionCount(1);
|
|
}
|
|
|
|
public function testBlankStringIsMissing(): void
|
|
{
|
|
// Une chaine vide apres trim compte comme manquante.
|
|
$supplier = $this->completeSupplier();
|
|
$supplier->setDescription(' ');
|
|
|
|
$this->expectException(ValidationException::class);
|
|
$this->validator()->validate($supplier);
|
|
}
|
|
|
|
/**
|
|
* Fournisseur dont l'onglet Information est entierement renseigne.
|
|
*/
|
|
private function completeSupplier(): Supplier
|
|
{
|
|
$supplier = new Supplier();
|
|
$supplier->setCompanyName('Recycla SAS');
|
|
$supplier->setDescription('Specialiste du recyclage');
|
|
$supplier->setCompetitors('Concurrent A, Concurrent B');
|
|
$supplier->setFoundedAt(new DateTimeImmutable('2010-01-01'));
|
|
$supplier->setEmployeesCount(42);
|
|
$supplier->setRevenueAmount('1000000.00');
|
|
$supplier->setDirectorName('Marie Durand');
|
|
$supplier->setProfitAmount('150000.00');
|
|
$supplier->setVolumeForecast(5000);
|
|
|
|
return $supplier;
|
|
}
|
|
|
|
private function validator(): SupplierInformationCompletenessValidator
|
|
{
|
|
return new SupplierInformationCompletenessValidator();
|
|
}
|
|
}
|