banque), RG-2.08 (LCR -> >= 1 RIB). * * On valide l'entite avec le validator Symfony (mapping par attributs) et on * assert le propertyPath exact de chaque violation (contrat ERP-101 : * exploitable par extractApiViolations). Pas de base : les Callback ne touchent * que des champs en memoire (categories via un double CategoryInterface). * * @internal */ final class SupplierValidationTest extends TestCase { private ValidatorInterface $validator; protected function setUp(): void { $this->validator = Validation::createValidatorBuilder() ->enableAttributeMapping() ->getValidator() ; } // === RG-2.10 : categories de type FOURNISSEUR === public function testFournisseurCategoryIsAccepted(): void { $supplier = $this->validSupplier(); self::assertSame([], $this->violationPaths($supplier)); } public function testNonFournisseurCategoryIsRejectedOnCategoriesPath(): void { $supplier = new Supplier(); $supplier->setCompanyName('Recycla SAS'); $supplier->addCategory($this->category('CLIENT')); self::assertContains('categories', $this->violationPaths($supplier)); } public function testMultiTypeCategoryContainingFournisseurIsAccepted(): void { // RG-2.10 sous ManyToMany : une categorie qui PORTE FOURNISSEUR (parmi // d'autres types) reste autorisee sur un fournisseur. $supplier = new Supplier(); $supplier->setCompanyName('Recycla SAS'); $supplier->addCategory($this->category('CLIENT', 'FOURNISSEUR')); self::assertNotContains('categories', $this->violationPaths($supplier)); } // === RG-2.07 : Virement impose une banque === public function testVirementWithoutBankIsRejectedOnBankPath(): void { $supplier = $this->validSupplier(); $supplier->setPaymentType($this->paymentType('VIREMENT')); self::assertContains('bank', $this->violationPaths($supplier)); } public function testVirementWithBankPasses(): void { $supplier = $this->validSupplier(); $supplier->setPaymentType($this->paymentType('VIREMENT')); $supplier->setBank(new Bank()); self::assertNotContains('bank', $this->violationPaths($supplier)); } // === RG-2.08 : LCR impose au moins un RIB === public function testLcrWithoutRibIsRejectedOnPaymentTypePath(): void { $supplier = $this->validSupplier(); $supplier->setPaymentType($this->paymentType('LCR')); // Miroir client : la violation LCR -> >= 1 RIB est portee sur `paymentType` // (affichee sous le select « Type de règlement », `ribs` n'ayant pas de champ). self::assertContains('paymentType', $this->violationPaths($supplier)); } public function testLcrWithRibPasses(): void { $supplier = $this->validSupplier(); $supplier->setPaymentType($this->paymentType('LCR')); $supplier->addRib(new SupplierRib()); self::assertNotContains('paymentType', $this->violationPaths($supplier)); } public function testNeutralPaymentTypeRequiresNeitherBankNorRib(): void { // Un type de reglement neutre (ni VIREMENT ni LCR) n'exige ni banque ni RIB. $supplier = $this->validSupplier(); $supplier->setPaymentType($this->paymentType('CHEQUE')); $paths = $this->violationPaths($supplier); self::assertNotContains('bank', $paths); self::assertNotContains('ribs', $paths); } /** * Fournisseur valide (nom + 1 categorie FOURNISSEUR), sans onglet * Comptabilite renseigne : sert de base aux tests RG-2.07/2.08. */ private function validSupplier(): Supplier { $supplier = new Supplier(); $supplier->setCompanyName('Recycla SAS'); $supplier->addCategory($this->category('FOURNISSEUR')); return $supplier; } /** * @return list propertyPaths des violations levees par le validator */ private function violationPaths(Supplier $supplier): array { $paths = []; foreach ($this->validator->validate($supplier) as $violation) { $paths[] = $violation->getPropertyPath(); } return $paths; } /** * Double minimal de CategoryInterface (pas d'acces base) PORTANT les codes de * type voulus — seul element regarde par validateCategoryType. Variadic pour * couvrir le cas multi-types (ManyToMany). * * @return list n'est pas le type de retour : helper renvoyant un double */ private function category(string ...$typeCodes): CategoryInterface { return new class(array_values($typeCodes)) implements CategoryInterface { /** @param list $typeCodes */ public function __construct(private readonly array $typeCodes) {} public function getId(): ?int { return 1; } public function getName(): ?string { return 'Categorie test'; } public function getCode(): ?string { return 'TEST'; } /** @return list */ public function getCategoryTypeCodes(): array { return $this->typeCodes; } }; } private function paymentType(string $code): PaymentType { $type = new PaymentType(); $type->setCode($code); $type->setLabel($code); return $type; } }