浏览代码

Finalisation des entités pour la page "fiche de la structure"

Vincent GUFFON 3 年之前
父节点
当前提交
7ae4f696ce

+ 1 - 0
config/opentalent/enum.yaml

@@ -166,6 +166,7 @@ opentalent:
   #Cotisation
     cotisation_function_enum_choices: 'App\Enum\Cotisation\CotisationFunctionEnum'
     type_of_practices_enum: 'App\Enum\Cotisation\TypeOfPracticeEnum'
+    category_type_of_practices_enum: 'App\Enum\Cotisation\CategoryTypeOfPracticeEnum'
 
   #OnlineRegistration
     onlineregistration_registration_status: 'App\Enum\OnlineRegistration\RegistrationStatus'

+ 47 - 0
src/Doctrine/Network/CurrentNetworkOrganizationExtension.php

@@ -0,0 +1,47 @@
+<?php
+declare(strict_types=1);
+
+namespace App\Doctrine\Network;
+
+use ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\QueryCollectionExtensionInterface;
+use ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\QueryItemExtensionInterface;
+use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryNameGeneratorInterface;
+use App\Entity\Access\Access;
+use App\Entity\Network\NetworkOrganization;
+use Doctrine\ORM\QueryBuilder;
+use Symfony\Component\Security\Core\Security;
+
+/**
+ * Class CurrentNetworkOrganizationExtension : Filtre de sécurité par défaut pour une resource NetworkOrganization
+ * @package App\Doctrine\Core
+ */
+final class CurrentNetworkOrganizationExtension implements QueryCollectionExtensionInterface, QueryItemExtensionInterface
+{
+    public function __construct(private Security $security)
+    { }
+
+    public function applyToCollection(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, string $operationName = null): void
+    {
+        $this->addWhere($queryBuilder, $resourceClass, $operationName);
+    }
+
+    public function applyToItem(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, array $identifiers, string $operationName = null, array $context = []): void
+    {
+        $this->addWhere($queryBuilder, $resourceClass, $operationName);
+    }
+
+    private function addWhere(QueryBuilder $queryBuilder, string $resourceClass, string $operationName): void
+    {
+        if (NetworkOrganization::class !== $resourceClass) {
+            return;
+        }
+
+        /** @var Access $currentUser */
+        $currentUser = $this->security->getUser();
+        $rootAlias = $queryBuilder->getRootAliases()[0];
+        $queryBuilder
+            ->andWhere(sprintf('%s.organization = :organization', $rootAlias))
+            ->setParameter('organization', $currentUser->getOrganization())
+        ;
+    }
+}

+ 47 - 0
src/Doctrine/Organization/CurrentOrganizationArticleExtension.php

@@ -0,0 +1,47 @@
+<?php
+declare(strict_types=1);
+
+namespace App\Doctrine\Organization;
+
+use ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\QueryCollectionExtensionInterface;
+use ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\QueryItemExtensionInterface;
+use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryNameGeneratorInterface;
+use App\Entity\Access\Access;
+use App\Entity\Organization\OrganizationArticle;
+use Doctrine\ORM\QueryBuilder;
+use Symfony\Component\Security\Core\Security;
+
+/**
+ * Class CurrentOrganizationArticleExtension : Filtre de sécurité par défaut pour une resource OrganizationArticle
+ * @package App\Doctrine\Core
+ */
+final class CurrentOrganizationArticleExtension implements QueryCollectionExtensionInterface, QueryItemExtensionInterface
+{
+    public function __construct(private Security $security)
+    { }
+
+    public function applyToCollection(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, string $operationName = null): void
+    {
+        $this->addWhere($queryBuilder, $resourceClass, $operationName);
+    }
+
+    public function applyToItem(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, array $identifiers, string $operationName = null, array $context = []): void
+    {
+        $this->addWhere($queryBuilder, $resourceClass, $operationName);
+    }
+
+    private function addWhere(QueryBuilder $queryBuilder, string $resourceClass, string $operationName): void
+    {
+        if (OrganizationArticle::class !== $resourceClass) {
+            return;
+        }
+
+        /** @var Access $currentUser */
+        $currentUser = $this->security->getUser();
+        $rootAlias = $queryBuilder->getRootAliases()[0];
+        $queryBuilder
+            ->andWhere(sprintf('%s.organization = :organization', $rootAlias))
+            ->setParameter('organization', $currentUser->getOrganization())
+        ;
+    }
+}

+ 59 - 0
src/Entity/Billing/BillingSetting.php

