purge manuelle obligatoire. * * @internal */ abstract class AbstractCommercialApiTestCase extends AbstractApiTestCase { protected const string TEST_CATEGORY_PREFIX = 'test_cli_cat_'; protected function tearDown(): void { $this->cleanupCommercialTestData(); parent::tearDown(); } protected function createAdminClient(): Client { return $this->authenticatedClient('admin', 'admin'); } /** * Recupere (ou cree) un CategoryType par son code metier. Idempotent : la * contrainte d'unicite sur category_type.code interdit les doublons. */ protected function createCategoryType(string $code): CategoryType { $em = $this->getEm(); $existing = $em->getRepository(CategoryType::class)->findOneBy(['code' => $code]); if (null !== $existing) { return $existing; } $type = new CategoryType(); $type->setCode($code); $type->setLabel(ucfirst(strtolower($code))); $em->persist($type); $em->flush(); return $type; } /** * Cree une Category de test rattachee a un type metier donne (code). */ protected function createCategory(string $typeCode = 'SECTEUR'): Category { $em = $this->getEm(); $suffix = substr(bin2hex(random_bytes(4)), 0, 8); $category = new Category(); $category->setName(self::TEST_CATEGORY_PREFIX.$suffix); $category->setCategoryType($this->createCategoryType($typeCode)); $em->persist($category); $em->flush(); return $category; } /** * Seede directement un Client en base (sans passer par l'API), pour les * tests de liste / archivage. Le client porte une categorie SECTEUR. */ protected function seedClient(string $companyName, bool $isArchived = false, string $categoryTypeCode = 'SECTEUR'): ClientEntity { $em = $this->getEm(); $client = new ClientEntity(); // Stocke en MAJUSCULES pour refleter l'etat normalise (RG-1.18) qu'aurait // produit le ClientProcessor via l'API. $client->setCompanyName(mb_strtoupper($companyName, 'UTF-8')); $client->setLastName('Seed'); $client->setPhonePrimary('0102030405'); $client->setEmail(strtolower(str_replace(' ', '', $companyName)).'@seed.test'); $client->addCategory($this->createCategory($categoryTypeCode)); $client->setIsArchived($isArchived); if ($isArchived) { $client->setArchivedAt(new DateTimeImmutable()); } $em->persist($client); $em->flush(); return $client; } private function cleanupCommercialTestData(): void { $em = $this->getEm(); // Clients d'abord (la jointure client_category est purgee par // ON DELETE CASCADE ; les auto-references distributor/broker sont // ON DELETE SET NULL). $em->createQuery('DELETE FROM '.ClientEntity::class)->execute(); // Categories de test ensuite (FK client_category deja purgee). $em->createQuery( 'DELETE FROM '.Category::class.' c WHERE c.name LIKE :prefix', )->setParameter('prefix', self::TEST_CATEGORY_PREFIX.'%')->execute(); // Users / roles jetables. $em->createQuery( 'DELETE FROM '.User::class.' u WHERE u.username LIKE :prefix', )->setParameter('prefix', 'test_%')->execute(); $em->createQuery( 'DELETE FROM '.Role::class.' r WHERE r.code LIKE :prefix', )->setParameter('prefix', 'test_%')->execute(); } }