浏览代码

improve validation, various fixes

Olivier Massot 1 年之前
父节点
当前提交
80772c3c3e

+ 46 - 2
src/ApiResources/Organization/OrganizationCreationRequest.php

@@ -9,7 +9,11 @@ use App\Enum\Organization\LegalEnum;
 use App\Enum\Organization\PrincipalTypeEnum;
 use App\Enum\Organization\SettingsProductEnum;
 use App\State\Processor\Organization\OrganizationCreationRequestProcessor;
+use Symfony\Component\Validator\Constraints as Assert;
 
+/**
+ * Requête de création d'une nouvelle organisation
+ */
 #[ApiResource(
     operations: [
         new Post(
@@ -34,34 +38,74 @@ class OrganizationCreationRequest
 
     private SettingsProductEnum $product;
 
+    #[Assert\Length(
+        min: 1,
+        minMessage: 'Street address must be at least {{ limit }} characters long',
+    )]
     private string $streetAddress1;
 
     private ?string $streetAddress2 = null;
 
     private ?string $streetAddress3 = null;
 
+    #[Assert\Length(
+        min: 3,
+        minMessage: 'Postal code must be at least {{ limit }} characters long',
+    )]
     private string $postalCode;
 
+    #[Assert\Length(
+        min: 1,
+        minMessage: 'City must be at least {{ limit }} characters long',
+    )]
     private string $city;
 
     private int $countryId = 72; // France's id
 
+    #[Assert\Length(
+        min: 10,
+        minMessage: 'Phone number must be at least {{ limit }} characters long',
+    )]
     private string $phoneNumber;
 
+    #[Assert\Length(
+        min: 5,
+        minMessage: 'Email must be at least {{ limit }} characters long',
+    )]
     private string $email;
 
+    #[Assert\Length(
+        min: 2,
+        minMessage: 'Subdomain must be at least {{ limit }} characters long',
+    )]
     private string $subdomain;
 
+    #[Assert\Positive]
     private int $parentId;
 
+    #[Assert\Positive]
     private int $networkId;
 
     private PrincipalTypeEnum $principalType;
 
-    private ?OrganizationMemberCreationRequest $president = null;
+    /**
+     * Id d'une Person existante ou requête de création d'un nouvel access qui aura le
+     * rôle de président(e) de la nouvelle structure.
+     * @var int|OrganizationMemberCreationRequest|null
+     */
+    private int|OrganizationMemberCreationRequest|null $president = null;
 
-    private ?OrganizationMemberCreationRequest $director = null;
+    /**
+     * Id d'une Person existante ou requête de création d'un nouvel access qui aura le
+     * rôle de directeur / directrice de la nouvelle structure.
+     * @var int|OrganizationMemberCreationRequest|null
+     */
+    private int|OrganizationMemberCreationRequest|null $director = null;
 
+    /**
+     * Faut-il créer un site typo3 pour la nouvelle structure
+     * @var bool
+     */
     private bool $createWebsite = true;
 
     public function getId(): int

+ 45 - 9
src/ApiResources/Organization/OrganizationMemberCreationRequest.php

@@ -4,31 +4,67 @@ namespace App\ApiResources\Organization;
 
 
 use App\Enum\Person\GenderEnum;
+use Symfony\Component\Validator\Constraints as Assert;
 
+/**
+ * Requête de création d'un nouvel access
+ */
 class OrganizationMemberCreationRequest
 {
     private GenderEnum $gender;
 
+    #[Assert\Length(
+        min: 1,
+        minMessage: 'Name must be at least {{ limit }} characters long',
+    )]
     private string $name;
 
+    #[Assert\Length(
+        min: 1,
+        minMessage: 'Given name must be at least {{ limit }} characters long',
+    )]
     private string $givenName;
 
+    #[Assert\Length(
+        min: 1,
+        minMessage: 'Street address must be at least {{ limit }} characters long',
+    )]
     private string $streetAddress1;
 
-    private string $streetAddress2;
+    private ?string $streetAddress2 = null;
 
-    private string $streetAddress3;
+    private ?string $streetAddress3 = null;
 
+    #[Assert\Length(
+        min: 3,
+        minMessage: 'Postal code must be at least {{ limit }} characters long',
+    )]
     private string $postalCode;
 
+    #[Assert\Length(
+        min: 1,
+        minMessage: 'City must be at least {{ limit }} characters long',
+    )]
     private string $city;
 
     private int $countryId = 72; // France's id
 