@@ -0,0 +1,59 @@
+<?php
+declare(strict_types=1);
+
+namespace App\Entity\Billing;
+
+use ApiPlatform\Core\Annotation\ApiResource;
+use App\Entity\Organization\Organization;
+use App\Repository\Billing\BillingSettingRepository;
+use Doctrine\ORM\Mapping as ORM;
+
+#[ApiResource(
+    collectionOperations: [],
+    itemOperations: ['get']
+)]
+#[ORM\Entity(repositoryClass: BillingSettingRepository::class)]
+class BillingSetting
+{
+    #[ORM\Id]
+    #[ORM\Column]
+    #[ORM\GeneratedValue]
+    private ?int $id = null;
+
+    #[ORM\OneToOne(inversedBy: 'billingSetting')]
+    #[ORM\JoinColumn(nullable: false)]
+    private Organization $organization;
+
+    #[ORM\Column(options: ['default' => false])]
+    private bool $applyVat = false;
+
+    public function getId(): ?int
+    {
+        return $this->id;
+    }
+
+    public function getOrganization(): Organization
+    {
+        return $this->organization;
+    }
+
+    public function setOrganization(Organization $organization): self
+    {
+        $this->organization = $organization;
+
+        return $this;
+    }
+
+
+    public function getApplyVat(): bool
+    {
+        return $this->applyVat;
+    }
+
+    public function setApplyVat(bool $applyVat): self
+    {
+        $this->applyVat = $applyVat;
+
+        return $this;
+    }
+}

+ 5 - 2
src/Entity/Core/BankAccount.php

@@ -22,6 +22,9 @@ use Symfony\Component\Validator\Constraints as Assert;
         ],
         'put' => [
             'security' => 'is_granted("BANK_ACCOUNT_EDIT", object)'
+        ],
+        'delete' => [
+            'security' => 'is_granted("BANK_ACCOUNT_DELETE", object)'
         ]
     ]
 )]
@@ -57,7 +60,7 @@ class BankAccount
      * 0 => jamais facturé, 1 => facturé 1 fois, 2 => facturé plusieurs fois
      */
     #[ORM\Column(options: ['default'=>0])]
-    private int $countInvoiced;
+    private ?int $countInvoiced = 0;
 
     #[ORM\Column(length: 255, nullable: true)]
     private ?string $holder = null;
@@ -73,7 +76,7 @@ class BankAccount
     private ?string $rum = null;
 
     #[ORM\Column(type: 'date', nullable: true)]
-    private \DateTimeInterface $signatureDateSamplingMandate;
+    private ?\DateTimeInterface $signatureDateSamplingMandate;
 
     #[ORM\ManyToMany(targetEntity: Organization::class, inversedBy: 'bankAccounts')]
     #[ORM\JoinTable(name: 'organization_bankaccount')]

+ 0 - 16
src/Entity/Core/ContactPoint.php

@@ -236,14 +236,6 @@ class ContactPoint
         return $this;
     }
 
-    public function removeOrganizations(): self
-    {
-        foreach ($this->getOrganization() as $organization){
-            $this->removeOrganization($organization);
-        }
-        return $this;
-    }
-
     public function getPerson(): Collection
     {
         return $this->person;
@@ -267,12 +259,4 @@ class ContactPoint
 
         return $this;
     }
-
-    public function removePeople(): self
-    {
-        foreach ($this->getPerson() as $person){
-            $this->removePerson($person);
-        }
-        return $this;
-    }
 }

+ 85 - 0
src/Entity/Core/File.php

@@ -3,6 +3,8 @@ declare(strict_types=1);
 
 namespace App\Entity\Core;
 
+use ApiPlatform\Core\Annotation\ApiResource;
+use App\Entity\Organization\Organization;
 use App\Entity\Person\Person;
 use App\Repository\Core\FileRepository;
 use Doctrine\Common\Collections\ArrayCollection;
@@ -10,6 +12,13 @@ use Doctrine\Common\Collections\Collection;
 use Doctrine\ORM\Mapping as ORM;
 use JetBrains\PhpStorm\Pure;
 
