Forráskód Böngészése

menu site internet

Vincent GUFFON 4 éve
szülő
commit
1110ac1fdf
33 módosított fájl, 1098 hozzáadás és 197 törlés
  1. 1 1
      .env
  2. 0 1
      config/packages/security.yaml
  3. 19 26
      src/ApiResources/Profile/AccessProfile.php
  4. 59 24
      src/ApiResources/Profile/OrganizationProfile.php
  5. 1 1
      src/DataPersister/Common/OpentalentDataPersister.php
  6. 22 9
      src/DataProvider/Access/AccessProfileDataProvider.php
  7. 1 2
      src/DataProvider/Common/OpentalentCollectionDataProvider.php
  8. 1 1
      src/DataProvider/Common/OpentalentItemDataProvider.php
  9. 1 1
      src/Doctrine/Access/AccessExtension.php
  10. 17 0
      src/Entity/Access/Access.php
  11. 18 0
      src/Entity/Organization/Organization.php
  12. 543 0
      src/Entity/Organization/Parameters.php
  13. 25 0
      src/Enum/Core/TimeZoneEnum.php
  14. 16 0
      src/Enum/Education/PeriodicityEnum.php
  15. 14 0
      src/Enum/Organization/BulletinOutputEnum.php
  16. 16 0
      src/Enum/Organization/BulletinPeriodEnum.php
  17. 1 0
      src/Enum/Organization/OrganizationIdsEnum.php
  18. 16 0
      src/Enum/Organization/SendToBulletinEnum.php
  19. 1 1
      src/Repository/Access/AccessRepository.php
  20. 90 24
      src/Repository/Network/NetworkOrganizationRepository.php
  21. 2 42
      src/Repository/Organization/OrganizationRepository.php
  22. 50 0
      src/Repository/Organization/ParametersRepository.php
  23. 2 3
      src/Security/ModuleVoter.php
  24. 9 12
      src/Service/Cotisation/Utils.php
  25. 62 0
      src/Service/Network/Tree.php
  26. 1 1
      src/Service/Network/Utils.php
  27. 3 8
      src/Service/Security/Module.php
  28. 1 2
      src/Service/Utils/Reflection.php
  29. 18 22
      tests/Service/Cotisation/UtilsTest.php
  30. 81 0
      tests/Service/Network/TreeTest.php
  31. 3 7
      tests/Service/Network/UtilsTest.php
  32. 3 8
      tests/Service/Organization/UtilsTest.php
  33. 1 1
      tests/Service/Security/ModuleTest.php

+ 1 - 1
.env

@@ -14,7 +14,7 @@
 # https://symfony.com/doc/current/best_practices.html#use-environment-variables-for-infrastructure-configuration
 
 ###> symfony/framework-bundle ###
-APP_ENV=dev
+APP_ENV=prod
 APP_SECRET=6a76497c8658bb23e2236f97a2627df3
 #TRUSTED_PROXIES=127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
 #TRUSTED_HOSTS='^(localhost|example\.com)$'

+ 0 - 1
config/packages/security.yaml

@@ -8,7 +8,6 @@ security:
         BASE_ROLE_ADMINISTRATION_CORE : &BASE_ROLE_ADMINISTRATION_CORE
             - ROLE_MEMBER_CORE
             - ROLE_ORGANIZATION_EDIT
-            - ROLE_ORGANIZATION_VIEW
 
         ROLE_ADMIN:
             - ROLE_CORE-CRUD

+ 19 - 26
src/ApiResources/Profile/AccessProfile.php

@@ -24,28 +24,12 @@ class AccessProfile
     /**
      * @ApiProperty(identifier=true)
      */
-    public $id;
-
-    /**
-     * @var string
-     */
-    private $name;
-
-    /**
-     * @var string
-     */
-    private $givenName;
-
-    /**
-     * @var array
-     */
-    private $roles = [];
-
-
-    /**
-     * @var OrganizationProfile
-     */
-    private $organization;
+    public int $id;
+    private bool $isAdminAccess;
+    private string $name;
+    private string $givenName;
+    private array $roles = [];
+    private OrganizationProfile $organization;
 
     public function __construct()
     {
@@ -63,6 +47,18 @@ class AccessProfile
         return $this;
     }
 
+    public function getIsAdminAccess(): ?bool
+    {
+        return $this->isAdminAccess;
+    }
+
+    public function setIsAdminAccess(bool $isAdminAccess): self
+    {
+        $this->isAdminAccess = $isAdminAccess;
+
+        return $this;
+    }
+
     public function getOrganization(): ?OrganizationProfile
     {
         return $this->organization;
@@ -99,10 +95,7 @@ class AccessProfile
         return $this;
     }
 
-    /**
-     * @inheritDoc
-     */
-    public function getRoles()
+    public function getRoles(): array
     {
         $roles = $this->roles;
 

+ 59 - 24
src/ApiResources/Profile/OrganizationProfile.php

@@ -3,35 +3,40 @@ declare(strict_types=1);
 
 namespace App\ApiResources\Profile;
 
+use ApiPlatform\Core\Annotation\ApiProperty;
+
 /**
  * Classe resource qui contient les champs relatifs aux organizations présentent dans la requete my_profile.
  * @package App\ApiResources\Profile
  */
 class OrganizationProfile
 {
-
-    /**
-     * @var string
-     */
-    private $name;
-
     /**
-     * @var string
+     * @ApiProperty(identifier=true)
      */
-    private $product;
+    public int $id;
+    private string $name;
+    private string $product;
+    private string $subDomain;
+    private string $website;
+    private array $modules = [];
+    private bool $hasChildren = false;
+    private array $parents = [];
 
-    /**
-     * @var array
-     */
-    private $modules = [];
+    public function __construct()
+    {
+    }
 
-    /**
-     * @var bool
-     */
-    private $hasChildren;
+    public function getId(): ?int
+    {
+        return $this->id;
+    }
 
-    public function __construct()
+    public function setId(?int $id): self
     {
+        $this->id = $id;
+
+        return $this;
     }
 
     public function getName(): ?string
@@ -58,10 +63,31 @@ class OrganizationProfile
         return $this;
     }
 
-    /**
-     * @inheritDoc
-     */
-    public function getModules()
+    public function getSubDomain(): ?string
+    {
+        return $this->subDomain;
+    }
+
+    public function setSubDomain(?string $subDomain): self
+    {
+        $this->subDomain = $subDomain;
+
+        return $this;
+    }
+
+    public function getWebsite(): ?string
+    {
+        return $this->website;
+    }
+
+    public function setWebsite(?string $website): self
+    {
+        $this->website = $website;
+
+        return $this;
+    }
+
+    public function getModules(): array
     {
         $modules = $this->modules;
         return array_unique($modules);
@@ -74,9 +100,6 @@ class OrganizationProfile
         return $this;
     }
 
-    /**
-     * @inheritDoc
-     */
     public function getHasChildren(): bool
     {
         return $this->hasChildren;
@@ -88,4 +111,16 @@ class OrganizationProfile
 
         return $this;
     }
+
+
+    public function getParents(): array
+    {
+        return $this->parents;
+    }
+
+    public function addParent(OrganizationProfile $parent): self
+    {
+        $this->parents[] = $parent;
+        return $this;
+    }
 }

+ 1 - 1
src/DataPersister/Common/OpentalentDataPersister.php