+    #[Assert\Length(
+        min: 10,
+        minMessage: 'Phone number must be at least {{ limit }} characters long',
+    )]
     private string $phone;
 
-    private string $mobile;
+    #[Assert\Length(
+        min: 10,
+        minMessage: 'Mobile phone number must be at least {{ limit }} characters long',
+    )]
+    private ?string $mobile = null;
 
+    #[Assert\Length(
+        min: 5,
+        minMessage: 'Email must be at least {{ limit }} characters long',
+    )]
     private string $email;
 
     public function getGender(): GenderEnum
@@ -75,23 +111,23 @@ class OrganizationMemberCreationRequest
         return $this;
     }
 
-    public function getStreetAddress2(): string
+    public function getStreetAddress2(): ?string
     {
         return $this->streetAddress2;
     }
 
-    public function setStreetAddress2(string $streetAddress2): self
+    public function setStreetAddress2(?string $streetAddress2): self
     {
         $this->streetAddress2 = $streetAddress2;
         return $this;
     }
 
-    public function getStreetAddress3(): string
+    public function getStreetAddress3(): ?string
     {
         return $this->streetAddress3;
     }
 
-    public function setStreetAddress3(string $streetAddress3): self
+    public function setStreetAddress3(?string $streetAddress3): self
     {
         $this->streetAddress3 = $streetAddress3;
         return $this;
@@ -141,12 +177,12 @@ class OrganizationMemberCreationRequest
         return $this;
     }
 
-    public function getMobile(): string
+    public function getMobile(): ?string
     {
         return $this->mobile;
     }
 
-    public function setMobile(string $mobile): self
+    public function setMobile(?string $mobile): self
     {
         $this->mobile = $mobile;
         return $this;

+ 139 - 39
src/Service/Organization/OrganizationFactory.php

@@ -14,18 +14,22 @@ use App\Entity\Organization\OrganizationAddressPostal;
 use App\Entity\Organization\Parameters;
 use App\Entity\Organization\Settings;
 use App\Entity\Person\Person;
+use App\Entity\Person\PersonAddressPostal;
 use App\Enum\Core\ContactPointTypeEnum;
 use App\Enum\Education\CycleEnum;
 use App\Enum\Organization\AddressPostalOrganizationTypeEnum;
+use App\Enum\Person\AddressPostalPersonTypeEnum;
 use App\Repository\Core\CountryRepository;
 use App\Repository\Network\NetworkRepository;
 use App\Repository\Organization\OrganizationRepository;
+use App\Repository\Person\PersonRepository;
 use App\Service\Dolibarr\DolibarrApiService;
 use App\Service\Typo3\SubdomainService;
 use App\Service\Typo3\Typo3Service;
 use App\Service\Utils\DatesUtils;
 use Doctrine\ORM\EntityManagerInterface;
 use Elastica\Param;
+use libphonenumber\NumberParseException;
 use libphonenumber\PhoneNumberUtil;
 use Psr\Log\LoggerInterface;
 use Symfony\Component\String\ByteString;
@@ -38,13 +42,14 @@ class OrganizationFactory
     private LoggerInterface $logger;
 
     public function __construct(
-        private readonly SubdomainService $subdomainService,
+        private readonly SubdomainService       $subdomainService,
         private readonly OrganizationRepository $organizationRepository,
-        private readonly CountryRepository $countryRepository,
-        private readonly NetworkRepository $networkRepository,
-        private readonly Typo3Service $typo3Service,
-        private readonly DolibarrApiService $dolibarrApiService,
-        private readonly EntityManagerInterface $entityManager
+        private readonly CountryRepository      $countryRepository,
+        private readonly NetworkRepository      $networkRepository,
+        private readonly Typo3Service           $typo3Service,
+        private readonly DolibarrApiService     $dolibarrApiService,
+        private readonly EntityManagerInterface $entityManager,
+        private readonly PersonRepository       $personRepository
     ) {}
 
     #[Required]
@@ -68,59 +73,53 @@ class OrganizationFactory
             'Start the creation of a new organization named ' . $organizationCreationRequest->getName()
         );
 