+#[ApiResource(
+    collectionOperations: [],
+    itemOperations: [
+        'get',
+        'put'
+    ]
+)]
 #[ORM\Entity(repositoryClass: FileRepository::class)]
 class File
 {
@@ -30,12 +39,23 @@ class File
     #[ORM\Column(length: 255, nullable: true)]
     private ?string $mimeType = null;
 
+    #[ORM\Column(length: 255)]
+    private string $config;
+
     #[ORM\OneToMany(mappedBy: 'image', targetEntity: Person::class, orphanRemoval: true)]
     private Collection $personImages;
 
+    #[ORM\OneToMany(mappedBy: 'logo', targetEntity: Organization::class, orphanRemoval: true)]
+    private Collection $organizationLogos;
+
+    #[ORM\OneToMany(mappedBy: 'image', targetEntity: Organization::class, orphanRemoval: true)]
+    private Collection $organizationImages;
+
     #[Pure] public function __construct()
     {
         $this->personImages = new ArrayCollection();
+        $this->organizationLogos = new ArrayCollection();
+        $this->organizationImages = new ArrayCollection();
     }
 
     public function getId(): ?int
@@ -88,6 +108,17 @@ class File
         return $this;
     }
 
+    public function getConfig(): string
+    {
+        return $this->config;
+    }
+
+    public function setConfig(string $config): self
+    {
+        $this->config = $config;
+        return $this;
+    }
+
     public function getPersonImages(): Collection
     {
         return $this->personImages;
@@ -114,4 +145,58 @@ class File
 
         return $this;
     }
+
+    public function getOrganizationLogos(): Collection
+    {
+        return $this->organizationLogos;
+    }
+
+    public function addOrganizationLogo(Organization $organization): self
+    {
+        if (!$this->organizationLogos->contains($organization)) {
+            $this->organizationLogos[] = $organization;
+            $organization->setLogo($this);
+        }
+
+        return $this;
+    }
+
+    public function removeOrganizationLogo(Organization $organization): self
+    {
+        if ($this->organizationLogos->removeElement($organization)) {
+            // set the owning side to null (unless already changed)
+            if ($organization->getLogo() === $this) {
+                $organization->setLogo(null);
+            }
+        }
+
+        return $this;
+    }
+
+    public function getOrganizationImages(): Collection
+    {
+        return $this->organizationImages;
+    }
+
+    public function addOrganizationImage(Organization $organization): self
+    {
+        if (!$this->organizationImages->contains($organization)) {
+            $this->organizationImages[] = $organization;
+            $organization->setImage($this);
+        }
+
+        return $this;
+    }
+
+    public function removeOrganizationImage(Organization $organization): self
+    {
+        if ($this->organizationImages->removeElement($organization)) {
+            // set the owning side to null (unless already changed)
+            if ($organization->getImage() === $this) {
+                $organization->setImage(null);
+            }
+        }
+
+        return $this;
+    }
 }

+ 9 - 0
src/Entity/Network/Network.php

@@ -3,21 +3,30 @@ declare(strict_types=1);
 
 namespace App\Entity\Network;
 
+use ApiPlatform\Core\Annotation\ApiResource;
 use App\Repository\Network\NetworkRepository;
 use Doctrine\ORM\Mapping as ORM;
+use Symfony\Component\Serializer\Annotation\Groups;
 
 /**
  * Enum des différents réseaux auxquels peut appartenir une Organization
  */
+#[ApiResource(
+    collectionOperations: ["get"],
+    itemOperations: ['get'],
+    attributes: ["security" => "is_granted('ROLE_ORGANIZATION_VIEW')"]
+)]
 #[ORM\Entity(repositoryClass: NetworkRepository::class)]
 class Network
 {
     #[ORM\Id]
     #[ORM\Column]
     #[ORM\GeneratedValue]
+    #[Groups("network")]
     private ?int $id = null;
 
     #[ORM\Column(length: 255)]
+    #[Groups("network")]
     private string $name;
 
     #[ORM\Column(length: 255, nullable: true)]

+ 46 - 5
src/Entity/Network/NetworkOrganization.php

@@ -4,27 +4,42 @@ declare(strict_types=1);
 namespace App\Entity\Network;
 
 use ApiPlatform\Core\Annotation\ApiResource;
+use App\Annotation\DateTimeConstraintAware;
 use App\Entity\Organization\Organization;
 use App\Repository\Network\NetworkOrganizationRepository;
-use App\Entity\Traits\ActivityPeriodTrait;
 use Doctrine\ORM\Mapping as ORM;
+use Symfony\Component\Serializer\Annotation\Groups;
 
 /**
  * Fait le lien entre une Organization et un Network
  */
-#[ApiResource]
+#[ApiResource(
+    collectionOperations: [
+        "get" => ["security" => "is_granted('ROLE_ORGANIZATION_VIEW')"]
+    ],
+    itemOperations: [
+        'get' => [
+            'security' => 'is_granted("ROLE_ORGANIZATION_VIEW" and object.getOrganization().getId() == user.getOrganization().getId()'
+        ]
+    ],
+    attributes: ["security" => "is_granted('ROLE_ORGANIZATION')"],
+    normalizationContext: [
+        'groups' => ['network'],
+    ]
+)]
 #[ORM\Entity(repositoryClass: NetworkOrganizationRepository::class)]
