瀏覽代碼

Merge branch 'feature/V8-6599-prevent-organizations-duplicates' into feature/V8-6489-dvelopper-loutil-de-cration-des-

Olivier Massot 1 年之前
父節點
當前提交
83372e296e

+ 11 - 0
sql/schema-extensions/003-view_organization_identification.sql

@@ -0,0 +1,11 @@
+CREATE OR REPLACE VIEW view_organization_identification AS
+    SELECT o.id, o.name, REGEXP_REPLACE(LOWER(o.name), '[^a-z0-9]+', '+') AS normalizedName,
+       o.identifier, o.siretNumber, o.waldecNumber,
+       CONCAT(a.streetAddress, ' ', a.streetAddressSecond, ' ', a.streetAddressThird) AS address,
+       a.addressCity AS city, a.postalCode, c.email, c.telphone
+    FROM opentalent.Organization o
+        INNER JOIN opentalent.OrganizationAddressPostal oa ON oa.organization_id = o.id
+        INNER JOIN opentalent.AddressPostal a ON a.id = oa.addressPostal_id
+        INNER JOIN opentalent.organization_contactpoint oc ON oc.organization_id = o.id
+        INNER JOIN opentalent.ContactPoint c ON oc.contactPoint_id = c.id
+    WHERE oa.type='ADDRESS_HEAD_OFFICE' AND c.contactType='PRINCIPAL';

+ 29 - 0
src/ApiResources/Organization/OrganizationCreationRequest.php

@@ -51,8 +51,15 @@ class OrganizationCreationRequest
      * Matricule (obligatoire dans le réseau CMF)
      * @var string
      */
+    #[Assert\Regex(pattern: '/^|(FR\d{12})$/')]
     private string $identifier = '';
 
+    #[Assert\Regex(pattern: '/^|(\d+)$/')]
+    private string $siretNumber = '';
+
+    #[Assert\Regex(pattern: '/^|(W\d+)$/')]
+    private string $waldecNumber = '';
+
     private ?LegalEnum $legalStatus = null;
 
     private SettingsProductEnum $product;
@@ -188,6 +195,28 @@ class OrganizationCreationRequest
         return $this;
     }
 