@@ -11,7 +11,7 @@ use ApiPlatform\Core\DataPersister\ContextAwareDataPersisterInterface;
  */
 final class OpentalentDataPersister implements ContextAwareDataPersisterInterface
 {
-    private $decorated;
+    private ContextAwareDataPersisterInterface $decorated;
 
     public function __construct(ContextAwareDataPersisterInterface $decorated)
     {

+ 22 - 9
src/DataProvider/Access/AccessProfileDataProvider.php

@@ -9,6 +9,8 @@ use App\ApiResources\Profile\AccessProfile;
 use App\ApiResources\Profile\OrganizationProfile;
 use App\Entity\Access\Access;
 use App\Entity\Organization\Organization;
+use App\Repository\Organization\OrganizationRepository;
+use App\Service\Network\Tree;
 use App\Service\Security\Module;
 use Symfony\Component\Security\Core\Role\Role;
 use Symfony\Component\Security\Core\Role\RoleHierarchyInterface;
@@ -20,24 +22,22 @@ use Symfony\Component\Security\Core\Security;
  */
 final class AccessProfileDataProvider implements ItemDataProviderInterface, RestrictedDataProviderInterface
 {
-    /** @var Security  */
-    private $security;
-
-    /** @var RoleHierarchyInterface  */
-    private $roleHierarchy;
-
-    /** @var Module  */
-    private $module;
+    private Security $security;
+    private RoleHierarchyInterface $roleHierarchy;
+    private Module $module;
+    private Tree $tree;
 
     public function __construct(
         Security $security,
         RoleHierarchyInterface $roleHierarchy,
-        Module $module
+        Module $module,
+        Tree $tree
     )
     {
         $this->security = $security;
         $this->roleHierarchy = $roleHierarchy;
         $this->module = $module;
+        $this->tree = $tree;
     }
 
     public function supports(string $resourceClass, string $operationName = null, array $context = []): bool
@@ -67,6 +67,7 @@ final class AccessProfileDataProvider implements ItemDataProviderInterface, Rest
     {
         $accessProfile = new AccessProfile();
         $accessProfile->setId($access->getId());
+        $accessProfile->setIsAdminAccess($access->getAdminAccess());
         $accessProfile->setName($access->getPerson()->getName());
         $accessProfile->setGivenName($access->getPerson()->getGivenName());
         return $accessProfile;
@@ -83,6 +84,18 @@ final class AccessProfileDataProvider implements ItemDataProviderInterface, Rest
         $organizationProfile->setModules($this->module->getOrganizationModules($organization));
         $organizationProfile->setProduct($organization->getSettings()->getProduct());
         $organizationProfile->setHasChildren($organization->getNetworkOrganizationChildren()->count() > 1);
+        $organizationProfile->setSubDomain($organization->getParameters()->getSubDomain());
+        $organizationProfile->setWebsite($organization->getParameters()->getWebsite());
+
+        /** @var Organization $parent */
+        foreach ($this->tree->findAllParentsAndSortByType($organization) as $parent){
+            $parentProfile = new OrganizationProfile();
+            $parentProfile->setId($parent->getId());
+            $parentProfile->setName($parent->getName());
+            $parentProfile->setSubDomain($parent->getParameters()->getSubDomain());
+            $parentProfile->setWebsite($parent->getParameters()->getWebsite());
+            $organizationProfile->addParent($parentProfile);
+        }
         return $organizationProfile;
     }
 }

+ 1 - 2
src/DataProvider/Common/OpentalentCollectionDataProvider.php

@@ -12,8 +12,7 @@ use ApiPlatform\Core\DataProvider\RestrictedDataProviderInterface;
  */
 final class OpentalentCollectionDataProvider implements ContextAwareCollectionDataProviderInterface, RestrictedDataProviderInterface
 {
-    /** @var ContextAwareCollectionDataProviderInterface  */
-    private $decorated;
+    private ContextAwareCollectionDataProviderInterface $decorated;
 
     public function __construct(
         ContextAwareCollectionDataProviderInterface $decorated

+ 1 - 1
src/DataProvider/Common/OpentalentItemDataProvider.php

@@ -12,7 +12,7 @@ use ApiPlatform\Core\DataProvider\RestrictedDataProviderInterface;
  */
 final class OpentalentItemDataProvider implements DenormalizedIdentifiersAwareItemDataProviderInterface, RestrictedDataProviderInterface
 {
-    private $decorated;
+    private DenormalizedIdentifiersAwareItemDataProviderInterface $decorated;
 
     public function __construct(DenormalizedIdentifiersAwareItemDataProviderInterface $decorated)
     {

+ 1 - 1
src/Doctrine/Access/AccessExtension.php

@@ -16,7 +16,7 @@ use Symfony\Component\Security\Core\Security;
  */
 final class AccessExtension implements QueryCollectionExtensionInterface, QueryItemExtensionInterface
 {
-    private $security;
+    private Security $security;
 
     public function __construct(Security $security)
     {

+ 17 - 0
src/Entity/Access/Access.php

@@ -38,6 +38,11 @@ class Access implements UserInterface
      */
     private $id;
 
+    /**
+     * @ORM\Column(type="boolean", options={"default" : false})
+     */
+    private $adminAccess = false;
+
     /**
      * @ORM\ManyToOne(targetEntity=Person::class, cascade={"persist"})
      * @ORM\JoinColumn(nullable=false)
@@ -72,6 +77,18 @@ class Access implements UserInterface
         return $this->id;
     }
 
+    public function getAdminAccess(): ?bool
+    {
+        return $this->adminAccess;
+    }
+
+    public function setAdminAccess(bool $adminAccess): self
+    {
+        $this->adminAccess = $adminAccess;
+
+        return $this;
+    }
+
     public function getPerson(): ?Person
     {
         return $this->person;

+ 18 - 0
src/Entity/Organization/Organization.php

@@ -69,6 +69,12 @@ class Organization
      */
     private $networkOrganizationChildren;
 
+    /**
+     * @ORM\OneToOne(targetEntity=Parameters::class, cascade={"persist", "remove"})
+     * @ORM\JoinColumn(nullable=false)
+     */
+    private $parameters;
+
     public function __construct()
     {
         $this->networkOrganizations = new ArrayCollection();
@@ -204,4 +210,16 @@ class Organization
 
         return $this;
     }
+
+    public function getParameters(): ?Parameters
+    {
+        return $this->parameters;
+    }
+
+    public function setParameters(Parameters $parameters): self
+    {
+        $this->parameters = $parameters;
+
+        return $this;
+    }
 }

+ 543 - 0
src/Entity/Organization/Parameters.php

@@ -0,0 +1,543 @@
+<?php
+declare(strict_types=1);
+
+namespace App\Entity\Organization;
+
+use ApiPlatform\Core\Annotation\ApiResource;
+use App\Repository\Organization\ParametersRepository;
+use Doctrine\ORM\Mapping as ORM;
+use Symfony\Component\Validator\Constraints as Assert;
+
+/**
+ * @ApiResource()
+ * @ORM\Entity(repositoryClass=ParametersRepository::class)
+ */
+class Parameters
+{
+    /**
+     * @ORM\Id
+     * @ORM\GeneratedValue
+     * @ORM\Column(type="integer")
+     */
+    private $id;
+
+    /**
+     * @ORM\Column(type="date", nullable=true)
+     */
+    private $financialDate;
+
+    /**
+     * @ORM\Column(type="date", nullable=true)
+     */
+    private $musicalDate;
+
+    /**
+     * @ORM\Column(type="date", nullable=true)
+     */
+    private $startCourseDate;
+
+    /**
+     * @ORM\Column(type="date", nullable=true)
+     */
+    private $endCourseDate;
+
+    /**
+     * @ORM\Column(type="boolean", options={"default" : false})
+     */
+    private $trackingValidation = false;
+
+    /**
+     * @ORM\Column(type="boolean", options={"default" : true})
+     */
+    private $editCriteriaNotationByAdminOnly = true;
+
+    /**
+     * @ORM\Column(type="string", length=255, nullable=true)
+     */
+    private $smsSenderName;
+
+    /**
+     * @ORM\Column(type="boolean", options={"default" : false})
+     */
+    private $logoDonorsMove = false;
+
+    /**
+     * @ORM\Column(type="string", length=255, nullable=true)
+     */
+    private $subDomain;
+
+    /**
+     * @ORM\Column(type="string", length=255, nullable=true)
+     */
+    private $website;
+
+    /**
+     * @ORM\Column(type="string", length=255, nullable=true)
+     */
+    private $otherWebsite;
+
+    /**
+     * @ORM\Column(type="boolean", options={"default" : false})
+     */
+    private $desactivateOpentalentSiteWeb = false;
+
+    /**
+     * @ORM\Column(type="string", length=255, nullable=true)
+     * @Assert\Choice(callback={"\App\Enum\Organization\BulletinPeriodEnum", "toArray"})
+     */
+    private $bulletinPeriod;
+
+    /**
+     * @ORM\Column(type="boolean", options={"default" : false})
+     */
+    private $bulletinWithTeacher = false;
+
+    /**
+     * @ORM\Column(type="boolean", options={"default" : false})
+     */
+    private $bulletinPrintAddress = false;
+
+    /**
+     * @ORM\Column(type="boolean", options={"default" : true})
+     */
+    private $bulletinSignatureDirector = true;
+
+    /**
+     * @ORM\Column(type="boolean", options={"default" : true})
+     */
+    private $bulletinDisplayLevelAcquired = true;
+
+    /**
+     * @ORM\Column(type="boolean", options={"default" : false})
+     */
+    private $bulletinShowEducationWithoutEvaluation = false;
+
+    /**
+     * @ORM\Column(type="boolean", options={"default" : false})
+     */
+    private $bulletinViewTestResults = false;
+
+    /**
+     * @ORM\Column(type="boolean", options={"default" : false})
+     */
+    private $bulletinShowAbsences = false;
+
+    /**
+     * @ORM\Column(type="boolean", options={"default" : true})
+     */
+    private $bulletinShowAverages = true;
+
+    /**
+     * @ORM\Column(type="string", length=255, nullable=true)
+     * @Assert\Choice(callback={"\App\Enum\Organization\BulletinOutputEnum", "toArray"})
+     */
+    private $bulletinOutput;
+
+    /**
+     * @ORM\Column(type="string", length=255, nullable=true)
+     */
+    private $usernameSMS;
+
+    /**
+     * @ORM\Column(type="string", length=255, nullable=true)
+     */
+    private $passwordSMS;
+
+    /**
+     * @ORM\Column(type="boolean", options={"default" : true})
+     */
+    private $bulletinEditWithoutEvaluation = true;
+
+    /**
+     * @ORM\Column(type="string", length=255, options={"default":"STUDENTS_AND_THEIR_GUARDIANS"})
+     * @Assert\Choice(callback={"\App\Enum\Organization\SendToBulletinEnum", "toArray"})
+     */
+    private $bulletinReceiver;
+
+    /**
+     * @ORM\Column(type="boolean", options={"default" : true})
+     */
+    private $showAdherentList = true;
+
+    /**
+     * @ORM\Column(type="boolean", options={"default" : false})
+     */
+    private $studentsAreAdherents = false;
+
+    /**
+     * @ORM\Column(type="string", length=255, options={"default" : "Europe/Paris"})
+     * @Assert\Choice(callback={"\App\Enum\Core\TimeZoneEnum", "toArrayCustom"})
+     */
+    private $timezone = "Europe/Paris";
+
+    /**
+     * @ORM\Column(type="string", length=255, nullable=true)
+     * @Assert\Choice(callback={"\App\Enum\Education\PeriodicityEnum", "toArray"})
+     */
+    private $educationPeriodicity;
+
+    public function getId(): ?int
+    {
+        return $this->id;
+    }
+
+    public function getFinancialDate(): ?\DateTimeInterface
+    {
+        return $this->financialDate;
+    }
+
+    public function setFinancialDate(?\DateTimeInterface $financialDate): self
+    {
+        $this->financialDate = $financialDate;
+
+        return $this;
+    }
+
+    public function getMusicalDate(): ?\DateTimeInterface
+    {
+        return $this->musicalDate;
+    }
+
+    public function setMusicalDate(?\DateTimeInterface $musicalDate): self
+    {
+        $this->musicalDate = $musicalDate;
+
+        return $this;
+    }
+
+    public function getStartCourseDate(): ?\DateTimeInterface
+    {
+        return $this->startCourseDate;
+    }
+
+    public function setStartCourseDate(?\DateTimeInterface $startCourseDate): self
+    {
+        $this->startCourseDate = $startCourseDate;
+
+        return $this;
+    }
+
+    public function getEndCourseDate(): ?\DateTimeInterface
+    {
+        return $this->endCourseDate;
+    }
+
+    public function setEndCourseDate(?\DateTimeInterface $endCourseDate): self
+    {
+        $this->endCourseDate = $endCourseDate;
+
+        return $this;
+    }
+
+    public function getTrackingValidation(): ?bool
+    {
+        return $this->trackingValidation;
+    }
+
+    public function setTrackingValidation(bool $trackingValidation): self
+    {
+        $this->trackingValidation = $trackingValidation;
+
+        return $this;
+    }
+
+    public function getEditCriteriaNotationByAdminOnly(): ?bool
+    {
+        return $this->editCriteriaNotationByAdminOnly;
+    }
+
+    public function setEditCriteriaNotationByAdminOnly(bool $editCriteriaNotationByAdminOnly): self
+    {
+        $this->editCriteriaNotationByAdminOnly = $editCriteriaNotationByAdminOnly;
+
+        return $this;
+    }
+
+    public function getSmsSenderName(): ?string
+    {
+        return $this->smsSenderName;
+    }
+
+    public function setSmsSenderName(?string $smsSenderName): self
+    {
+        $this->smsSenderName = $smsSenderName;
+
+        return $this;
+    }
+
+    public function getLogoDonorsMove(): ?bool
+    {
+        return $this->logoDonorsMove;
+    }
+
+    public function setLogoDonorsMove(bool $logoDonorsMove): self
+    {
+        $this->logoDonorsMove = $logoDonorsMove;
+
+        return $this;
+    }
+
+    public function getSubDomain(): ?string
+    {
+        return $this->subDomain;
+    }
+
+    public function setSubDomain(?string $subDomain): self
+    {
+        $this->subDomain = $subDomain;
+
+        return $this;
+    }
+
+    public function getWebsite(): ?string
+    {
+        return $this->website;
+    }
+
+    public function setWebsite(?string $website): self
+    {
+        $this->website = $website;
+
+        return $this;
+    }
+
+    public function getOtherWebsite(): ?string
+    {
+        return $this->otherWebsite;
+    }
+
+    public function setOtherWebsite(?string $otherWebsite): self
+    {
+        $this->otherWebsite = $otherWebsite;
+
+        return $this;
+    }
+
+    public function getDesactivateOpentalentSiteWeb(): ?bool
+    {
+        return $this->desactivateOpentalentSiteWeb;
+    }
+
+    public function setDesactivateOpentalentSiteWeb(bool $desactivateOpentalentSiteWeb): self
+    {
+        $this->desactivateOpentalentSiteWeb = $desactivateOpentalentSiteWeb;
+
+        return $this;
+    }
+
+    public function getBulletinPeriod(): ?string
+    {
+        return $this->bulletinPeriod;
+    }
+
+    public function setBulletinPeriod(?string $bulletinPeriod): self
+    {
+        $this->bulletinPeriod = $bulletinPeriod;
+
+        return $this;
+    }
+
+    public function getBulletinWithTeacher(): ?bool
+    {
+        return $this->bulletinWithTeacher;
+    }
+
+    public function setBulletinWithTeacher(bool $bulletinWithTeacher): self
+    {
+        $this->bulletinWithTeacher = $bulletinWithTeacher;
+
+        return $this;
+    }
+
+    public function getBulletinPrintAddress(): ?bool
+    {
+        return $this->bulletinPrintAddress;
+    }
+
+    public function setBulletinPrintAddress(bool $bulletinPrintAddress): self
+    {
+        $this->bulletinPrintAddress = $bulletinPrintAddress;
+
+        return $this;
+    }
+
+    public function getBulletinSignatureDirector(): ?bool
+    {
+        return $this->bulletinSignatureDirector;
+    }
+
+    public function setBulletinSignatureDirector(bool $bulletinSignatureDirector): self
+    {
+        $this->bulletinSignatureDirector = $bulletinSignatureDirector;
+
+        return $this;
+    }
+
+    public function getBulletinDisplayLevelAcquired(): ?bool
+    {
+        return $this->bulletinDisplayLevelAcquired;
+    }
+
+    public function setBulletinDisplayLevelAcquired(bool $bulletinDisplayLevelAcquired): self
+    {
+        $this->bulletinDisplayLevelAcquired = $bulletinDisplayLevelAcquired;
+
+        return $this;
+    }
+
+    public function getBulletinShowEducationWithoutEvaluation(): ?bool
+    {
+        return $this->bulletinShowEducationWithoutEvaluation;
+    }
+
+    public function setBulletinShowEducationWithoutEvaluation(bool $bulletinShowEducationWithoutEvaluation): self
+    {
+        $this->bulletinShowEducationWithoutEvaluation = $bulletinShowEducationWithoutEvaluation;
+
+        return $this;
+    }
+
+    public function getBulletinViewTestResults(): ?bool
+    {
+        return $this->bulletinViewTestResults;
+    }
+
+    public function setBulletinViewTestResults(bool $bulletinViewTestResults): self
+    {
+        $this->bulletinViewTestResults = $bulletinViewTestResults;
+
+        return $this;
+    }
+
+    public function getBulletinShowAbsences(): ?bool
+    {
+        return $this->bulletinShowAbsences;
+    }
+
+    public function setBulletinShowAbsences(bool $bulletinShowAbsences): self
+    {
+        $this->bulletinShowAbsences = $bulletinShowAbsences;
+
+        return $this;
+    }
+
+    public function getBulletinShowAverages(): ?bool
+    {
+        return $this->bulletinShowAverages;
+    }
+
+    public function setBulletinShowAverages(bool $bulletinShowAverages): self
+    {
+        $this->bulletinShowAverages = $bulletinShowAverages;
+
+        return $this;
+    }
+
+    public function getBulletinOutput(): ?string
+    {
+        return $this->bulletinOutput;
+    }
+
+    public function setBulletinOutput(?string $bulletinOutput): self
+    {
+        $this->bulletinOutput = $bulletinOutput;
+
+        return $this;
+    }
+
+    public function getUsernameSMS(): ?string
+    {
+        return $this->usernameSMS;
+    }
+
+    public function setUsernameSMS(?string $usernameSMS): self
+    {
+        $this->usernameSMS = $usernameSMS;
+
+        return $this;
+    }
+
+    public function getPasswordSMS(): ?string
+    {
+        return $this->passwordSMS;
+    }
+
+    public function setPasswordSMS(?string $passwordSMS): self
+    {
+        $this->passwordSMS = $passwordSMS;
+
+        return $this;
+    }
+
+    public function getBulletinEditWithoutEvaluation(): ?bool
+    {
+        return $this->bulletinEditWithoutEvaluation;
+    }
+
+    public function setBulletinEditWithoutEvaluation(bool $bulletinEditWithoutEvaluation): self
+    {
+        $this->bulletinEditWithoutEvaluation = $bulletinEditWithoutEvaluation;
+
+        return $this;
+    }
+
+    public function getBulletinReceiver(): ?string
+    {
+        return $this->bulletinReceiver;
+    }
+
+    public function setBulletinReceiver(?string $bulletinReceiver): self
+    {
+        $this->bulletinReceiver = $bulletinReceiver;
+
+        return $this;
+    }
+
+    public function getShowAdherentList(): ?bool
+    {
+        return $this->showAdherentList;
+    }
+
+    public function setShowAdherentList(bool $showAdherentList): self
+    {
+        $this->showAdherentList = $showAdherentList;
+
+        return $this;
+    }
+
+    public function getStudentsAreAdherents(): ?bool
+    {
+        return $this->studentsAreAdherents;
+    }
+
+    public function setStudentsAreAdherents(bool $studentsAreAdherents): self
+    {
+        $this->studentsAreAdherents = $studentsAreAdherents;
+
+        return $this;
+    }
+
+    public function getTimezone(): ?string
+    {
+        return $this->timezone;
+    }
+
+    public function setTimezone(string $timezone): self
+    {
+        $this->timezone = $timezone;
+
+        return $this;
+    }
+
+    public function getEducationPeriodicity(): ?string
+    {
+        return $this->educationPeriodicity;
+    }
+
+    public function setEducationPeriodicity(?string $educationPeriodicity): self
+    {
+        $this->educationPeriodicity = $educationPeriodicity;
+
+        return $this;
+    }
+}

+ 25 - 0
src/Enum/Core/TimeZoneEnum.php

@@ -0,0 +1,25 @@
+<?php
+
+namespace App\Enum\Core;
+
+use MyCLabs\Enum\Enum;
+
+/**
+ * TimeZone disponibles
+ * @package App\Enum\Core
+ */
+class TimeZoneEnum extends Enum
+{
+    /**
+     * Return a custom array instead the original array
+     * @return mixed
+     */
+    public static function toArrayCustom()
+    {
+        return [
+         'Indian/Reunion' => 'Indian/Reunion',
+         'Europe/Zurich' => 'Europe/Zurich',
+         'Europe/Paris' => 'Europe/Paris'
+        ];
+    }
+}

+ 16 - 0
src/Enum/Education/PeriodicityEnum.php

@@ -0,0 +1,16 @@
+<?php
+
+namespace App\Enum\Education;
+
+use MyCLabs\Enum\Enum;
+
+/**
+ * Période disponibles.
+ */
+class PeriodicityEnum extends Enum
+{
+    private const MONTHLY ='MONTHLY';
+    private const QUARTERLY ='QUARTERLY';
+    private const HALF = 'HALF';
+    private const ANNUAL = 'ANNUAL';
+}

+ 14 - 0
src/Enum/Organization/BulletinOutputEnum.php

@@ -0,0 +1,14 @@
+<?php
+
+namespace App\Enum\Organization;
+
+use MyCLabs\Enum\Enum;
+
+/**
+ * Sortie des bulletin
+ */
+class BulletinOutputEnum extends Enum
+{
+    private const PRINTING = 'PRINTING';
+    private const SEND_BY_EMAIL = 'SEND_BY_EMAIL';
+}

+ 16 - 0
src/Enum/Organization/BulletinPeriodEnum.php

@@ -0,0 +1,16 @@
+<?php
+
+namespace App\Enum\Organization;
+
+use MyCLabs\Enum\Enum;
+
+/**
+ * Périodes des bulletin
+ */
+class BulletinPeriodEnum extends Enum
+{
+    private const YEAR = 'YEAR';
+    private const SEMESTER = 'SEMESTER';
+    private const TRIMESTER = 'TRIMESTER';
+    private const MONTH = 'MONTH';
+}

+ 1 - 0
src/Enum/Organization/OrganizationIdsEnum.php

@@ -11,4 +11,5 @@ class OrganizationIdsEnum extends Enum
 {
     private const CMF     = 12097;
     private const _2IOS   = 32366;
+    private const OPENTALENT_BASE   = 13;
 }

+ 16 - 0
src/Enum/Organization/SendToBulletinEnum.php

@@ -0,0 +1,16 @@
+<?php
+
+namespace App\Enum\Organization;
+
+use MyCLabs\Enum\Enum;
+
+/**
+ * Bulletin envoyés à...
+ */
+class SendToBulletinEnum extends Enum
+{
+    private const STUDENTS_AND_THEIR_GUARDIANS = 'STUDENTS_AND_THEIR_GUARDIANS';
+    private const STUDENTS = 'STUDENTS';
+    private const GUARDIANS = 'GUARDIANS';
+}
+

+ 1 - 1
src/Repository/Access/AccessRepository.php

@@ -19,7 +19,7 @@ final class AccessRepository extends ServiceEntityRepository implements UserLoad
 {
     const ACCESS_NAME_HEADER = 'X-AccessId';
 
-    private $requestStack;
+    private RequestStack $requestStack;
 
     public function __construct(ManagerRegistry $registry, RequestStack $requestStack)
     {

+ 90 - 24
src/Repository/Network/NetworkOrganizationRepository.php

@@ -4,7 +4,12 @@ declare(strict_types=1);
 namespace App\Repository\Network;
 
 use App\Entity\Network\NetworkOrganization;
+use App\Entity\Organization\Organization;
+use App\Enum\Organization\OrganizationIdsEnum;
+use App\Enum\Organization\PrincipalTypeEnum;
 use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
+use Doctrine\ORM\Query\ResultSetMapping;
+use Doctrine\ORM\Query\ResultSetMappingBuilder;
 use Doctrine\Persistence\ManagerRegistry;
 
 /**
@@ -13,39 +18,100 @@ use Doctrine\Persistence\ManagerRegistry;
  * @method NetworkOrganization[]    findAll()
  * @method NetworkOrganization[]    findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
  */
-final class NetworkOrganizationRepository extends ServiceEntityRepository
+class NetworkOrganizationRepository extends ServiceEntityRepository
 {
     public function __construct(ManagerRegistry $registry)
     {
         parent::__construct($registry, NetworkOrganization::class);
     }
 
-    // /**
-    //  * @return NetworkOrganization[] Returns an array of NetworkOrganization objects
-    //  */
-    /*
-    public function findByExampleField($value)
+    /**
+     * Vérifie si l'organisation est un dernier parent : possède des enfants mais ces enfants ne possèdent pas d'enfant
+     * @param Organization $organization
+     * @return bool
+     */
+    public function isLastParent(Organization $organization): bool
     {
-        return $this->createQueryBuilder('n')
-            ->andWhere('n.exampleField = :val')
-            ->setParameter('val', $value)
-            ->orderBy('n.id', 'ASC')
-            ->setMaxResults(10)
-            ->getQuery()
-            ->getResult()
-        ;
+        $sql = sprintf("
+        SELECT 
+            IF( (SELECT o.id 
+                    FROM Organization as o 
+                    WHERE o.id=neto.organization_id
+                    AND o.principalType IN('%s','%s','%s','%s','%s','%s')) IS NOT NULL ,0,1) AS is_last_parent
+        FROM
+            NetworkOrganization as neto
+        WHERE
+            neto.parent_id = %d
+            and (neto.endDate is null or neto.endDate = \"0000-00-00\" or neto.endDate > CURDATE())
+        GROUP BY is_last_parent
+        ORDER BY is_last_parent DESC",
+            PrincipalTypeEnum::NATIONAL_FEDERATION(),
+            PrincipalTypeEnum::REGIONAL_FEDERATION(),
+            PrincipalTypeEnum::LOCAL_FEDERATION(),
+            PrincipalTypeEnum::GROUPMENT(),
+            PrincipalTypeEnum::DEPARTEMENTAL_FEDERATION(),
+            PrincipalTypeEnum::DELEGATION(),
+            $organization->getId()
+        );
+
+        $rsm = new ResultSetMapping();
+        $rsm->addScalarResult('is_last_parent', 'is_last_parent', 'integer');
+        $query = $this->getEntityManager()->createNativeQuery($sql, $rsm);
+        $result = $query->getResult(\Doctrine\ORM\Query::HYDRATE_ARRAY);
+
+        foreach ($result as $a_tmp) {
+            if ($a_tmp['is_last_parent']) {
+                return true;
+            }
+        }
+        return false;
     }
-    */
 
-    /*
-    public function findOneBySomeField($value): ?NetworkOrganization
+    /**
+     * Retrouve tous les parents de la structure passée en paramètres
+     *
+     * @param Organization $organization.
+     *
+     * @return array The organizations.
+     */
+    public function findAllParents(Organization $organization): ?array
     {
-        return $this->createQueryBuilder('n')
-            ->andWhere('n.exampleField = :val')
-            ->setParameter('val', $value)
-            ->getQuery()
-            ->getOneOrNullResult()
-        ;
+        $sql = sprintf("
+            SELECT *
+             FROM Organization o
+             JOIN
+               (
+                 SELECT
+                         GROUP_CONCAT(@target:=
+                             (
+                                 SELECT @source:=GROUP_CONCAT(parent_id)
+                                 FROM
+                                     NetworkOrganization
+                                 WHERE
+                                     FIND_IN_SET(organization_id, @source) AND parent_id NOT IN (%s, %s)
+                                    AND (endDate is null or endDate = \"0000-00-00\" or endDate > CURDATE()) 
+                             )
+                         ) AS parents
+                 FROM
+                     (SELECT
+                         @source:=%d,
+                         @target:=0
+                     ) vars,
+                     NetworkOrganization
+                 WHERE
+                     @source IS NOT NULL
+               ) tmp
+               WHERE FIND_IN_SET(o.id, parents)
+         ",
+            OrganizationIdsEnum::_2IOS(),
+            OrganizationIdsEnum::OPENTALENT_BASE(),
+            $organization->getId()
+        );
+
+        $rsm = new ResultSetMappingBuilder($this->getEntityManager());
+        $rsm->addRootEntityFromClassMetadata(Organization::class, 'o');
+        $query = $this->getEntityManager()->createNativeQuery($sql, $rsm);
+
+        return $query->getResult();
     }
-    */
 }

+ 2 - 42
src/Repository/Organization/OrganizationRepository.php

@@ -4,9 +4,11 @@ declare(strict_types=1);
 namespace App\Repository\Organization;
 
 use App\Entity\Organization\Organization;
+use App\Enum\Organization\OrganizationIdsEnum;
 use App\Enum\Organization\PrincipalTypeEnum;
 use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
 use Doctrine\ORM\Query\ResultSetMapping;
+use Doctrine\ORM\Query\ResultSetMappingBuilder;
 use Doctrine\Persistence\ManagerRegistry;
 
 /**
@@ -21,46 +23,4 @@ class OrganizationRepository extends ServiceEntityRepository
     {
         parent::__construct($registry, Organization::class);
     }
-
-    /**
-     * Vérifie si l'organisation est un dernier parent : possède des enfants mais ces enfants ne possèdent pas d'enfant
-     * @param Organization $organization
-     * @return bool
-     */
-    public function isLastParent(Organization $organization): bool {
-        $sql = sprintf("
-        SELECT 
-            IF( (SELECT o.id 
-                    FROM Organization as o 
-                    WHERE o.id=neto.organization_id
-                    AND o.principalType IN('%s','%s','%s','%s','%s','%s')) IS NOT NULL ,0,1) AS is_last_parent
-        FROM
-            NetworkOrganization as neto
-        WHERE
-            neto.parent_id = %d
-            and (neto.endDate is null or neto.endDate = \"0000-00-00\" or neto.endDate > CURDATE())
-        GROUP BY is_last_parent
-        ORDER BY is_last_parent DESC",
-            PrincipalTypeEnum::NATIONAL_FEDERATION(),
-            PrincipalTypeEnum::REGIONAL_FEDERATION(),
-            PrincipalTypeEnum::LOCAL_FEDERATION(),
-            PrincipalTypeEnum::GROUPMENT(),
-            PrincipalTypeEnum::DEPARTEMENTAL_FEDERATION(),
-            PrincipalTypeEnum::DELEGATION(),
-            $organization->getId()
-        );
-
-        $rsm = new ResultSetMapping();
-        $rsm->addScalarResult('is_last_parent', 'is_last_parent', 'integer');
-        $query = $this->getEntityManager()->createNativeQuery($sql, $rsm);
-        $result = $query->getResult(\Doctrine\ORM\Query::HYDRATE_ARRAY);
-
-        foreach ($result as $a_tmp) {
-            if ($a_tmp['is_last_parent']) {
-                return true;
-            }
-        }
-
-        return false;
-    }
 }

+ 50 - 0
src/Repository/Organization/ParametersRepository.php

@@ -0,0 +1,50 @@
+<?php
+
+namespace App\Repository\Organization;
+
+use App\Entity\Organization\Parameters;
+use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
+use Doctrine\Persistence\ManagerRegistry;
+
+/**
+ * @method Parameters|null find($id, $lockMode = null, $lockVersion = null)
+ * @method Parameters|null findOneBy(array $criteria, array $orderBy = null)
+ * @method Parameters[]    findAll()
+ * @method Parameters[]    findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
+ */
+class ParametersRepository extends ServiceEntityRepository
+{
+    public function __construct(ManagerRegistry $registry)
+    {
+        parent::__construct($registry, Parameters::class);
+    }
+
+    // /**
+    //  * @return Parameters[] Returns an array of Parameters objects
+    //  */
+    /*
+    public function findByExampleField($value)
+    {
+        return $this->createQueryBuilder('p')
+            ->andWhere('p.exampleField = :val')
+            ->setParameter('val', $value)
+            ->orderBy('p.id', 'ASC')
+            ->setMaxResults(10)
+            ->getQuery()
+            ->getResult()
+        ;
+    }
+    */
+
+    /*
+    public function findOneBySomeField($value): ?Parameters
+    {
+        return $this->createQueryBuilder('p')
+            ->andWhere('p.exampleField = :val')
+            ->setParameter('val', $value)
+            ->getQuery()
+            ->getOneOrNullResult()
+        ;
+    }
+    */
+}

+ 2 - 3
src/Security/ModuleVoter.php

@@ -19,11 +19,10 @@ use ApiPlatform\Core\Util\RequestAttributesExtractor;
  */
 class ModuleVoter extends Voter
 {
-    private $resourceMetadataFactory;
-
     const HAVING_MODULE = 'IS_HAVING_MODULE';
 
-    private $module;
+    private ResourceMetadataFactoryInterface $resourceMetadataFactory;
+    private Module $module;
 
     public function __construct(Module $module, ResourceMetadataFactoryInterface $resourceMetadataFactory)
     {

+ 9 - 12
src/Service/Cotisation/Utils.php

@@ -4,31 +4,28 @@ declare(strict_types=1);
 namespace App\Service\Cotisation;
 
 use App\Entity\Organization\Organization;
-use App\Repository\Organization\OrganizationRepository;
+use App\Repository\Network\NetworkOrganizationRepository;
 use App\Service\Organization\Utils as OrganizationUtils;
 use App\Tests\Service\Cotisation\UtilsTest;
-use App\Service\Network\Utils as NetWorkUtils;
+use App\Service\Network\Utils as NetworkUtils;
 
 /**
  * Class Utils : Service rassemblant des fonctions d'interrogation pour gérer des conditions dans les Cotisations
  * @package App\Service\Cotisation
  */
 class Utils {
-    /** @var NetWorkUtils  */
-    private $networkUtils;
-    /** @var OrganizationRepository  */
-    private $organizationRepository;
-    /** @var OrganizationUtils  */
-    private $organizationUtils;
+    private NetworkUtils $networkUtils;
+    private NetworkOrganizationRepository $networkOrganizationRepository;
+    private OrganizationUtils $organizationUtils;
 
     function __construct(
-        NetWorkUtils $networkUtils,
+        NetworkUtils $networkUtils,
         OrganizationUtils $organizationUtils,
-        OrganizationRepository $organizationRepository
+        NetworkOrganizationRepository $networkOrganizationRepository
     ) {
         $this->networkUtils = $networkUtils;
         $this->organizationUtils = $organizationUtils;
-        $this->organizationRepository = $organizationRepository;
+        $this->networkOrganizationRepository = $networkOrganizationRepository;
     }
 
     /**
@@ -38,7 +35,7 @@ class Utils {
      * @see UtilsTest::testIsLastParentAndCMF()
      */
     public function isLastParentAndCMF(Organization $organization): bool {
-        return $this->organizationRepository->isLastParent($organization) && $this->networkUtils->isCMF($organization);
+        return $this->networkOrganizationRepository->isLastParent($organization) && $this->networkUtils->isCMF($organization);
     }
 
     /**

+ 62 - 0
src/Service/Network/Tree.php

@@ -0,0 +1,62 @@
+<?php
+declare(strict_types=1);
+
+namespace App\Service\Network;
+
+
+use App\Entity\Organization\Organization;
+use App\Enum\Organization\PrincipalTypeEnum;
+use App\Repository\Network\NetworkOrganizationRepository;
+use App\Tests\Service\Network\TreeTest;
+
+/**
+ * Class Tree : service rassemblant des fonction pour répondre aux poblématique d'arbre du réseau
+ * @package App\Service\Network
+ */
+class Tree
+{
+    private NetworkOrganizationRepository $networkOrganizationRepository;
+
+    public function __construct(NetworkOrganizationRepository $networkOrganizationRepository)
+    {
+        $this->networkOrganizationRepository = $networkOrganizationRepository;
+    }
+
+    /**
+     * Retrouve tous les parents d'une structure et les tries selon leur type principal
+     * @param Organization $organization
+     * @return array
+     */
+    public function findAllParentsAndSortByType(Organization $organization): array {
+        return $this->sortByType($this->networkOrganizationRepository->findAllParents($organization));
+    }
+
+    /**
+     * Trie les organisations par rapport à leur type principal :
+     * DELEGATION, GROUPMENT,  LOCAL_FEDERATION, DEPARTEMENTAL_FEDERATION, REGIONAL_FEDERATION, NATIONAL_FEDERATION
+     * @param array $organizations
+     * @return array
+     * @see TreeTest::testSortByType()
+     */
+    public function sortByType(array $organizations): array {
+        $typeOrder = [
+            PrincipalTypeEnum::DELEGATION(),
+            PrincipalTypeEnum::GROUPMENT(),
+            PrincipalTypeEnum::LOCAL_FEDERATION(),
+            PrincipalTypeEnum::DEPARTEMENTAL_FEDERATION(),
+            PrincipalTypeEnum::REGIONAL_FEDERATION(),
+            PrincipalTypeEnum::NATIONAL_FEDERATION()
+        ];
+
+        usort($organizations, function(Organization $organization1, Organization $organization2) use($typeOrder){
+            $orderOrganization1 = array_keys($typeOrder, $organization1->getPrincipalType());
+            $orderOrganization2 = array_keys($typeOrder, $organization2->getPrincipalType());
+            if ($orderOrganization1 == $orderOrganization2) {
+                return 0;
+            }
+            return ($orderOrganization1 < $orderOrganization2) ? -1 : 1;
+        });
+
+        return $organizations;
+    }
+}

+ 1 - 1
src/Service/Network/Utils.php

@@ -21,7 +21,7 @@ class Utils
     /**
      * Test si l'organisation appartient au réseau de la CMF
      * @param Organization $organization
-     * @return bool TRUE if the organization belong to CMF net
+     * @return bool
      * @see UtilsTest::testIsCmf()
      */
     public function isCMF(Organization $organization): bool {

+ 3 - 8
src/Service/Security/Module.php

@@ -19,14 +19,9 @@ class Module
 {
     const OPENTALENT_CONFIG = __DIR__.'/../../../config/opentalent';
 
-    /** @var array  */
-    private $moduleConfig;
-
-    /** @var array */
-    private $moduleByConditionsConfig;
-
-    /** @var Reflection  */
-    private $reflection;
+    private array $moduleConfig;
+    private array $moduleByConditionsConfig;
+    private Reflection $reflection;
 
     public function __construct(Reflection $reflection)
     {

+ 1 - 2
src/Service/Utils/Reflection.php

@@ -11,8 +11,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
  */
 class Reflection
 {
-    /** @var ContainerInterface */
-    private $container;
+    private ContainerInterface $container;
 
     public function __construct(ContainerInterface $container)
     {

+ 18 - 22
tests/Service/Cotisation/UtilsTest.php

@@ -3,7 +3,7 @@
 namespace App\Tests\Service\Cotisation;
 
 use App\Entity\Organization\Organization;
-use App\Repository\Organization\OrganizationRepository;
+use App\Repository\Network\NetworkOrganizationRepository;
 use App\Service\Cotisation\Utils;
 use App\Service\Organization\Utils as OrganizationUtils;
 use PHPUnit\Framework\TestCase;
@@ -11,19 +11,15 @@ use \App\Service\Network\Utils as NetworkUtils;
 
 class UtilsTest extends TestCase
 {
-    private $organizationMock;
-
-    private $organizationRepositoryMock;
-
-    private $networkUtilsMock;
-
-    private $organizationUtilsMock;
+    private NetworkOrganizationRepository $networkOrganizationRepositoryMock;
+    private NetworkUtils $networkUtilsMock;
+    private OrganizationUtils $organizationUtilsMock;
 
     public function setUp(): void
     {
-        $this->organizationRepositoryMock =
+        $this->networkOrganizationRepositoryMock =
             $this
-                ->getMockBuilder(OrganizationRepository::class)
+                ->getMockBuilder(NetworkOrganizationRepository::class)
                 ->disableOriginalConstructor()
                 ->getMock();
 
@@ -49,7 +45,7 @@ class UtilsTest extends TestCase
             ->method('getId')
             ->willReturn(1);
 
-        $this->organizationRepositoryMock
+        $this->networkOrganizationRepositoryMock
                 ->expects($this->once())
                 ->method('isLastParent')
                 ->with($organizationMock)
@@ -61,7 +57,7 @@ class UtilsTest extends TestCase
                 ->with($organizationMock)
                 ->willReturn(true);
 
-        $utils = new Utils($this->networkUtilsMock, $this->organizationUtilsMock, $this->organizationRepositoryMock);
+        $utils = new Utils($this->networkUtilsMock, $this->organizationUtilsMock, $this->networkOrganizationRepositoryMock);
         $this->assertTrue($utils->isLastParentAndCMF($organizationMock));
     }
 
@@ -75,7 +71,7 @@ class UtilsTest extends TestCase
             ->method('getId')
             ->willReturn(1);
 
-        $this->organizationRepositoryMock
+        $this->networkOrganizationRepositoryMock
             ->expects($this->once())
             ->method('isLastParent')
             ->with($organizationMock)
@@ -85,7 +81,7 @@ class UtilsTest extends TestCase
             ->expects($this->never())
             ->method('isCMF');
 
-        $utils = new Utils($this->networkUtilsMock, $this->organizationUtilsMock, $this->organizationRepositoryMock);
+        $utils = new Utils($this->networkUtilsMock, $this->organizationUtilsMock, $this->networkOrganizationRepositoryMock);
         $this->assertFalse($utils->isLastParentAndCMF($organizationMock));
     }
 
@@ -108,7 +104,7 @@ class UtilsTest extends TestCase
             ->with($organizationMock)
             ->willReturn(true);
 
-        $utils = new Utils($this->networkUtilsMock, $this->organizationUtilsMock, $this->organizationRepositoryMock);
+        $utils = new Utils($this->networkUtilsMock, $this->organizationUtilsMock, $this->networkOrganizationRepositoryMock);
         $this->assertTrue($utils->isStructureAndCMF($organizationMock));
     }
 
@@ -129,7 +125,7 @@ class UtilsTest extends TestCase
             ->expects($this->never())
             ->method('isCMF');
 
-        $utils = new Utils($this->networkUtilsMock, $this->organizationUtilsMock, $this->organizationRepositoryMock);
+        $utils = new Utils($this->networkUtilsMock, $this->organizationUtilsMock, $this->networkOrganizationRepositoryMock);
         $this->assertFalse($utils->isStructureAndCMF($organizationMock));
     }
 
@@ -152,7 +148,7 @@ class UtilsTest extends TestCase
             ->with($organizationMock)
             ->willReturn(true);
 
-        $utils = new Utils($this->networkUtilsMock, $this->organizationUtilsMock, $this->organizationRepositoryMock);
+        $utils = new Utils($this->networkUtilsMock, $this->organizationUtilsMock, $this->networkOrganizationRepositoryMock);
         $this->assertTrue($utils->isManagerAndCMF($organizationMock));
     }
 
@@ -173,7 +169,7 @@ class UtilsTest extends TestCase
             ->expects($this->never())
             ->method('isCMF');
 
-        $utils = new Utils($this->networkUtilsMock, $this->organizationUtilsMock, $this->organizationRepositoryMock);
+        $utils = new Utils($this->networkUtilsMock, $this->organizationUtilsMock, $this->networkOrganizationRepositoryMock);
         $this->assertFalse($utils->isManagerAndCMF($organizationMock));
     }
 
@@ -193,7 +189,7 @@ class UtilsTest extends TestCase
             ->with($organizationMock)
             ->willReturn(true);
 
-        $this->organizationRepositoryMock
+        $this->networkOrganizationRepositoryMock
             ->expects($this->once())
             ->method('isLastParent')
             ->with($organizationMock)
@@ -203,7 +199,7 @@ class UtilsTest extends TestCase
             ->expects($this->never())
             ->method('isCMF');
 
-        $utils = new Utils($this->networkUtilsMock, $this->organizationUtilsMock, $this->organizationRepositoryMock);
+        $utils = new Utils($this->networkUtilsMock, $this->organizationUtilsMock, $this->networkOrganizationRepositoryMock);
         $this->assertTrue($utils->isManagerAndNotLastParentAndCMF($organizationMock));
     }
 
@@ -223,7 +219,7 @@ class UtilsTest extends TestCase
             ->with($organizationMock)
             ->willReturn(true);
 
-        $this->organizationRepositoryMock
+        $this->networkOrganizationRepositoryMock
             ->expects($this->once())
             ->method('isLastParent')
             ->with($organizationMock)
@@ -234,7 +230,7 @@ class UtilsTest extends TestCase
             ->method('isCMF')
             ->willReturn(true);
 
-        $utils = new Utils($this->networkUtilsMock, $this->organizationUtilsMock, $this->organizationRepositoryMock);
+        $utils = new Utils($this->networkUtilsMock, $this->organizationUtilsMock, $this->networkOrganizationRepositoryMock);
         $this->assertTrue($utils->isManagerAndLastParentAndCMF($organizationMock));
     }
 }

+ 81 - 0
tests/Service/Network/TreeTest.php

@@ -0,0 +1,81 @@
+<?php
+namespace App\Tests\Service\Network;
+
+use App\Entity\Organization\Organization;
+use App\Repository\Network\NetworkOrganizationRepository;
+use PHPUnit\Framework\TestCase;
+use App\Service\Network\Tree;
+
+class TreeTest extends TestCase
+{
+    private Tree $tree;
+    private NetworkOrganizationRepository $networkOrganizationRepositoryMock;
+
+    public function setUp():void
+    {
+        $this->networkOrganizationRepositoryMock =
+            $this
+                ->getMockBuilder(NetworkOrganizationRepository::class)
+                ->disableOriginalConstructor()
+                ->getMock();
+        $this->tree = new Tree($this->networkOrganizationRepositoryMock);
+    }
+
+    /**
+     * @see Tree::findAllParentsAndSortByType()
+     */
+    public function testFindAllParentsAndSortByType():void
+    {
+        $organizationMock = $this->getMockBuilder(Organization::class)->getMock();
+        $organizationMock
+            ->method('getId')
+            ->willReturn(1);
+        $this->networkOrganizationRepositoryMock
+            ->expects($this->once())
+            ->method('findAllParents')
+            ->with($organizationMock)
+            ->willReturn([$organizationMock]);
+
+        $treeMock = $this
+            ->getMockBuilder(Tree::class)
+            ->setConstructorArgs([$this->networkOrganizationRepositoryMock])
+            ->onlyMethods(['sortByType'])
+            ->getMock();
+        $treeMock
+            ->expects($this->once())
+            ->method('sortByType');
+        $treeMock->findAllParentsAndSortByType($organizationMock);
+    }
+
+    /**
+     * @see Tree::sortByType()
+     */
+    public function testSortByType():void
+    {
+        $organizationMock1 = $this->getMockBuilder(Organization::class)->getMock();
+        $organizationMock1->method('getId')->willReturn(2);
+        $organizationMock1->method('getPrincipalType')->willReturn('REGIONAL_FEDERATION');
+
+        $organizationMock2 = $this->getMockBuilder(Organization::class)->getMock();
+        $organizationMock2->method('getId')->willReturn(3);
+        $organizationMock2->method('getPrincipalType')->willReturn('NATIONAL_FEDERATION');
+
+        $organizationMock3 = $this->getMockBuilder(Organization::class)->getMock();
+        $organizationMock3->method('getId')->willReturn(4);
+        $organizationMock3->method('getPrincipalType')->willReturn('DEPARTEMENTAL_FEDERATION');
+
+        $organizations = [
+            $organizationMock2,
+            $organizationMock1,
+            $organizationMock3
+        ];
+
+        $result = $this->tree->sortByType($organizations);
+
+        $this->assertIsArray($result);
+        $this->assertContainsOnlyInstancesOf(Organization::class, $result);
+        $this->assertEquals(4, $result[0]->getId());
+        $this->assertEquals(2, $result[1]->getId());
+        $this->assertEquals(3, $result[2]->getId());
+    }
+}

+ 3 - 7
tests/Service/Network/UtilsTest.php

@@ -5,18 +5,14 @@ use App\Entity\Network\Network;
 use App\Entity\Network\NetworkOrganization;
 use App\Entity\Organization\Organization;
 use App\Enum\Network\NetworkEnum;
-use Doctrine\Common\Collections\ArrayCollection;
 use PHPUnit\Framework\TestCase;
 use App\Service\Network\Utils;
 
 class UtilsTest extends TestCase
 {
-    /** @var Utils */
-    private $utils;
-
-    private $organizationCmf;
-
-    private $organizationFfec;
+    private Utils $utils;
+    private Organization $organizationCmf;
+    private Organization $organizationFfec;
 
     public function setUp():void
     {

+ 3 - 8
tests/Service/Organization/UtilsTest.php

@@ -9,14 +9,9 @@ use PHPUnit\Framework\TestCase;
 
 class UtilsTest extends TestCase
 {
-    /** @var OrganizationUtils */
-    private $organizationUtils;
-
-    /** @var Organization */
-    private $organization;
-
-    /** @var Organization */
-    private $federation;
+    private OrganizationUtils $organizationUtils;
+    private Organization $organization;
+    private Organization $federation;
 
     public function setUp():void
     {

+ 1 - 1
tests/Service/Security/ModuleTest.php

@@ -9,7 +9,7 @@ use App\Service\Security\Module;
 
 class ModuleTest extends TestCase
 {
-    private $reflectionMock;
+    private Reflection $reflectionMock;
 
     public function setUp():void
     {