+#[DateTimeConstraintAware(startDateFieldName: "startDate", endDateFieldName: "endDate")]
 class NetworkOrganization
 {
-    use ActivityPeriodTrait;
-
     #[ORM\Id]
     #[ORM\Column]
     #[ORM\GeneratedValue]
+    #[Groups("network")]
     private ?int $id = null;
 
     #[ORM\ManyToOne]
     #[ORM\JoinColumn(nullable: true)]
+    #[Groups("network")]
     private Network $network;
 
     #[ORM\ManyToOne(inversedBy: 'networkOrganizations')]
@@ -32,11 +47,18 @@ class NetworkOrganization
     private Organization $organization;
 
     #[ORM\ManyToOne(inversedBy: 'networkOrganizationChildren')]
-    private Organization $parent;
+    private ?Organization $parent;
 
     #[ORM\Column(length: 255, nullable: true)]
     private ?string $leadingCause = null;
 
+    #[ORM\Column(type: 'date', nullable: true)]
+    #[Groups("network")]
+    private ?\DateTimeInterface $startDate = null;
+
+    #[ORM\Column(type: 'date', nullable: true)]
+    private ?\DateTimeInterface $endDate = null;
+
     public function getId(): ?int
     {
         return $this->id;
@@ -89,4 +111,23 @@ class NetworkOrganization
 
         return $this;
     }
+
+    public function getStartDate(): ?\DateTimeInterface {
+        return $this->startDate;
+    }
+
+    public function setStartDate(?\DateTime $startDate = null): self {
+        if($startDate == null) $startDate = new \DateTime();
+        $this->startDate = $startDate;
+        return $this;
+    }
+
+    public function getEndDate(): ?\DateTimeInterface {
+        return $this->endDate;
+    }
+
+    public function setEndDate(?\DateTime $endDate = null) :self {
+        $this->endDate = $endDate;
+        return $this;
+    }
 }

+ 125 - 1
src/Entity/Organization/Organization.php

@@ -5,8 +5,10 @@ namespace App\Entity\Organization;
 
 use ApiPlatform\Core\Annotation\ApiResource;
 use ApiPlatform\Core\Annotation\ApiSubresource;
+use App\Entity\Billing\BillingSetting;
 use App\Entity\Core\BankAccount;
 use App\Entity\Core\ContactPoint;
+use App\Entity\Core\File;
 use App\Entity\Network\NetworkOrganization;
 use App\Repository\Organization\OrganizationRepository;
 use Doctrine\Common\Collections\ArrayCollection;
@@ -36,7 +38,7 @@ class Organization
     #[ORM\GeneratedValue]
     private ?int $id = null;
 
-    #[ORM\Column(length: 128)]
+    #[ORM\Column(length: 128, nullable: false)]
     public string $name;
 
     #[ORM\Column(length: 128)]
@@ -54,6 +56,7 @@ class Organization
     private Settings $settings;
 
     #[ORM\OneToMany(mappedBy: 'organization', targetEntity: NetworkOrganization::class, orphanRemoval: true)]
+    #[ApiSubresource]
     private Collection $networkOrganizations;
 
     #[ORM\OneToMany(mappedBy: 'parent', targetEntity: NetworkOrganization::class, orphanRemoval: true)]
@@ -63,6 +66,9 @@ class Organization
     #[ORM\JoinColumn(nullable: false)]
     private Parameters $parameters;
 
+    #[ORM\OneToOne(mappedBy: 'organization', cascade: ['persist', 'remove'], orphanRemoval: true)]
+    private BillingSetting $billingSetting;
+
     #[ORM\Column(length: 255, nullable: true)]
     private ?string $description = null;
 
@@ -96,6 +102,9 @@ class Organization
     #[ORM\Column(length: 255, nullable: true)]
     private ?string $twitter = null;
 
+    #[ORM\Column(length: 255, nullable: true)]
+    private ?string $youtube = null;
+
     #[ORM\Column(length: 255, nullable: true)]
     private ?string $instagram = null;
 
@@ -164,6 +173,18 @@ class Organization
     #[ORM\Column(nullable: true)]
     private ?int $cmsId = null;
 