+    public function getSiretNumber(): string
+    {
+        return $this->siretNumber;
+    }
+
+    public function setSiretNumber(string $siretNumber): self
+    {
+        $this->siretNumber = $siretNumber;
+        return $this;
+    }
+
+    public function getWaldecNumber(): string
+    {
+        return $this->waldecNumber;
+    }
+
+    public function setWaldecNumber(string $waldecNumber): self
+    {
+        $this->waldecNumber = $waldecNumber;
+        return $this;
+    }
+
     public function getLegalStatus(): LegalEnum
     {
         return $this->legalStatus;

+ 183 - 0
src/Entity/Organization/OrganizationIdentification.php

@@ -0,0 +1,183 @@
+<?php
+declare(strict_types=1);
+
+namespace App\Entity\Organization;
+
+use ApiPlatform\Metadata\ApiResource;
+use ApiPlatform\Metadata\Get;
+use App\Repository\Organization\OrganizationIdentificationRepository;
+use Doctrine\ORM\Mapping as ORM;
+
+/**
+ * Cette entité regroupe les informations permettant d'identifier une organisation, et est utilisée entre autre
+ * pour la création d'organisation
+ *
+ * Fichier source de la view : ./sql/schema-extensions/003-view_organization_identification.sql
+ */
+#[ApiResource(
+    operations: [
+        new Get(
+            uriTemplate: '/public/organization_identification/{id}',
+            requirements: ['id' => '\\d+']
+        )
+    ]
+)]
+#[ORM\Table(name: 'view_organization_identification')]
+#[ORM\Entity(repositoryClass: OrganizationIdentificationRepository::class, readOnly: true)]
+class OrganizationIdentification
+{
+    #[ORM\Id]
+    #[ORM\Column]
+    private int $id;
+
+    #[ORM\Column]
+    private string $name;
+
+    #[ORM\Column]
+    private string $normalizedName;
+
+    #[ORM\Column]
+    private ?string $identifier;
+
+    #[ORM\Column]
+    private ?string $siretNumber;
+
+    #[ORM\Column]
+    private ?string $waldecNumber;
+
+    #[ORM\Column]
+    private ?string $address;
+
+    #[ORM\Column]
+    private ?string $city;
+
+    #[ORM\Column]
+    private ?string $postalCode;
+
+    #[ORM\Column]
+    private ?string $email;
+
+    #[ORM\Column]
+    private ?string $telphone;
+
+    public function getId(): int
+    {
+        return $this->id;
+    }
+
+    public function setId(int $id): self
+    {
+        $this->id = $id;
+        return $this;
+    }
+
+    public function getName(): string
+    {
+        return $this->name;
+    }
+
+    public function setName(string $name): self
+    {
+        $this->name = $name;
+        return $this;
+    }
+
+    public function getNormalizedName(): string
+    {
+        return $this->normalizedName;
+    }
+
+    public function setNormalizedName(string $normalizedName): self
+    {
+        $this->normalizedName = $normalizedName;
+        return $this;
+    }
+
+    public function getIdentifier(): ?string
+    {
+        return $this->identifier;
+    }
+
+    public function setIdentifier(?string $identifier): self
+    {
+        $this->identifier = $identifier;
+        return $this;
+    }
+
+    public function getSiretNumber(): ?string
+    {
+        return $this->siretNumber;
+    }
+
+    public function setSiretNumber(?string $siretNumber): self
+    {
+        $this->siretNumber = $siretNumber;
+        return $this;
+    }
+
+    public function getWaldecNumber(): ?string
+    {
+        return $this->waldecNumber;
+    }
+
+    public function setWaldecNumber(?string $waldecNumber): self
+    {
+        $this->waldecNumber = $waldecNumber;
+        return $this;
+    }
+
+    public function getAddress(): ?string
+    {
+        return $this->address;
+    }
+
+    public function setAddress(?string $address): self
+    {
+        $this->address = $address;
+        return $this;
+    }
+
+    public function getCity(): ?string
+    {
+        return $this->city;
+    }
+
+    public function setCity(?string $city): self
+    {
+        $this->city = $city;
+        return $this;
+    }
+
+    public function getPostalCode(): ?string
+    {
+        return $this->postalCode;
+    }
+
+    public function setPostalCode(?string $postalCode): self
+    {
+        $this->postalCode = $postalCode;
+        return $this;
+    }
+
+    public function getEmail(): ?string
+    {
+        return $this->email;
+    }
+
+    public function setEmail(?string $email): self
+    {
+        $this->email = $email;
+        return $this;
+    }
+
+    public function getTelphone(): ?string
+    {
+        return $this->telphone;
+    }
+
+    public function setTelphone(?string $telphone): self
+    {
+        $this->telphone = $telphone;
+        return $this;
+    }
+}

+ 21 - 0
src/Repository/Organization/OrganizationIdentificationRepository.php

@@ -0,0 +1,21 @@
+<?php
+
+declare(strict_types=1);
+
+namespace App\Repository\Organization;
+
+use App\Entity\Organization\OrganizationIdentification;
+use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
+use Doctrine\Persistence\ManagerRegistry;
+
+/**
+ * @method OrganizationIdentification|null find($id, $lockMode = null, $lockVersion = null)
+ * @method OrganizationIdentification|null findOneBy(array $criteria, array $orderBy = null)
+ */
+final class OrganizationIdentificationRepository extends ServiceEntityRepository
+{
+    public function __construct(ManagerRegistry $registry)
+    {
+        parent::__construct($registry, OrganizationIdentification::class);
+    }
+}

+ 81 - 20
src/Service/Organization/OrganizationFactory.php

@@ -23,6 +23,7 @@ use App\Enum\Organization\AddressPostalOrganizationTypeEnum;
 use App\Enum\Organization\SettingsProductEnum;
 use App\Enum\Person\AddressPostalPersonTypeEnum;
 use App\Repository\Core\CountryRepository;
+use App\Repository\Organization\OrganizationIdentificationRepository;
 use App\Repository\Organization\OrganizationRepository;
 use App\Repository\Person\PersonRepository;
 use App\Service\Dolibarr\DolibarrApiService;