-        $this->validateSubdomain($organizationCreationRequest->getSubdomain());
-        $this->logger->info("Subdomain is valid and available : " . $organizationCreationRequest->getSubdomain());
-
         $this->entityManager->beginTransaction();
 
         try {
+            if ($this->isExistingOrganization($organizationCreationRequest)) {
+                throw new \RuntimeException('An organization named ' . $organizationCreationRequest->getName() . ' already exists at this address.');
+            }
+
+            $this->validateSubdomain($organizationCreationRequest->getSubdomain());
+            $this->logger->info("Subdomain is valid and available : " . $organizationCreationRequest->getSubdomain());
+
             // Création de l'organisation
             $organization = $this->makeOrganization($organizationCreationRequest);
-            $this->entityManager->persist($organization);
             $this->logger->debug(" - Organization created");
 
             // Création des Parameters
             $parameters = $this->makeParameters($organizationCreationRequest);
             $organization->setParameters($parameters);
-            $parameters->setOrganization($organization);
-            $this->entityManager->persist($parameters);
             $this->logger->debug(" - Parameters created");
 
             // Création des Settings
             $settings = $this->makeSettings($organizationCreationRequest);
             $organization->setSettings($settings);
-            $this->entityManager->persist($settings);
             $this->logger->debug(" - Settings created");
 
             // Création de l'adresse postale
             $organizationAddressPostal = $this->makePostalAddress($organizationCreationRequest);
             $organization->addOrganizationAddressPostal($organizationAddressPostal);
-            $this->entityManager->persist($organizationAddressPostal);
             $this->logger->debug(" - OrganizationAddressPostal created");
 
             // Création du point de contact
             $contactPoint = $this->makeContactPoint($organizationCreationRequest);
             $organization->addContactPoint($contactPoint);
-            $this->entityManager->persist($contactPoint);
             $this->logger->debug(" - ContactPoint created");
 
             // Rattachement au réseau
             $networkOrganization = $this->makeNetworkOrganization($organizationCreationRequest);
             $organization->addNetworkOrganization($networkOrganization);
-            $this->entityManager->persist($networkOrganization);
             $this->logger->debug(" - NetworkOrganization created");
 
             // Créé l'admin
             $adminAccess = $this->makeAdminAccess($organizationCreationRequest);
-            $adminAccess->setOrganization($organization);
-            $this->entityManager->persist($adminAccess);
+            $organization->addAccess($adminAccess);
             $this->logger->debug(" - Admin access created");
 
             // Création des cycles
             foreach ($this->makeCycles() as $cycle) {
-                $cycle->setOrganization($organization);
                 $organization->addCycle($cycle);
-                $this->entityManager->persist($cycle);
             }
             $this->logger->debug(" - Cycles created");
 
@@ -129,7 +128,6 @@ class OrganizationFactory
             if ($presidentCreationRequest !== null) {
                 $presidentAccess = $this->makeAccess($presidentCreationRequest);
                 $organization->addAccess($presidentAccess);
-                $this->entityManager->persist($presidentAccess);
                 $this->logger->debug(" - President access created");
             }
 
@@ -138,12 +136,13 @@ class OrganizationFactory
             if ($directorCreationRequest !== null) {
                 $directorAccess = $this->makeAccess($directorCreationRequest);
                 $organization->addAccess($directorAccess);
-                $this->entityManager->persist($directorAccess);
                 $this->logger->debug(" - Director access created");
             }
 
+            $this->entityManager->persist($organization);
             $this->entityManager->flush();
             $this->entityManager->commit();
+
             $this->logger->debug(" - New records commited");
             $this->logger->info("Organization created in the DB");
 
@@ -182,6 +181,27 @@ class OrganizationFactory
         return $organization;
     }
 