+    #[ORM\ManyToOne(inversedBy: 'organizationLogos')]
+    #[ORM\JoinColumn(referencedColumnName: 'id', nullable: true, onDelete: 'SET NULL')]
+    private ?File $logo = null;
+
+    #[ORM\ManyToOne(inversedBy: 'organizationLogos')]
+    #[ORM\JoinColumn(referencedColumnName: 'id', nullable: true, onDelete: 'SET NULL')]
+    private ?File $image = null;
+
+    #[ORM\ManyToMany(targetEntity: TypeOfPractice::class, mappedBy: 'organizations')]
+    #[ApiSubresource]
+    private Collection $typeOfPractices;
+
     #[ORM\Column(nullable: true)]
     private ?string $otherPractice = null;
 
@@ -182,14 +203,20 @@ class Organization
     #[ORM\OneToMany(mappedBy: 'organization', targetEntity: OrganizationLicence::class, orphanRemoval: true)]
     private Collection $organizationLicences;
 
+    #[ORM\OneToMany(mappedBy: 'organization', targetEntity: OrganizationArticle::class, orphanRemoval: true)]
+    #[ApiSubresource]
+    private Collection $organizationArticles;
+
     #[Pure] public function __construct()
     {
         $this->networkOrganizations = new ArrayCollection();
         $this->networkOrganizationChildren = new ArrayCollection();
+        $this->typeOfPractices = new ArrayCollection();
         $this->contactPoints = new ArrayCollection();
         $this->bankAccounts = new ArrayCollection();
         $this->organizationAddressPostals = new ArrayCollection();
         $this->organizationLicences = new ArrayCollection();
+        $this->organizationArticles = new ArrayCollection();
     }
 
     public function getId(): ?int
@@ -328,6 +355,18 @@ class Organization
         return $this;
     }
 
+    public function getBillingSetting(): BillingSetting
+    {
+        return $this->billingSetting;
+    }
+
+    public function setBillingSetting(BillingSetting $billingSetting): self
+    {
+        $this->billingSetting = $billingSetting;
+
+        return $this;
+    }
+
     public function getDescription(): ?string
     {
         return $this->description;
@@ -460,6 +499,18 @@ class Organization
         return $this;
     }
 
+    public function getYoutube(): ?string
+    {
+        return $this->youtube;
+    }
+
+    public function setYoutube(?string $youtube): self
+    {
+        $this->youtube = $youtube;
+
+        return $this;
+    }
+
     public function getInstagram(): ?string
     {
         return $this->instagram;
@@ -712,6 +763,52 @@ class Organization
         return $this;
     }
 
+    public function setLogo(?File $image):self
+    {
+        $this->logo = $image;
+        return $this;
+    }
+
+    public function getLogo(): ?File
+    {
+        return $this->logo;
+    }
+
+    public function setImage(?File $image):self
+    {
+        $this->image = $image;
+        return $this;
+    }
+
+    public function getImage(): ?File
+    {
+        return $this->image;
+    }
+
+    public function getTypeOfPractices(): Collection
+    {
+        return $this->typeOfPractices;
+    }
+
+    public function addTypeOfPractice(TypeOfPractice $typeOfPractice): self
+    {
+        if (!$this->typeOfPractices->contains($typeOfPractice)) {
+            $this->typeOfPractices[] = $typeOfPractice;
+            $typeOfPractice->addOrganization($this);
+        }
+
+        return $this;
+    }
+
+    public function removeTypeOfPractice(TypeOfPractice $typeOfPractice): self
+    {
+        if ($this->typeOfPractices->removeElement($typeOfPractice)) {
+            $typeOfPractice->removeOrganization($this);
+        }
+
+        return $this;
+    }
+
     public function getOtherPractice(): ?string
     {
         return $this->otherPractice;
@@ -825,4 +922,31 @@ class Organization
 
         return $this;
     }
+
+    public function getOrganizationArticles(): Collection
+    {
+        return $this->organizationArticles;
+    }
+
+    public function addOrganizationArticle(OrganizationArticle $organizationArticle): self
+    {
+        if (!$this->organizationArticles->contains($organizationArticle)) {
+            $this->organizationArticles[] = $organizationArticle;
+            $organizationArticle->setOrganization($this);
+        }
+
+        return $this;
+    }
+
+    public function removeOrganizationArticle(OrganizationArticle $organizationArticle): self
+    {
+        if ($this->organizationArticles->removeElement($organizationArticle)) {
+            // set the owning side to null (unless already changed)
+            if ($organizationArticle->getOrganization() === $this) {
+                $organizationArticle->setOrganization(null);
+            }
+        }
+
+        return $this;
+    }
 }