@@ -50,15 +51,16 @@ class OrganizationFactory
     private LoggerInterface $logger;
 
     public function __construct(
-        private readonly SubdomainService       $subdomainService,
-        private readonly OrganizationRepository $organizationRepository,
-        private readonly CountryRepository      $countryRepository,
-        private readonly OrganizationUtils      $organizationUtils,
-        private readonly Typo3Service           $typo3Service,
-        private readonly DolibarrApiService     $dolibarrApiService,
-        private readonly EntityManagerInterface $entityManager,
-        private readonly PersonRepository       $personRepository,
-        private readonly BindFileService        $bindFileService,
+        private readonly SubdomainService                     $subdomainService,
+        private readonly OrganizationRepository               $organizationRepository,
+        private readonly CountryRepository                    $countryRepository,
+        private readonly OrganizationUtils                    $organizationUtils,
+        private readonly Typo3Service                         $typo3Service,
+        private readonly DolibarrApiService                   $dolibarrApiService,
+        private readonly EntityManagerInterface               $entityManager,
+        private readonly PersonRepository                     $personRepository,
+        private readonly BindFileService                      $bindFileService,
+        private readonly OrganizationIdentificationRepository $organizationIdentificationRepository
     ) {}
 
     #[Required]
@@ -86,9 +88,7 @@ class OrganizationFactory
 
         try {
             // On vérifie si cette organisation n'existe pas déjà
-            if ($this->isExistingOrganization($organizationCreationRequest)) {
-                throw new \RuntimeException('An organization named ' . $organizationCreationRequest->getName() . ' already exists');
-            }
+            $this->interruptIfOrganizationExists($organizationCreationRequest);
 
             // On vérifie la validité et la disponibilité du sous domaine
             $this->validateSubdomain($organizationCreationRequest->getSubdomain());
@@ -163,20 +163,81 @@ class OrganizationFactory
     }
 
     /**
-     * Une organisation du même nom existe-t-elle déjà à la même adresse ?
+     * Lève une exception si cette organisation existe déjà
      *
      * @param OrganizationCreationRequest $organizationCreationRequest
-     * @return bool
      */
-    protected function isExistingOrganization(OrganizationCreationRequest $organizationCreationRequest): bool
+    protected function interruptIfOrganizationExists(OrganizationCreationRequest $organizationCreationRequest): void
     {
-        return $this
-            ->organizationRepository
-            ->count(
+        if (
+            $organizationCreationRequest->getSiretNumber() &&
+            $this->organizationIdentificationRepository->findOneBy(
+                ['siretNumber' => $organizationCreationRequest->getSiretNumber()]
+            )
+        ) {
+            throw new \RuntimeException(
+                "This siret number is already registered : '" . $organizationCreationRequest->getSiretNumber()
+            );
+        }
+
+        if (
+            $organizationCreationRequest->getWaldecNumber() &&
+            $this->organizationIdentificationRepository->findOneBy(
+                ['waldecNumber' => $organizationCreationRequest->getWaldecNumber()]
+            )
+        ) {
+            throw new \RuntimeException(
+                "This RNA identifier (waldec number) is already registered : '" . $organizationCreationRequest->getWaldecNumber()
+            );
+        }
+
+        if (
+            $organizationCreationRequest->getIdentifier() &&
+            $this->organizationIdentificationRepository->findOneBy(
+                ['identifier' => $organizationCreationRequest->getIdentifier()]
+            )
+        ) {
+            throw new \RuntimeException(
+                "This CMF identifier is already registered : '" . $organizationCreationRequest->getIdentifier()
+            );
+        }
+
+        $normalizedName = preg_replace(
+            "/[^a-z0-9]+/",
+            "+",
+            strtolower($organizationCreationRequest->getName())
+        );
+
+        if (
+            $this->organizationIdentificationRepository->findOneBy(
+                ['normalizedName' => $normalizedName, 'city' => $organizationCreationRequest->getCity()]
+            )
+        ) {
+            throw new \RuntimeException(
+                "An organization named '" . $organizationCreationRequest->getName() .
+                "' already exists in " . $organizationCreationRequest->getCity()
+            );
+        }
+
+        $address = implode(' ', [
+            $organizationCreationRequest->getStreetAddress1(),
+            $organizationCreationRequest->getStreetAddress2(),
+            $organizationCreationRequest->getStreetAddress3()
+        ]);
+
+        if (
+            $this->organizationIdentificationRepository->findOneBy(
                 [
-                    'name' => $organizationCreationRequest->getName(),
+                    'address' => $address,
+                    'city' => $organizationCreationRequest->getCity(),
+                    'postalCode' => $organizationCreationRequest->getPostalCode()
                 ]
-            ) > 0;
+            )
+        ) {
+            throw new \RuntimeException(
+                'An organization already exists at this address.'
+            );
+        }
     }
 
     /**