+    /**
+     * Une organisation du même nom existe-t-elle déjà à la même adresse?
+     *
+     * @param OrganizationCreationRequest $organizationCreationRequest
+     * @return bool
+     */
+    protected function isExistingOrganization(OrganizationCreationRequest $organizationCreationRequest): bool
+    {
+        $results = $this
+            -> organizationRepository
+            ->findBy(
+                [
+                    'name' => $organizationCreationRequest->getName(),
+                    'streetAddress' => $organizationCreationRequest->getStreetAddress1(),
+                    'postalCode' => $organizationCreationRequest->getPostalCode()
+                ]
+            );
+
+        return count($results) > 0;
+    }
+
     /**
      * Vérifie la disponibilité et la validité d'un sous domaine
      *
@@ -217,6 +237,8 @@ class OrganizationFactory
         $organization->setName($organizationCreationRequest->getName());
         $organization->setLegalStatus($organizationCreationRequest->getLegalStatus());
 
+        $this->entityManager->persist($organization);
+
         return $organization;
     }
 
@@ -229,7 +251,9 @@ class OrganizationFactory
      */
     protected function makeParameters(OrganizationCreationRequest $organizationCreationRequest): Parameters
     {
-        return new Parameters();
+        $parameters = new Parameters();
+        $this->entityManager->persist($parameters);
+        return $parameters;
     }
 
     /**
@@ -243,7 +267,7 @@ class OrganizationFactory
     {
         $settings = new Settings();
         $settings->setProduct($organizationCreationRequest->getProduct());
-
+        $this->entityManager->persist($settings);
         return $settings;
     }
 
@@ -262,13 +286,14 @@ class OrganizationFactory
         $addressPostal->setStreetAddressThird($organizationCreationRequest->getStreetAddress3());
         $addressPostal->setPostalCode($organizationCreationRequest->getPostalCode());
         $addressPostal->setAddressCity($organizationCreationRequest->getCity());
-
         $country = $this->countryRepository->find($organizationCreationRequest->getCountryId());
         $addressPostal->setAddressCountry($country);
+        $this->entityManager->persist($addressPostal);
 
         $organizationAddressPostal = new OrganizationAddressPostal();
         $organizationAddressPostal->setAddressPostal($addressPostal);
         $organizationAddressPostal->setType(AddressPostalOrganizationTypeEnum::ADDRESS_HEAD_OFFICE);
+        $this->entityManager->persist($organizationAddressPostal);
 
         return $organizationAddressPostal;
     }
@@ -289,6 +314,7 @@ class OrganizationFactory
         $contactPoint->setContactType(ContactPointTypeEnum::PRINCIPAL);
         $contactPoint->setEmail($organizationCreationRequest->getEmail());
         $contactPoint->setTelphone($phoneNumber);
+        $this->entityManager->persist($contactPoint);
 
         return $contactPoint;
     }
@@ -300,7 +326,7 @@ class OrganizationFactory
      *
      * @return NetworkOrganization The newly created instance of the NetworkOrganization class.
      *
-     * @throws \RuntimeException if no parent organization is found for the given parent ID or if no network is found for the given network ID.
+     * @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.
      */
     protected function makeNetworkOrganization(OrganizationCreationRequest $organizationCreationRequest): NetworkOrganization
     {
@@ -318,6 +344,7 @@ class OrganizationFactory
         $networkOrganization->setParent($parent);
         $networkOrganization->setNetwork($network);
         $networkOrganization->setStartDate(DatesUtils::new());
+        $this->entityManager->persist($networkOrganization);
 
         return $networkOrganization;
     }
@@ -335,10 +362,12 @@ class OrganizationFactory
         $admin->setUsername('admin' . strtolower($organizationCreationRequest->getSubdomain()));
         $randomString = ByteString::fromRandom(32)->toString();
         $admin->setPassword($randomString);
+        $this->entityManager->persist($admin);
 
         $adminAccess = new Access();
         $adminAccess->setAdminAccess(true);
         $adminAccess->setPerson($admin);
+        $this->entityManager->persist($adminAccess);
 
         return $adminAccess;
     }
@@ -367,6 +396,7 @@ class OrganizationFactory
             $cycle->setOrder($cycleData[1]);
             $cycle->setCycleEnum($cycleData[2]);
             $cycle->setIsSystem(false);
+            $this->entityManager->persist($cycle);
             $cycles[] = $cycle;
         }
 
@@ -376,27 +406,97 @@ class OrganizationFactory
     /**
      * Creates an Access object based on the given OrganizationMemberCreationRequest.
      *
-     * @param OrganizationMemberCreationRequest $organizationMemberCreationRequest The request object containing the necessary data for creating an Access object.
+     * @param int|OrganizationMemberCreationRequest $organizationMemberCreationRequest The request object containing the
+     *                                                                                 necessary data for creating a
+     *                                                                                 Person object, or the id of an
+     *                                                                                 existing one.
      * @return Access The created Access object.
      */