+ 96 - 0
src/Entity/Organization/OrganizationArticle.php

@@ -0,0 +1,96 @@
+<?php
+declare(strict_types=1);
+
+namespace App\Entity\Organization;
+
+use ApiPlatform\Core\Annotation\ApiResource;
+use App\Repository\Organization\OrganizationArticleRepository;
+use Doctrine\ORM\Mapping as ORM;
+use Symfony\Component\Serializer\Annotation\Groups;
+
+/**
+ * Fait le lien entre une Organization et un coup de projecteur
+ */
+#[ApiResource(
+    collectionOperations: [
+        "get" => ["security" => "is_granted('ROLE_ORGANIZATION_VIEW')"]
+    ],
+    itemOperations: [
+        'get' => [
+            'security' => 'is_granted("ROLE_ORGANIZATION_VIEW" and object.getOrganization().getId() == user.getOrganization().getId()'
+        ]
+    ],
+    attributes: ["security" => "is_granted('ROLE_ORGANIZATION')"]
+)]
+#[ORM\Entity(repositoryClass: OrganizationArticleRepository::class)]
+class OrganizationArticle
+{
+    #[ORM\Id]
+    #[ORM\Column]
+    #[ORM\GeneratedValue]
+    private ?int $id = null;
+
+    #[ORM\ManyToOne(inversedBy: 'organizationArticles')]
+    #[ORM\JoinColumn(nullable: true)]
+    private Organization $organization;
+
+    #[ORM\Column(length: 255)]
+    private string $title;
+
+    #[ORM\Column(length: 255)]
+    private string $link;
+
+    #[ORM\Column(type: 'date', nullable: true)]
+    private ?\DateTimeInterface $date = null;
+
+    public function getId(): ?int
+    {
+        return $this->id;
+    }
+
+    public function getOrganization(): ?Organization
+    {
+        return $this->organization;
+    }
+
+    public function setOrganization(?Organization $organization): self
+    {
+        $this->organization = $organization;
+
+        return $this;
+    }
+
+    public function getTitle(): ?string
+    {
+        return $this->title;
+    }
+
+    public function setTitle(?string $title): self
+    {
+        $this->title = $title;
+
+        return $this;
+    }
+
+    public function getLink(): ?string
+    {
+        return $this->link;
+    }
+
+    public function setLink(?string $link): self
+    {
+        $this->link = $link;
+
+        return $this;
+    }
+
+    public function getDate(): ?\DateTimeInterface {
+        return $this->date;
+    }
+
+    public function setDate(?\DateTime $date = null): self {
+        if($date == null) $date = new \DateTime();
+        $this->date = $date;
+        return $this;
+    }
+}

+ 4 - 1
src/Entity/Organization/Parameters.php

@@ -8,7 +8,10 @@ use App\Repository\Organization\ParametersRepository;
 use Doctrine\ORM\Mapping as ORM;
 use Symfony\Component\Validator\Constraints as Assert;
 
