OrganizationFactory.php 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779
  1. <?php
  2. namespace App\Service\Organization;
  3. use App\ApiResources\Organization\OrganizationCreationRequest;
  4. use App\ApiResources\Organization\OrganizationMemberCreationRequest;
  5. use App\Entity\Access\Access;
  6. use App\Entity\Access\OrganizationFunction;
  7. use App\Entity\Core\AddressPostal;
  8. use App\Entity\Core\ContactPoint;
  9. use App\Entity\Education\Cycle;
  10. use App\Entity\Network\NetworkOrganization;
  11. use App\Entity\Organization\Organization;
  12. use App\Entity\Organization\OrganizationAddressPostal;
  13. use App\Entity\Organization\Parameters;
  14. use App\Entity\Organization\Settings;
  15. use App\Entity\Organization\Subdomain;
  16. use App\Entity\Person\Person;
  17. use App\Entity\Person\PersonAddressPostal;
  18. use App\Enum\Access\FunctionEnum;
  19. use App\Enum\Core\ContactPointTypeEnum;
  20. use App\Enum\Education\CycleEnum;
  21. use App\Enum\Network\NetworkEnum;
  22. use App\Enum\Organization\AddressPostalOrganizationTypeEnum;
  23. use App\Enum\Organization\SettingsProductEnum;
  24. use App\Enum\Person\AddressPostalPersonTypeEnum;
  25. use App\Repository\Access\FunctionTypeRepository;
  26. use App\Repository\Core\CountryRepository;
  27. use App\Repository\Organization\OrganizationIdentificationRepository;
  28. use App\Repository\Organization\OrganizationRepository;
  29. use App\Repository\Person\PersonRepository;
  30. use App\Service\ApiLegacy\ApiLegacyRequestService;
  31. use App\Service\Dolibarr\DolibarrApiService;
  32. use App\Service\Organization\Utils as OrganizationUtils;
  33. use App\Service\Typo3\BindFileService;
  34. use App\Service\Typo3\SubdomainService;
  35. use App\Service\Typo3\Typo3Service;
  36. use App\Service\Utils\DatesUtils;
  37. use Doctrine\ORM\EntityManagerInterface;
  38. use Elastica\Param;
  39. use libphonenumber\NumberParseException;
  40. use libphonenumber\PhoneNumberUtil;
  41. use Psr\Log\LoggerInterface;
  42. use Symfony\Component\HttpFoundation\Response;
  43. use Symfony\Component\String\ByteString;
  44. use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
  45. use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
  46. use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
  47. use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
  48. use Symfony\Contracts\Service\Attribute\Required;
  49. class OrganizationFactory
  50. {
  51. private LoggerInterface $logger;
  52. protected PhoneNumberUtil $phoneNumberUtil;
  53. public function __construct(
  54. private readonly SubdomainService $subdomainService,
  55. private readonly OrganizationRepository $organizationRepository,
  56. private readonly CountryRepository $countryRepository,
  57. private readonly OrganizationUtils $organizationUtils,
  58. private readonly Typo3Service $typo3Service,
  59. private readonly DolibarrApiService $dolibarrApiService,
  60. private readonly EntityManagerInterface $entityManager,
  61. private readonly PersonRepository $personRepository,
  62. private readonly BindFileService $bindFileService,
  63. private readonly OrganizationIdentificationRepository $organizationIdentificationRepository,
  64. private readonly ApiLegacyRequestService $apiLegacyRequestService,
  65. private readonly FunctionTypeRepository $functionTypeRepository,
  66. ) {
  67. $this->phoneNumberUtil = PhoneNumberUtil::getInstance();
  68. }
  69. #[Required]
  70. /** @see https://symfony.com/doc/current/logging/channels_handlers.html#how-to-autowire-logger-channels */
  71. public function setLoggerInterface(LoggerInterface $adminLogger): void
  72. {
  73. $this->logger = $adminLogger;
  74. }
  75. /**
  76. * Créé une nouvelle organisation à partir des données contenues dans une OrganizationCreationRequest.
  77. *
  78. * @throws TransportExceptionInterface
  79. * @throws \Throwable
  80. */
  81. public function create(OrganizationCreationRequest $organizationCreationRequest): Organization
  82. {
  83. $this->logger->info(
  84. "Start the creation of a new organization named '".$organizationCreationRequest->getName()."'"
  85. );
  86. $this->entityManager->beginTransaction();
  87. try {
  88. // On vérifie si cette organisation n'existe pas déjà
  89. $this->interruptIfOrganizationExists($organizationCreationRequest);
  90. // On vérifie la validité et la disponibilité du sous domaine
  91. $this->validateSubdomain($organizationCreationRequest->getSubdomain());
  92. $this->logger->info("Subdomain is valid and available : '".$organizationCreationRequest->getSubdomain()."'");
  93. // On construit l'organisation et ses relations
  94. $organization = $this->makeOrganizationWithRelations($organizationCreationRequest);
  95. $this->logger->info('Organization created with all its relations');
  96. // On persiste et on commit, les objets liés seront persistés en cascade
  97. $this->entityManager->persist($organization);
  98. $this->entityManager->flush();
  99. $this->entityManager->commit();
  100. $this->logger->debug(' - New entities committed in DB');
  101. $this->logger->info('Organization persisted in the DB');
  102. } catch (\Throwable $e) {
  103. $this->logger->critical("An error happened, operation cancelled\n".$e);
  104. $this->entityManager->rollback();
  105. throw $e;
  106. }
  107. $withError = false;
  108. // Création de la société Dolibarr
  109. try {
  110. $dolibarrId = $this->dolibarrApiService->createSociety(
  111. $organization,
  112. $organizationCreationRequest->isClient()
  113. );
  114. $this->logger->info('New dolibarr structure created (uid : '.$dolibarrId.')');
  115. } catch (\Throwable $e) {
  116. $this->logger->critical('An error happened while creating the dolibarr society, please proceed manually.');
  117. $this->logger->debug($e);
  118. $withError = true;
  119. }
  120. // Register the subdomain into the BindFile (takes up to 5min to take effect)
  121. try {
  122. $this->bindFileService->registerSubdomain($organizationCreationRequest->getSubdomain());
  123. $this->logger->info('Subdomain registered');
  124. } catch (\Throwable $e) {
  125. $this->logger->critical('An error happened while updating the bind file, please proceed manually.');
  126. $this->logger->debug($e);
  127. $withError = true;
  128. }
  129. // Création du site typo3 (on est obligé d'attendre que l'organisation soit persistée en base)
  130. if ($organizationCreationRequest->getCreateWebsite()) {
  131. try {
  132. $rootUid = $this->createTypo3Website($organization);
  133. $this->logger->info('Typo3 website created (root uid: '.$rootUid.')');
  134. } catch (\Throwable $e) {
  135. $this->logger->critical('An error happened while creating the typo3 website, please proceed manually.');
  136. $this->logger->debug($e);
  137. $withError = true;
  138. }
  139. } else {
  140. $this->logger->warning('Typo3 website creation was not required');
  141. }
  142. // Création de l'organisation dans la base adminassos (géré par la V1)
  143. try {
  144. $this->updateAdminassosDb($organization);
  145. $this->logger->info('Adminassos db updated');
  146. } catch (\Throwable $e) {
  147. $this->logger->critical('An error happened while updating the adminassos db, please proceed manually.');
  148. $this->logger->debug($e);
  149. $withError = true;
  150. }
  151. if ($withError) {
  152. $organizationCreationRequest->setStatus(OrganizationCreationRequest::STATUS_OK_WITH_ERRORS);
  153. $this->logger->warning('-- Operation ended with errors, check the logs for more information --');
  154. } else {
  155. $organizationCreationRequest->setStatus(OrganizationCreationRequest::STATUS_OK);
  156. }
  157. return $organization;
  158. }
  159. /**
  160. * Lève une exception si cette organisation existe déjà.
  161. */
  162. protected function interruptIfOrganizationExists(OrganizationCreationRequest $organizationCreationRequest): void
  163. {
  164. if (
  165. $organizationCreationRequest->getSiretNumber()
  166. && $this->organizationIdentificationRepository->findOneBy(
  167. ['siretNumber' => $organizationCreationRequest->getSiretNumber()]
  168. )
  169. ) {
  170. throw new \RuntimeException("This siret number is already registered : '".$organizationCreationRequest->getSiretNumber()."'");
  171. }
  172. if (
  173. $organizationCreationRequest->getWaldecNumber()
  174. && $this->organizationIdentificationRepository->findOneBy(
  175. ['waldecNumber' => $organizationCreationRequest->getWaldecNumber()]
  176. )
  177. ) {
  178. throw new \RuntimeException("This RNA identifier (waldec number) is already registered : '".$organizationCreationRequest->getWaldecNumber()."'");
  179. }
  180. if (
  181. $organizationCreationRequest->getIdentifier()
  182. && $this->organizationIdentificationRepository->findOneBy(
  183. ['identifier' => $organizationCreationRequest->getIdentifier()]
  184. )
  185. ) {
  186. throw new \RuntimeException("This CMF identifier is already registered : '".$organizationCreationRequest->getIdentifier()."'");
  187. }
  188. $normalizedName = $this->normalizeIdentificationField($organizationCreationRequest->getName());
  189. if (
  190. $this->organizationIdentificationRepository->findOneBy(
  191. ['normalizedName' => $normalizedName, 'addressCity' => $organizationCreationRequest->getCity()]
  192. )
  193. ) {
  194. throw new \RuntimeException("An organization named '".$organizationCreationRequest->getName()."' already exists in ".$organizationCreationRequest->getCity());
  195. }
  196. $address = $this->normalizeIdentificationField(implode(' ', [
  197. $organizationCreationRequest->getStreetAddress1(),
  198. $organizationCreationRequest->getStreetAddress2(),
  199. $organizationCreationRequest->getStreetAddress3(),
  200. ]));
  201. if (
  202. $this->organizationIdentificationRepository->findOneBy(
  203. [
  204. 'normalizedAddress' => $address,
  205. 'addressCity' => $organizationCreationRequest->getCity(),
  206. 'postalCode' => $organizationCreationRequest->getPostalCode(),
  207. ]
  208. )
  209. ) {
  210. throw new \RuntimeException('An organization already exists at this address.');
  211. }
  212. }
  213. /**
  214. * Vérifie la disponibilité et la validité d'un sous domaine.
  215. *
  216. * @throws \Exception
  217. */
  218. protected function validateSubdomain(string $subdomainValue): void
  219. {
  220. if (!$this->subdomainService->isValidSubdomain($subdomainValue)) {
  221. throw new \RuntimeException('Not a valid subdomain : '.$subdomainValue);
  222. }
  223. if ($this->subdomainService->isReservedSubdomain($subdomainValue)) {
  224. throw new \RuntimeException('This subdomain is not available : '.$subdomainValue);
  225. }
  226. if ($this->subdomainService->isRegistered($subdomainValue)) {
  227. throw new \RuntimeException('This subdomain is already registered : '.$subdomainValue);
  228. }
  229. }
  230. /**
  231. * Créé une nouvelle instance d'organisation, et toutes les instances liées (paramètres, contact, adresses, ...),
  232. * selon le contenu de la requête de création.
  233. *
  234. * @throws \Throwable
  235. */
  236. protected function makeOrganizationWithRelations(
  237. OrganizationCreationRequest $organizationCreationRequest,
  238. ): Organization {
  239. // Création de l'organisation
  240. $organization = $this->makeOrganization($organizationCreationRequest);
  241. $this->logger->debug(' - Organization created');
  242. // Création des Parameters
  243. $parameters = $this->makeParameters($organizationCreationRequest);
  244. $organization->setParameters($parameters);
  245. $this->logger->debug(' - Parameters created');
  246. // Création des Settings
  247. $settings = $this->makeSettings($organizationCreationRequest);
  248. $organization->setSettings($settings);
  249. $this->logger->debug(' - Settings created');
  250. // Création de l'adresse postale
  251. $organizationAddressPostal = $this->makePostalAddress($organizationCreationRequest);
  252. $organization->addOrganizationAddressPostal($organizationAddressPostal);
  253. $this->logger->debug(' - OrganizationAddressPostal created');
  254. // Création du point de contact
  255. $contactPoint = $this->makeContactPoint($organizationCreationRequest);
  256. $organization->addContactPoint($contactPoint);
  257. $this->logger->debug(' - ContactPoint created');
  258. // Rattachement au réseau
  259. $networkOrganization = $this->makeNetworkOrganization($organizationCreationRequest);
  260. $organization->addNetworkOrganization($networkOrganization);
  261. $this->logger->debug(' - NetworkOrganization created');
  262. // Créé l'admin
  263. $adminAccess = $this->makeAdminAccess($organizationCreationRequest);
  264. $organization->addAccess($adminAccess);
  265. $this->logger->debug(' - Admin access created');
  266. // Création des cycles
  267. foreach ($this->makeCycles() as $cycle) {
  268. $organization->addCycle($cycle);
  269. }
  270. $this->logger->debug(' - Cycles created');
  271. // Création du président (si renseigné)
  272. $presidentCreationRequest = $organizationCreationRequest->getPresident();
  273. if ($presidentCreationRequest !== null) {
  274. $presidentAccess = $this->makeAccess(
  275. $presidentCreationRequest,
  276. FunctionEnum::PRESIDENT,
  277. $organizationCreationRequest->getCreationDate(),
  278. $organizationCreationRequest->getAuthorId()
  279. );
  280. $organization->addAccess($presidentAccess);
  281. $this->logger->debug(' - President access created');
  282. }
  283. // Création du directeur (si renseigné)
  284. $directorCreationRequest = $organizationCreationRequest->getDirector();
  285. if ($directorCreationRequest !== null) {
  286. $directorAccess = $this->makeAccess(
  287. $directorCreationRequest,
  288. FunctionEnum::DIRECTOR,
  289. $organizationCreationRequest->getCreationDate(),
  290. $organizationCreationRequest->getAuthorId()
  291. );
  292. $organization->addAccess($directorAccess);
  293. $this->logger->debug(' - Director access created');
  294. }
  295. // Création du sous-domaine
  296. $subdomain = $this->makeSubdomain($organizationCreationRequest);
  297. $organization->addSubdomain($subdomain);
  298. // <--- Pour la rétrocompatibilité avec la v1 ; pourra être supprimé lorsque la migration sera achevée
  299. $parameters = $organization->getParameters();
  300. $parameters->setSubDomain($organizationCreationRequest->getSubdomain());
  301. $parameters->setOtherWebsite('https://'.$organizationCreationRequest->getSubdomain().'.opentalent.fr');
  302. // --->
  303. $this->logger->debug(' - Subdomain created');
  304. return $organization;
  305. }
  306. /**
  307. * Créé une nouvelle instance d'organisation.
  308. */
  309. protected function makeOrganization(OrganizationCreationRequest $organizationCreationRequest): Organization
  310. {
  311. // Création de l'organisation
  312. $organization = new Organization();
  313. $organization->setName($organizationCreationRequest->getName());
  314. $organization->setLegalStatus($organizationCreationRequest->getLegalStatus());
  315. $organization->setPrincipalType($organizationCreationRequest->getPrincipalType());
  316. $organization->setIdentifier($organizationCreationRequest->getIdentifier());
  317. $organization->setCreationDate($organizationCreationRequest->getCreationDate());
  318. $organization->setCreateDate($organizationCreationRequest->getCreationDate());
  319. $organization->setCreatedBy($organizationCreationRequest->getAuthorId());
  320. return $organization;
  321. }
  322. /**
  323. * Create a new Parameters object from the data in an OrganizationCreationRequest.
  324. *
  325. * @param OrganizationCreationRequest $organizationCreationRequest The organization creation request
  326. *
  327. * @return Parameters The created Parameters object
  328. *
  329. * @throws \Throwable If there is an error
  330. */
  331. protected function makeParameters(OrganizationCreationRequest $organizationCreationRequest): Parameters
  332. {
  333. $parameters = new Parameters();
  334. return $parameters;
  335. }
  336. /**
  337. * Creates a new instance of the Settings class based on the given OrganizationCreationRequest object.
  338. *
  339. * @param OrganizationCreationRequest $organizationCreationRequest the OrganizationCreationRequest object containing the required data
  340. *
  341. * @return Settings the newly created instance of the Settings class
  342. */
  343. protected function makeSettings(OrganizationCreationRequest $organizationCreationRequest): Settings
  344. {
  345. $settings = new Settings();
  346. $settings->setProduct($organizationCreationRequest->getProduct());
  347. // TODO: à revoir, pour étendre à d'autres pays (voir à remplacer le champs 'country' par un champs 'currency'?)
  348. $settings->setCountry(
  349. $organizationCreationRequest->getCountryId() === 41 ? 'SWITZERLAND' : 'FRANCE'
  350. );
  351. $settings->setCreateDate($organizationCreationRequest->getCreationDate());
  352. $settings->setCreatedBy($organizationCreationRequest->getAuthorId());
  353. return $settings;
  354. }
  355. /**
  356. * Creates a new instance of the OrganizationAddressPostal class based on the given OrganizationCreationRequest object.
  357. *
  358. * @param OrganizationCreationRequest $organizationCreationRequest the OrganizationCreationRequest object containing the required data
  359. *
  360. * @return OrganizationAddressPostal the newly created instance of the OrganizationAddressPostal class
  361. */
  362. protected function makePostalAddress(OrganizationCreationRequest $organizationCreationRequest): OrganizationAddressPostal
  363. {
  364. $country = $this->countryRepository->find($organizationCreationRequest->getCountryId());
  365. if (!$country) {
  366. throw new \RuntimeException('No country found for id '.$organizationCreationRequest->getCountryId());
  367. }
  368. $addressPostal = new AddressPostal();
  369. $addressPostal->setStreetAddress($organizationCreationRequest->getStreetAddress1());
  370. $addressPostal->setStreetAddressSecond($organizationCreationRequest->getStreetAddress2());
  371. $addressPostal->setStreetAddressThird($organizationCreationRequest->getStreetAddress3());
  372. $addressPostal->setPostalCode($organizationCreationRequest->getPostalCode());
  373. $addressPostal->setAddressCity($organizationCreationRequest->getCity());
  374. $addressPostal->setAddressCountry($country);
  375. $addressPostal->setCreateDate($organizationCreationRequest->getCreationDate());
  376. $addressPostal->setCreatedBy($organizationCreationRequest->getAuthorId());
  377. $organizationAddressPostal = new OrganizationAddressPostal();
  378. $organizationAddressPostal->setAddressPostal($addressPostal);
  379. $organizationAddressPostal->setType(AddressPostalOrganizationTypeEnum::ADDRESS_HEAD_OFFICE);
  380. $organizationAddressPostal->setCreateDate($organizationCreationRequest->getCreationDate());
  381. $organizationAddressPostal->setCreatedBy($organizationCreationRequest->getAuthorId());
  382. return $organizationAddressPostal;
  383. }
  384. /**
  385. * Creates a new instance of the ContactPoint class based on the given OrganizationCreationRequest object.
  386. *
  387. * @param OrganizationCreationRequest $organizationCreationRequest the OrganizationCreationRequest object containing the required data
  388. *
  389. * @return ContactPoint the newly created instance of the ContactPoint class
  390. *
  391. * @throws NumberParseException
  392. */
  393. protected function makeContactPoint(OrganizationCreationRequest $organizationCreationRequest): ContactPoint
  394. {
  395. if (!$this->phoneNumberUtil->isPossibleNumber($organizationCreationRequest->getPhoneNumber())) {
  396. throw new \RuntimeException('Phone number is invalid or missing');
  397. }
  398. $phoneNumber = $this->phoneNumberUtil->parse($organizationCreationRequest->getPhoneNumber());
  399. $contactPoint = new ContactPoint();
  400. $contactPoint->setContactType(ContactPointTypeEnum::PRINCIPAL);
  401. $contactPoint->setEmail($organizationCreationRequest->getEmail());
  402. $contactPoint->setTelphone($phoneNumber);
  403. $contactPoint->setCreateDate($organizationCreationRequest->getCreationDate());
  404. $contactPoint->setCreatedBy($organizationCreationRequest->getAuthorId());
  405. return $contactPoint;
  406. }
  407. /**
  408. * Creates a new instance of the NetworkOrganization class based on the given OrganizationCreationRequest object.
  409. *
  410. * @param OrganizationCreationRequest $organizationCreationRequest the OrganizationCreationRequest object containing the required data
  411. *
  412. * @return NetworkOrganization the newly created instance of the NetworkOrganization class
  413. *
  414. * @throws \RuntimeException|\Exception if no parent organization is found for the given parent ID or if no network is found for the given network ID
  415. */
  416. protected function makeNetworkOrganization(OrganizationCreationRequest $organizationCreationRequest): NetworkOrganization
  417. {
  418. $parent = $this->organizationRepository->find($organizationCreationRequest->getParentId());
  419. if (!$parent) {
  420. throw new \RuntimeException('No parent organization found for id '.$organizationCreationRequest->getParentId());
  421. }
  422. if ($parent->getSettings()->getProduct() !== SettingsProductEnum::MANAGER) {
  423. throw new \RuntimeException("Parent organization must have the product 'manager' (actual product: '".$parent->getSettings()->getProduct()->value."')");
  424. }
  425. $networkOrganization = $this->organizationUtils->getActiveNetworkOrganization($parent);
  426. if (!$networkOrganization) {
  427. throw new \RuntimeException('No network found for parent '.$organizationCreationRequest->getParentId());
  428. }
  429. $network = $networkOrganization->getNetwork();
  430. // Si réseau CMF, on vérifie que le matricule est valide
  431. if ($network->getId() === NetworkEnum::CMF->value) {
  432. if (!preg_match("/FR\d{12}/", $organizationCreationRequest->getIdentifier())) {
  433. throw new \RuntimeException('CMF identifier is missing or invalid.');
  434. }
  435. }
  436. $networkOrganization = new NetworkOrganization();
  437. $networkOrganization->setParent($parent);
  438. $networkOrganization->setNetwork($network);
  439. $networkOrganization->setStartDate(DatesUtils::new());
  440. $networkOrganization->setCreateDate($organizationCreationRequest->getCreationDate());
  441. $networkOrganization->setCreatedBy($organizationCreationRequest->getAuthorId());
  442. return $networkOrganization;
  443. }
  444. /**
  445. * Creates a new instance of the Access class with admin access based on the given OrganizationCreationRequest object.
  446. *
  447. * @param OrganizationCreationRequest $organizationCreationRequest the OrganizationCreationRequest object containing the required data
  448. *
  449. * @return Access the newly created instance of the Access class with admin access
  450. */
  451. protected function makeAdminAccess(OrganizationCreationRequest $organizationCreationRequest): Access
  452. {
  453. $admin = new Person();
  454. $admin->setUsername('admin'.strtolower($organizationCreationRequest->getSubdomain()));
  455. $randomString = ByteString::fromRandom(32)->toString();
  456. $admin->setPassword($randomString);
  457. $admin->setEnabled(true);
  458. $adminAccess = new Access();
  459. $adminAccess->setAdminAccess(true);
  460. $adminAccess->setPerson($admin);
  461. $adminAccess->setLoginEnabled(true);
  462. $adminAccess->setRoles(['ROLE_ADMIN', 'ROLE_ADMIN_CORE']);
  463. $adminAccess->setCreateDate($organizationCreationRequest->getCreationDate());
  464. $adminAccess->setCreatedBy($organizationCreationRequest->getAuthorId());
  465. $contactPoint = new ContactPoint();
  466. $contactPoint->setContactType(ContactPointTypeEnum::PRINCIPAL);
  467. $contactPoint->setEmail($organizationCreationRequest->getEmail());
  468. $admin->addContactPoint($contactPoint);
  469. return $adminAccess;
  470. }
  471. /**
  472. * Creates an array of Cycle objects based on a predefined set of data.
  473. *
  474. * @return Cycle[] an array of Cycle objects
  475. */
  476. protected function makeCycles(): array
  477. {
  478. $cyclesData = [
  479. ['Cycle initiation', 10, CycleEnum::INITIATION_CYCLE],
  480. ['Cycle 1', 20, CycleEnum::CYCLE_1],
  481. ['Cycle 2', 30, CycleEnum::CYCLE_2],
  482. ['Cycle 3', 40, CycleEnum::CYCLE_3],
  483. ['Cycle 4', 50, CycleEnum::CYCLE_4],
  484. ['Hors cycle', 60, CycleEnum::OUT_CYCLE],
  485. ];
  486. $cycles = [];
  487. foreach ($cyclesData as $cycleData) {
  488. $cycle = new Cycle();
  489. $cycle->setLabel($cycleData[0]);
  490. $cycle->setOrder($cycleData[1]);
  491. $cycle->setCycleEnum($cycleData[2]);
  492. $cycle->setIsSystem(false);
  493. $cycles[] = $cycle;
  494. }
  495. return $cycles;
  496. }
  497. /**
  498. * Creates an Access object based on the given OrganizationMemberCreationRequest.
  499. *
  500. * @param int|OrganizationMemberCreationRequest $creationRequestData the request object containing the
  501. * necessary data for creating a Person object,
  502. * or the id of an existing one
  503. *
  504. * @return Access the created Access object
  505. *
  506. * @throws NumberParseException
  507. */
  508. protected function makeAccess(
  509. int|OrganizationMemberCreationRequest $creationRequestData,
  510. FunctionEnum $function,
  511. \DateTime $creationDate,
  512. ?int $authorId,
  513. ): Access {
  514. if (is_int($creationRequestData)) {
  515. $person = $this->personRepository->find($creationRequestData);
  516. } else {
  517. $person = new Person();
  518. if ($this->personRepository->findOneBy(['username' => $creationRequestData->getUsername()])) {
  519. throw new \RuntimeException('Username already in use : '.$creationRequestData->getUsername());
  520. }
  521. $person->setUsername($creationRequestData->getUsername());
  522. $person->setPassword(ByteString::fromRandom(32)->toString());
  523. $person->setGender($creationRequestData->getGender());
  524. $person->setName(
  525. ucfirst(strtolower($creationRequestData->getName()))
  526. );
  527. $person->setGivenName(
  528. ucfirst(strtolower($creationRequestData->getGivenName()))
  529. );
  530. $personPostalAddress = $this->makePersonPostalAddress($creationRequestData, $creationDate, $authorId);
  531. $person->addPersonAddressPostal($personPostalAddress);
  532. $contactPoint = $this->makePersonContactPoint($creationRequestData, $creationDate, $authorId);
  533. $person->addContactPoint($contactPoint);
  534. $person->setCreateDate($creationDate);
  535. $person->setCreatedBy($authorId);
  536. }
  537. $access = new Access();
  538. $access->setPerson($person);
  539. $functionType = $this->functionTypeRepository->findOneBy(['mission' => $function]);
  540. $organizationFunction = new OrganizationFunction();
  541. $organizationFunction->setFunctionType($functionType);
  542. $organizationFunction->setStartDate($creationDate);
  543. $organizationFunction->setCreateDate($creationDate);
  544. $organizationFunction->setCreatedBy($authorId);
  545. $access->addOrganizationFunction($organizationFunction);
  546. $access->setCreateDate($creationDate);
  547. $access->setCreatedBy($authorId);
  548. return $access;
  549. }
  550. /**
  551. * Creates a PersonAddressPostal object based on the given OrganizationMemberCreationRequest.
  552. *
  553. * @param OrganizationMemberCreationRequest $organizationMemberCreationRequest the request object containing the
  554. * necessary data for creating a
  555. * PersonAddressPostal object
  556. *
  557. * @return PersonAddressPostal the created PersonAddressPostal object
  558. */
  559. protected function makePersonPostalAddress(
  560. OrganizationMemberCreationRequest $organizationMemberCreationRequest,
  561. \DateTime $creationDate,
  562. ?int $authorId,
  563. ): PersonAddressPostal {
  564. $addressPostal = new AddressPostal();
  565. $addressPostal->setStreetAddress($organizationMemberCreationRequest->getStreetAddress1());
  566. $addressPostal->setStreetAddressSecond($organizationMemberCreationRequest->getStreetAddress2());
  567. $addressPostal->setStreetAddressThird($organizationMemberCreationRequest->getStreetAddress3());
  568. $addressPostal->setPostalCode($organizationMemberCreationRequest->getPostalCode());
  569. $addressPostal->setAddressCity($organizationMemberCreationRequest->getCity());
  570. $addressPostal->setCreateDate($creationDate);
  571. $addressPostal->setCreatedBy($authorId);
  572. $country = $this->countryRepository->find($organizationMemberCreationRequest->getCountryId());
  573. $addressPostal->setAddressCountry($country);
  574. $personAddressPostal = new PersonAddressPostal();
  575. $personAddressPostal->setAddressPostal($addressPostal);
  576. $personAddressPostal->setType(AddressPostalPersonTypeEnum::ADDRESS_PRINCIPAL);
  577. $personAddressPostal->setCreateDate($creationDate);
  578. $personAddressPostal->setCreatedBy($authorId);
  579. return $personAddressPostal;
  580. }
  581. /**
  582. * Creates a new instance of the ContactPoint class based on the given OrganizationCreationRequest object.
  583. *
  584. * @param OrganizationMemberCreationRequest $organizationMemberCreationRequest the OrganizationMemberCreationRequest object containing the required data
  585. *
  586. * @return ContactPoint the newly created instance of the ContactPoint class
  587. *
  588. * @throws NumberParseException
  589. */
  590. protected function makePersonContactPoint(
  591. OrganizationMemberCreationRequest $organizationMemberCreationRequest,
  592. \DateTime $creationDate,
  593. ?int $authorId,
  594. ): ContactPoint {
  595. if (!$this->phoneNumberUtil->isPossibleNumber($organizationMemberCreationRequest->getPhone())) {
  596. throw new \RuntimeException('Phone number is invalid or missing (person: '.$organizationMemberCreationRequest->getUsername().')');
  597. }
  598. if (
  599. $organizationMemberCreationRequest->getMobile() !== null
  600. && !$this->phoneNumberUtil->isPossibleNumber($organizationMemberCreationRequest->getMobile())) {
  601. throw new \RuntimeException('Mobile phone number is invalid (person: '.$organizationMemberCreationRequest->getUsername().')');
  602. }
  603. $phoneNumber = $this->phoneNumberUtil->parse($organizationMemberCreationRequest->getPhone());
  604. $contactPoint = new ContactPoint();
  605. $contactPoint->setContactType(ContactPointTypeEnum::PRINCIPAL);
  606. $contactPoint->setEmail($organizationMemberCreationRequest->getEmail());
  607. $contactPoint->setTelphone($phoneNumber);
  608. if ($organizationMemberCreationRequest->getMobile() !== null) {
  609. $mobileNumber = $this->phoneNumberUtil->parse($organizationMemberCreationRequest->getMobile());
  610. $contactPoint->setMobilPhone($mobileNumber);
  611. }
  612. $contactPoint->setCreateDate($creationDate);
  613. $contactPoint->setCreatedBy($authorId);
  614. return $contactPoint;
  615. }
  616. protected function makeSubdomain(OrganizationCreationRequest $organizationCreationRequest): Subdomain
  617. {
  618. $subdomain = new Subdomain();
  619. $subdomain->setSubdomain($organizationCreationRequest->getSubdomain());
  620. $subdomain->setActive(true);
  621. return $subdomain;
  622. }
  623. /**
  624. * Créé le site Typo3 et retourne l'id de la page racine du site nouvellement créé, ou null en cas d'erreur.
  625. *
  626. * @throws RedirectionExceptionInterface
  627. * @throws ClientExceptionInterface
  628. * @throws TransportExceptionInterface
  629. * @throws ServerExceptionInterface
  630. */
  631. protected function createTypo3Website(Organization $organization): ?int
  632. {
  633. $response = $this->typo3Service->createSite($organization->getId());
  634. $content = json_decode($response->getContent(), true);
  635. $rootPageUid = $content['root_uid'];
  636. if ($response->getStatusCode() === Response::HTTP_OK && $rootPageUid > 0) {
  637. // TODO: revoir l'utilité du champs cmsId
  638. $organization->setCmsId($rootPageUid);
  639. $this->entityManager->persist($organization);
  640. $this->entityManager->flush();
  641. return $rootPageUid;
  642. } else {
  643. $this->logger->critical("/!\ A critical error happened while creating the Typo3 website");
  644. $this->logger->debug($response->getContent());
  645. }
  646. return null;
  647. }
  648. protected function updateAdminassosDb(Organization $organization): void
  649. {
  650. $response = $this->apiLegacyRequestService->post(
  651. '/_internal/secure/organization/creation-event',
  652. ['organizationId' => $organization->getId()]
  653. );
  654. if ($response->getStatusCode() !== Response::HTTP_OK) {
  655. throw new \RuntimeException('An error happened while updating the adminassos database: '.$response->getContent());
  656. }
  657. }
  658. /**
  659. * Normalise la chaine comme sont normalisées les champs de l'entité OrganizationIdentification.
  660. *
  661. * @øee sql/schema-extensions/003-view_organization_identification.sql
  662. */
  663. protected function normalizeIdentificationField(string $value): string
  664. {
  665. $value = strtolower(trim($value));
  666. $value = preg_replace('/[éèê]/u', 'e', $value);
  667. $value = preg_replace('/[à]/u', 'a', $value);
  668. $value = preg_replace('/[ç]/u', 'c', $value);
  669. return preg_replace('/[^a-z0-9]+/u', '+', $value);
  670. }
  671. }