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)); } // === 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 testLcrWithoutRibIsRejectedOnRibsPath(): void { $supplier = $this->validSupplier(); $supplier->setPaymentType($this->paymentType('LCR')); self::assertContains('ribs', $this->violationPaths($supplier)); } public function testLcrWithRibPasses(): void { $supplier = $this->validSupplier(); $supplier->setPaymentType($this->paymentType('LCR')); $supplier->addRib(new SupplierRib()); self::assertNotContains('ribs', $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) renvoyant le code de * type de categorie voulu — seul element regarde par validateCategoryType. */ private function category(string $typeCode): CategoryInterface { return new class($typeCode) implements CategoryInterface { public function __construct(private readonly string $typeCode) {} public function getId(): ?int { return 1; } public function getName(): ?string { return 'Categorie test'; } public function getCode(): ?string { return 'TEST'; } public function getCategoryTypeCode(): ?string { return $this->typeCode; } }; } private function paymentType(string $code): PaymentType { $type = new PaymentType(); $type->setCode($code); $type->setLabel($code); return $type; } }