-#[ApiResource]
+#[ApiResource(
+    collectionOperations: [],
+    itemOperations: ['get']
+)]
 #[ORM\Entity(repositoryClass: ParametersRepository::class)]
 class Parameters
 {

+ 113 - 0
src/Entity/Organization/TypeOfPractice.php

@@ -0,0 +1,113 @@
+<?php
+declare(strict_types=1);
+
+namespace App\Entity\Organization;
+
+use ApiPlatform\Core\Annotation\ApiResource;
+use App\Repository\Organization\TypeOfPracticeRepository;
+use Doctrine\Common\Collections\ArrayCollection;
+use Doctrine\Common\Collections\Collection;
+use Doctrine\ORM\Mapping as ORM;
+use JetBrains\PhpStorm\Pure;
+use Symfony\Component\Serializer\Annotation\Groups;
+use Symfony\Component\Validator\Constraints as Assert;
+
+/**
+ * Type des pratique d'une organisation
+ */
+#[ApiResource(
+    collectionOperations: [
+        'get' => [
+            'normalization_context' => [
+                'groups' => ['read']
+            ]
+        ]
+    ],
+    itemOperations: ['get'],
+    attributes:[
+        'pagination_enabled' => false
+    ]
+)]
+#[ORM\Entity(repositoryClass: TypeOfPracticeRepository::class)]
+class TypeOfPractice
+{
+    #[ORM\Id]
+    #[ORM\Column]
+    #[ORM\GeneratedValue]
+    #[Groups(["read"])]
+    private ?int $id = null;
+
+    #[ORM\Column(length: 255, nullable: true)]
+    #[Assert\Choice(callback: ['\App\Enum\Cotisation\TypeOfPracticeEnum', 'toArray'], message: 'invalid-name')]
+    #[Groups(["read"])]
+    private ?string $name = null;
+
+    #[ORM\Column(length: 255, nullable: true)]
+    #[Assert\Choice(callback: ['\App\Enum\Cotisation\CategoryTypeOfPracticeEnum', 'toArray'], message: 'invalid-category')]
+    #[Groups(["read"])]
+    private ?string $category = null;
+
+    #[ORM\ManyToMany(targetEntity: Organization::class, inversedBy: 'typeOfPractices')]
+    #[ORM\JoinTable(name: 'organization_type_of_practices')]
+    #[ORM\JoinColumn(name: 'typeofpractice_id', referencedColumnName: 'id')]
+    #[ORM\InverseJoinColumn(name: 'organization_id', referencedColumnName: 'id')]
+    private Collection $organizations;
+
+    #[Pure] public function __construct()
+    {
+        $this->organizations = new ArrayCollection();
+    }
+
+    public function getId(): ?int
+    {
+        return $this->id;
+    }
+
+    public function getName(): ?string
+    {
+        return $this->name;
+    }
+
+    public function setName(?string $name): self
+    {
+        $this->name = $name;
+
+        return $this;
+    }
+
+    public function getCategory(): ?string
+    {
+        return $this->category;
+    }
+
+    public function setCategory(?string $category): self
+    {
+        $this->category = $category;
+
+        return $this;
+    }
+
+    public function getOrganizations(): Collection
+    {
+        return $this->organizations;
+    }
+
+    public function addOrganization(Organization $organization): self
+    {
+        if (!$this->organizations->contains($organization)) {
+            $this->organizations[] = $organization;
+            $organization->addTypeOfPractice($this);
+        }
+
+        return $this;
+    }
+
+    public function removeOrganization(Organization $organization): self
+    {
+        if ($this->organizations->removeElement($organization)) {
+            $organization->removeTypeOfPractice($this);
+        }
+
+        return $this;
+    }
+}

+ 18 - 0
src/Enum/Cotisation/CategoryTypeOfPracticeEnum.php

@@ -0,0 +1,18 @@
+<?php
+declare(strict_types=1);
+
+namespace App\Enum\Cotisation;
+
+use MyCLabs\Enum\Enum;
+
+/**
+ * catgorie des types de pratiques
+ */
+class CategoryTypeOfPracticeEnum extends Enum
+{
+    private const CATEGORY_ORCHESTRE = 'CATEGORY_ORCHESTRE';
+    private const CATEGORY_AMBULATORY = 'CATEGORY_AMBULATORY';
+    private const CATEGORY_CHORUS = 'CATEGORY_CHORUS';
+    private const CATEGORY_BAND = 'CATEGORY_BAND';
+    private const CATEGORY_OTHER = 'CATEGORY_OTHER';
+}

+ 51 - 0
src/Enum/Cotisation/TypeOfPracticeEnum.php