-    protected function makeAccess(OrganizationMemberCreationRequest $organizationMemberCreationRequest): Access
+    protected function makeAccess(int|OrganizationMemberCreationRequest $organizationMemberCreationRequest): Access
     {
-        $president = new Person();
+        if (is_int($organizationMemberCreationRequest)) {
+            $person = $this->personRepository->find($organizationMemberCreationRequest);
+        } else {
+            $person = new Person();
 
-        $president->setUsername(
-            strtolower(str_replace(' ', '-', $organizationMemberCreationRequest->getName()))
-        );
-        $president->setPassword(ByteString::fromRandom(32)->toString());
+            $person->setUsername(
+                strtolower(str_replace(' ', '-', $organizationMemberCreationRequest->getName()))
+            );
+            $person->setPassword(ByteString::fromRandom(32)->toString());
 
-        $president->setGender($organizationMemberCreationRequest->getGender());
-        $president->setName($organizationMemberCreationRequest->getName());
-        $president->setGivenName($organizationMemberCreationRequest->getGivenName());
+            $person->setGender($organizationMemberCreationRequest->getGender());
+            $person->setName($organizationMemberCreationRequest->getName());
+            $person->setGivenName($organizationMemberCreationRequest->getGivenName());
 
-        $presidentAccess = new Access();
-        $presidentAccess->setPerson($president);
+            $personPostalAddress = $this->makeAccessPostalAddress($organizationMemberCreationRequest);
+            $person->addPersonAddressPostal($personPostalAddress);
 
-        return $presidentAccess;
+            $contactPoint = $this->makeAccessContactPoint($organizationMemberCreationRequest);
+            $person->addContactPoint($contactPoint);
+
+            $this->entityManager->persist($person);
+        }
+
+        $access = new Access();
+        $access->setPerson($person);
+        $this->entityManager->persist($access);
+
+        return $access;
     }
 
+    /**
+     * Creates a PersonAddressPostal object based on the given OrganizationMemberCreationRequest.
+     *
+     * @param OrganizationMemberCreationRequest $organizationMemberCreationRequest The request object containing the
+     *                                                                             necessary data for creating a
+     *                                                                             PersonAddressPostal object.
+     * @return PersonAddressPostal The created PersonAddressPostal object.
+     */
+    protected function makeAccessPostalAddress(OrganizationMemberCreationRequest $organizationMemberCreationRequest): PersonAddressPostal
+    {
+        $addressPostal = new AddressPostal();
+        $addressPostal->setStreetAddress($organizationMemberCreationRequest->getStreetAddress1());
+        $addressPostal->setStreetAddressSecond($organizationMemberCreationRequest->getStreetAddress2());
+        $addressPostal->setStreetAddressThird($organizationMemberCreationRequest->getStreetAddress3());
+        $addressPostal->setPostalCode($organizationMemberCreationRequest->getPostalCode());
+        $addressPostal->setAddressCity($organizationMemberCreationRequest->getCity());
+
+        $country = $this->countryRepository->find($organizationMemberCreationRequest->getCountryId());
+        $addressPostal->setAddressCountry($country);
+        $this->entityManager->persist($addressPostal);
+
+        $personAddressPostal = new PersonAddressPostal();
+        $personAddressPostal->setAddressPostal($addressPostal);
+        $personAddressPostal->setType(AddressPostalPersonTypeEnum::ADDRESS_PRINCIPAL);
+        $this->entityManager->persist($personAddressPostal);
+
+        return $personAddressPostal;
+    }
+
+    /**
+     * Creates a new instance of the ContactPoint class based on the given OrganizationCreationRequest object.
+     *
+     * @param OrganizationMemberCreationRequest $organizationMemberCreationRequest The OrganizationMemberCreationRequest object containing the required data.
+     *
+     * @return ContactPoint The newly created instance of the ContactPoint class.
+     * @throws NumberParseException
+     */
+    protected function makeAccessContactPoint(OrganizationMemberCreationRequest $organizationMemberCreationRequest): ContactPoint
+    {
+        $phoneUtil = PhoneNumberUtil::getInstance();
+        $phoneNumber = $phoneUtil->parse($organizationMemberCreationRequest->getPhone());
+
+        $contactPoint = new ContactPoint();
+        $contactPoint->setContactType(ContactPointTypeEnum::PRINCIPAL);
+        $contactPoint->setEmail($organizationMemberCreationRequest->getEmail());
+        $contactPoint->setTelphone($phoneNumber);
 
+        if ($organizationMemberCreationRequest->getMobile() !== null) {
+            $mobileNumber = $phoneUtil->parse($organizationMemberCreationRequest->getMobile());
+            $contactPoint->setMobilPhone($mobileNumber);
+        }
+        
+        $this->entityManager->persist($contactPoint);
+        return $contactPoint;
+    }
 }