@@ -0,0 +1,51 @@
+<?php
+declare(strict_types=1);
+
+namespace App\Enum\Cotisation;
+
+use MyCLabs\Enum\Enum;
+
+/**
+ * noms des types de pratiques
+ */
+class TypeOfPracticeEnum extends Enum
+{
+    private const BATTERY_FANFARE = 'BATTERY_FANFARE';
+    private const BIG_BAND = 'BIG_BAND';
+    private const BRASS_BAND = 'BRASS_BAND';
+    private const MIXED_CHORUS = 'MIXED_CHORUS';
+    private const FEMAL_CHOIR = 'FEMAL_CHOIR';
+    private const MENS_CHOIR = 'MENS_CHOIR';
+    private const CHILDRENS_CHOIR = 'CHILDRENS_CHOIR';
+    private const ORCHESTRA_CLASS = 'ORCHESTRA_CLASS';
+    private const COPPER_BAND = 'COPPER_BAND';
+    private const JAZZ_BAND = 'JAZZ_BAND';
+    private const PERCUSSION_BAND = 'PERCUSSION_BAND';
+    private const PLUCKED_ORCHESTRA = 'PLUCKED_ORCHESTRA';
+    private const FOLKLORIC_BAND = 'FOLKLORIC_BAND';
+    private const VOCAL_BAND_UP_16 = 'VOCAL_BAND_UP_16';
+    private const FIFE_AND_DRUM = 'FIFE_AND_DRUM';
+    private const CURRENT_MUSIC_GROUP = 'CURRENT_MUSIC_GROUP';
+    private const CHAMBER_MUSIC_ENSEMBLE = 'CHAMBER_MUSIC_ENSEMBLE';
+    private const TRADITIONAL_MUSIC_ENSEMBLE = 'TRADITIONAL_MUSIC_ENSEMBLE';
+    private const VARIOUS_ORCHESTRA = 'VARIOUS_ORCHESTRA';
+    private const ACCORDION_ORCHESTRA = 'ACCORDION_ORCHESTRA';
+    private const HARMONY_ORCHESTRA = 'HARMONY_ORCHESTRA';
+    private const FANFARE_BAND = 'FANFARE_BAND';
+    private const SYMPHONY_ORCHESTRA = 'SYMPHONY_ORCHESTRA';
+    private const VIOLIN_BAND = 'VIOLIN_BAND';
+    private const SAXOPHONES_BAND = 'SAXOPHONES_BAND';
+    private const HUNTING_HORNS = 'HUNTING_HORNS';
+    private const STRING_ORCHESTRA = 'STRING_ORCHESTRA';
+    private const FLUTE_ENSEMBLE = 'FLUTE_ENSEMBLE';
+    private const CLARINET_CHOIR = 'CLARINET_CHOIR';
+    private const PHILHARMONIC_ORCHESTRA = 'PHILHARMONIC_ORCHESTRA';
+    private const BANDAS = 'BANDAS';
+    private const BAGAD = 'BAGAD';
+    private const BATTUCADA = 'BATTUCADA';
+    private const MARCHING_BAND = 'MARCHING_BAND';
+    private const EDUCATION = "EDUCATION";
+    private const CHEERLEADER = "CHEERLEADER";
+    private const TROOP = "TROOP";
+    private const OTHER = "OTHER";
+}

+ 16 - 0
src/Repository/Billing/BillingSettingRepository.php

@@ -0,0 +1,16 @@
+<?php
+declare(strict_types=1);
+
+namespace App\Repository\Billing;
+
+use App\Entity\Billing\BillingSetting;
+use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
+use Doctrine\Persistence\ManagerRegistry;
+
+class BillingSettingRepository extends ServiceEntityRepository
+{
+    public function __construct(ManagerRegistry $registry)
+    {
+        parent::__construct($registry, BillingSetting::class);
+    }
+}

+ 18 - 0
src/Repository/Organization/OrganizationArticleRepository.php

@@ -0,0 +1,18 @@
+<?php
+declare(strict_types=1);
+
+namespace App\Repository\Organization;
+
+use App\Entity\Organization\OrganizationArticle;
+use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
+use Doctrine\Persistence\ManagerRegistry;
+
+/**
+ */
+class OrganizationArticleRepository extends ServiceEntityRepository
+{
+    public function __construct(ManagerRegistry $registry)
+    {
+        parent::__construct($registry, OrganizationArticle::class);
+    }
+}

+ 18 - 0
src/Repository/Organization/TypeOfPracticeRepository.php

@@ -0,0 +1,18 @@
+<?php
+declare(strict_types=1);
+
+namespace App\Repository\Organization;
+
+use App\Entity\Organization\TypeOfPractice;
+use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
+use Doctrine\Persistence\ManagerRegistry;
+
+/**
+ */
+class TypeOfPracticeRepository extends ServiceEntityRepository
+{
+    public function __construct(ManagerRegistry $registry)
+    {
+        parent::__construct($registry, TypeOfPractice::class);
+    }
+}

+ 2 - 1
src/Security/Voter/BankAccountVoter.php

@@ -17,7 +17,7 @@ class BankAccountVoter extends Voter
 
     protected function supports($attribute, $subject): bool
     {
-        return in_array($attribute, ['BANK_ACCOUNT_READ', 'BANK_ACCOUNT_EDIT'])
+        return in_array($attribute, ['BANK_ACCOUNT_READ', 'BANK_ACCOUNT_EDIT', 'BANK_ACCOUNT_DELETE'])
             && $subject instanceof BankAccount;
     }
 
@@ -44,6 +44,7 @@ class BankAccountVoter extends Voter
                 }
                 break;
             case 'BANK_ACCOUNT_EDIT':
+            case 'BANK_ACCOUNT_DELETE':
                 if($subject->getOrganization()->count() === 1){
                     return $this->security->isGranted('ROLE_ORGANIZATION')
                         && $subject->getOrganization()->current()->getId() === $user->getOrganization()->getId();