feat(project-management) : migrate core Projects/Tasks domain into module (back)
Tranche 2 of LST-65. Mechanical, behaviour-preserving move of the core
business domain into src/Module/ProjectManagement/. API operations,
securities, uriTemplates and the 38 MCP tool names are all unchanged.
- 10 entities + 2 enums moved to Domain/{Entity,Enum}; intra-module
relations stay concrete, cross-module relations go through contracts
(Project.client -> ClientInterface, Task/TaskDocument users ->
UserInterface).
- 9 repositories split into Domain/Repository interfaces + Doctrine impls,
bound in services.yaml; consumers inject the interfaces. find() kept off
the interfaces (ServiceEntityRepository ?object compat) -> findById().
- State (7), MCP tools (38), controller, CalDavService/RecurrenceCalculator,
3 Doctrine listeners and SwitchWorkflowOutput moved under Infrastructure/.
- doctrine.yaml: ProjectManagement mapping + resolve_target_entities of the
3 module contracts repointed to the module (ClientInterface stays legacy).
- ProjectManagementModule registered (id project-management, 4 RBAC perms,
not re-wired); sidebar my-tasks/projects gated by the module.
- Legacy not-yet-modularised consumers (Mail/Gitea/BookStack, Serializer,
fixtures, tests) swapped to the module FQCN — transitional coupling to be
cleaned in 2.4/2.5/2.6.
159 tests green, mapping valid, no API route regression, cs-fixer clean.
This commit is contained in:
@@ -10,6 +10,7 @@ use ApiPlatform\Metadata\Get;
|
||||
use ApiPlatform\Metadata\GetCollection;
|
||||
use ApiPlatform\Metadata\Patch;
|
||||
use ApiPlatform\Metadata\Post;
|
||||
use App\Module\ProjectManagement\Domain\Entity\Project;
|
||||
use App\Repository\ClientRepository;
|
||||
use App\Shared\Domain\Contract\ClientInterface;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
|
||||
@@ -1,272 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use ApiPlatform\Doctrine\Orm\Filter\BooleanFilter;
|
||||
use ApiPlatform\Metadata\ApiFilter;
|
||||
use ApiPlatform\Metadata\ApiResource;
|
||||
use ApiPlatform\Metadata\Delete;
|
||||
use ApiPlatform\Metadata\Get;
|
||||
use ApiPlatform\Metadata\GetCollection;
|
||||
use ApiPlatform\Metadata\Link;
|
||||
use ApiPlatform\Metadata\Patch;
|
||||
use ApiPlatform\Metadata\Post;
|
||||
use App\ApiResource\SwitchWorkflowOutput;
|
||||
use App\Repository\ProjectRepository;
|
||||
use App\Shared\Domain\Contract\ClientInterface;
|
||||
use App\Shared\Domain\Contract\ProjectInterface;
|
||||
use App\State\SwitchProjectWorkflowProcessor;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
|
||||
use Symfony\Component\Serializer\Attribute\Groups;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
#[ApiResource(
|
||||
operations: [
|
||||
new GetCollection(paginationEnabled: false, security: "is_granted('ROLE_USER')"),
|
||||
new Get(security: "is_granted('ROLE_USER')"),
|
||||
new Post(
|
||||
security: "is_granted('ROLE_ADMIN')",
|
||||
denormalizationContext: ['groups' => ['project:write', 'project:create']],
|
||||
),
|
||||
new Patch(security: "is_granted('ROLE_ADMIN')"),
|
||||
new Delete(security: "is_granted('ROLE_ADMIN')"),
|
||||
new Post(
|
||||
uriTemplate: '/projects/{id}/switch-workflow',
|
||||
uriVariables: ['id' => new Link(fromClass: Project::class)],
|
||||
security: "is_granted('ROLE_ADMIN')",
|
||||
input: false,
|
||||
output: SwitchWorkflowOutput::class,
|
||||
normalizationContext: ['groups' => ['switch_workflow:read']],
|
||||
processor: SwitchProjectWorkflowProcessor::class,
|
||||
read: true,
|
||||
deserialize: false,
|
||||
validate: false,
|
||||
name: 'switch_workflow',
|
||||
),
|
||||
],
|
||||
normalizationContext: ['groups' => ['project:read']],
|
||||
denormalizationContext: ['groups' => ['project:write']],
|
||||
order: ['name' => 'ASC'],
|
||||
)]
|
||||
#[ApiFilter(BooleanFilter::class, properties: ['archived'])]
|
||||
#[ORM\Entity(repositoryClass: ProjectRepository::class)]
|
||||
#[UniqueEntity(fields: ['code'], message: 'Ce code de projet est déjà utilisé.')]
|
||||
class Project implements ProjectInterface
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
#[Groups(['project:read', 'time_entry:read', 'task:read', 'me:read', 'user:list'])]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column(length: 10, unique: true)]
|
||||
#[Groups(['project:read', 'project:create', 'task:read'])]
|
||||
#[Assert\NotBlank]
|
||||
#[Assert\Regex(pattern: '/^[A-Z]{2,10}$/', message: 'Le code doit contenir entre 2 et 10 lettres majuscules.')]
|
||||
private ?string $code = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
#[Groups(['project:read', 'project:write', 'time_entry:read', 'task:read', 'me:read', 'user:list'])]
|
||||
private ?string $name = null;
|
||||
|
||||
#[ORM\Column(type: 'text', nullable: true)]
|
||||
#[Groups(['project:read', 'project:write'])]
|
||||
private ?string $description = null;
|
||||
|
||||
#[ORM\Column(length: 7)]
|
||||
#[Groups(['project:read', 'project:write', 'time_entry:read', 'task:read'])]
|
||||
private ?string $color = '#222783';
|
||||
|
||||
#[ORM\ManyToOne(targetEntity: ClientInterface::class, inversedBy: 'projects')]
|
||||
#[ORM\JoinColumn(nullable: true, onDelete: 'SET NULL')]
|
||||
#[Groups(['project:read', 'project:write'])]
|
||||
private ?ClientInterface $client = null;
|
||||
|
||||
#[ORM\ManyToOne(targetEntity: Workflow::class)]
|
||||
#[ORM\JoinColumn(nullable: false, onDelete: 'RESTRICT')]
|
||||
#[Groups(['project:read', 'project:write', 'task:read'])]
|
||||
#[Assert\NotNull(message: 'Un projet doit avoir un workflow.')]
|
||||
private ?Workflow $workflow = null;
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
#[Groups(['project:read', 'project:write', 'task:read'])]
|
||||
private ?string $giteaOwner = null;
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
#[Groups(['project:read', 'project:write', 'task:read'])]
|
||||
private ?string $giteaRepo = null;
|
||||
|
||||
#[ORM\Column(nullable: true)]
|
||||
#[Groups(['project:read', 'project:write', 'task:read'])]
|
||||
private ?int $bookstackShelfId = null;
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
#[Groups(['project:read', 'project:write'])]
|
||||
private ?string $bookstackShelfName = null;
|
||||
|
||||
#[ORM\Column]
|
||||
#[Groups(['project:read', 'project:write'])]
|
||||
private bool $archived = false;
|
||||
|
||||
/** @var Collection<int, Task> */
|
||||
#[ORM\OneToMany(targetEntity: Task::class, mappedBy: 'project')]
|
||||
private Collection $tasks;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->tasks = new ArrayCollection();
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getCode(): ?string
|
||||
{
|
||||
return $this->code;
|
||||
}
|
||||
|
||||
public function setCode(string $code): static
|
||||
{
|
||||
$this->code = $code;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getName(): ?string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function setName(string $name): static
|
||||
{
|
||||
$this->name = $name;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDescription(): ?string
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
public function setDescription(?string $description): static
|
||||
{
|
||||
$this->description = $description;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getColor(): ?string
|
||||
{
|
||||
return $this->color;
|
||||
}
|
||||
|
||||
public function setColor(string $color): static
|
||||
{
|
||||
$this->color = $color;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getClient(): ?ClientInterface
|
||||
{
|
||||
return $this->client;
|
||||
}
|
||||
|
||||
public function setClient(?ClientInterface $client): static
|
||||
{
|
||||
$this->client = $client;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getGiteaOwner(): ?string
|
||||
{
|
||||
return $this->giteaOwner;
|
||||
}
|
||||
|
||||
public function setGiteaOwner(?string $giteaOwner): static
|
||||
{
|
||||
$this->giteaOwner = $giteaOwner;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getGiteaRepo(): ?string
|
||||
{
|
||||
return $this->giteaRepo;
|
||||
}
|
||||
|
||||
public function setGiteaRepo(?string $giteaRepo): static
|
||||
{
|
||||
$this->giteaRepo = $giteaRepo;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function hasGiteaRepo(): bool
|
||||
{
|
||||
return null !== $this->giteaOwner && null !== $this->giteaRepo;
|
||||
}
|
||||
|
||||
public function isArchived(): bool
|
||||
{
|
||||
return $this->archived;
|
||||
}
|
||||
|
||||
public function setArchived(bool $archived): static
|
||||
{
|
||||
$this->archived = $archived;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getBookstackShelfId(): ?int
|
||||
{
|
||||
return $this->bookstackShelfId;
|
||||
}
|
||||
|
||||
public function setBookstackShelfId(?int $bookstackShelfId): static
|
||||
{
|
||||
$this->bookstackShelfId = $bookstackShelfId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getBookstackShelfName(): ?string
|
||||
{
|
||||
return $this->bookstackShelfName;
|
||||
}
|
||||
|
||||
public function setBookstackShelfName(?string $bookstackShelfName): static
|
||||
{
|
||||
$this->bookstackShelfName = $bookstackShelfName;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getWorkflow(): ?Workflow
|
||||
{
|
||||
return $this->workflow;
|
||||
}
|
||||
|
||||
public function setWorkflow(Workflow $workflow): static
|
||||
{
|
||||
$this->workflow = $workflow;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
#[Groups(['project:read'])]
|
||||
public function getTaskCount(): int
|
||||
{
|
||||
return $this->tasks->count();
|
||||
}
|
||||
}
|
||||
@@ -1,488 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use ApiPlatform\Doctrine\Orm\Filter\BooleanFilter;
|
||||
use ApiPlatform\Doctrine\Orm\Filter\DateFilter;
|
||||
use ApiPlatform\Doctrine\Orm\Filter\OrderFilter;
|
||||
use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
|
||||
use ApiPlatform\Metadata\ApiFilter;
|
||||
use ApiPlatform\Metadata\ApiResource;
|
||||
use ApiPlatform\Metadata\Delete;
|
||||
use ApiPlatform\Metadata\Get;
|
||||
use ApiPlatform\Metadata\GetCollection;
|
||||
use ApiPlatform\Metadata\Patch;
|
||||
use ApiPlatform\Metadata\Post;
|
||||
use App\Repository\TaskRepository;
|
||||
use App\Shared\Domain\Contract\TaskInterface;
|
||||
use App\Shared\Domain\Contract\UserInterface;
|
||||
use App\State\TaskCalendarProcessor;
|
||||
use App\State\TaskNumberProcessor;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Serializer\Attribute\Groups;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
|
||||
#[ApiResource(
|
||||
operations: [
|
||||
new GetCollection(paginationEnabled: false, security: "is_granted('ROLE_USER')"),
|
||||
new Get(security: "is_granted('ROLE_USER')"),
|
||||
new Post(security: "is_granted('ROLE_ADMIN')", processor: TaskNumberProcessor::class),
|
||||
new Patch(security: "is_granted('ROLE_ADMIN')", processor: TaskCalendarProcessor::class),
|
||||
new Delete(security: "is_granted('ROLE_ADMIN')", processor: TaskCalendarProcessor::class),
|
||||
],
|
||||
normalizationContext: ['groups' => ['task:read']],
|
||||
denormalizationContext: ['groups' => ['task:write']],
|
||||
order: ['id' => 'DESC'],
|
||||
)]
|
||||
#[ApiFilter(SearchFilter::class, properties: ['project' => 'exact', 'group' => 'exact', 'assignee' => 'exact', 'collaborators' => 'exact', 'priority' => 'exact', 'effort' => 'exact', 'tags' => 'exact', 'status' => 'exact'])]
|
||||
#[ApiFilter(DateFilter::class, properties: ['scheduledStart', 'scheduledEnd', 'deadline'])]
|
||||
#[ApiFilter(BooleanFilter::class, properties: ['archived', 'syncToCalendar'])]
|
||||
#[ApiFilter(OrderFilter::class, properties: ['scheduledStart', 'deadline'])]
|
||||
#[ORM\Entity(repositoryClass: TaskRepository::class)]
|
||||
#[ORM\Table(name: 'task')]
|
||||
#[ORM\UniqueConstraint(name: 'uniq_task_project_number', columns: ['project_id', 'number'])]
|
||||
class Task implements TaskInterface
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
#[Groups(['task:read'])]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column(type: 'integer')]
|
||||
#[Groups(['task:read'])]
|
||||
private ?int $number = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
#[Groups(['task:read', 'task:write'])]
|
||||
private ?string $title = null;
|
||||
|
||||
#[ORM\Column(type: 'text', nullable: true)]
|
||||
#[Groups(['task:read', 'task:write'])]
|
||||
private ?string $description = null;
|
||||
|
||||
#[ORM\ManyToOne(targetEntity: TaskStatus::class)]
|
||||
#[ORM\JoinColumn(nullable: true, onDelete: 'SET NULL')]
|
||||
#[Groups(['task:read', 'task:write'])]
|
||||
private ?TaskStatus $status = null;
|
||||
|
||||
#[ORM\ManyToOne(targetEntity: TaskEffort::class)]
|
||||
#[ORM\JoinColumn(nullable: true, onDelete: 'SET NULL')]
|
||||
#[Groups(['task:read', 'task:write'])]
|
||||
private ?TaskEffort $effort = null;
|
||||
|
||||
#[ORM\ManyToOne(targetEntity: TaskPriority::class)]
|
||||
#[ORM\JoinColumn(nullable: true, onDelete: 'SET NULL')]
|
||||
#[Groups(['task:read', 'task:write'])]
|
||||
private ?TaskPriority $priority = null;
|
||||
|
||||
#[ORM\ManyToOne(targetEntity: UserInterface::class)]
|
||||
#[ORM\JoinColumn(nullable: true, onDelete: 'SET NULL')]
|
||||
#[Groups(['task:read', 'task:write'])]
|
||||
private ?UserInterface $assignee = null;
|
||||
|
||||
/** @var Collection<int, UserInterface> */
|
||||
#[ORM\ManyToMany(targetEntity: UserInterface::class)]
|
||||
#[ORM\JoinTable(
|
||||
name: 'task_collaborator',
|
||||
joinColumns: [new ORM\JoinColumn(name: 'task_id', referencedColumnName: 'id', onDelete: 'CASCADE')],
|
||||
inverseJoinColumns: [new ORM\JoinColumn(name: 'user_id', referencedColumnName: 'id', onDelete: 'CASCADE')],
|
||||
)]
|
||||
#[Groups(['task:read', 'task:write'])]
|
||||
private Collection $collaborators;
|
||||
|
||||
#[ORM\ManyToOne(targetEntity: TaskGroup::class)]
|
||||
#[ORM\JoinColumn(nullable: true, onDelete: 'SET NULL')]
|
||||
#[Groups(['task:read', 'task:write'])]
|
||||
private ?TaskGroup $group = null;
|
||||
|
||||
#[ORM\ManyToOne(targetEntity: Project::class, inversedBy: 'tasks')]
|
||||
#[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')]
|
||||
#[Groups(['task:read', 'task:write'])]
|
||||
private ?Project $project = null;
|
||||
|
||||
/** @var Collection<int, TaskTag> */
|
||||
#[ORM\ManyToMany(targetEntity: TaskTag::class)]
|
||||
#[ORM\JoinTable(
|
||||
name: 'task_task_type',
|
||||
joinColumns: [new ORM\JoinColumn(name: 'task_id', referencedColumnName: 'id')],
|
||||
inverseJoinColumns: [new ORM\JoinColumn(name: 'task_type_id', referencedColumnName: 'id')],
|
||||
)]
|
||||
#[Groups(['task:read', 'task:write'])]
|
||||
private Collection $tags;
|
||||
|
||||
#[ORM\Column(type: 'boolean')]
|
||||
#[Groups(['task:read', 'task:write'])]
|
||||
private bool $archived = false;
|
||||
|
||||
/** @var Collection<int, TaskDocument> */
|
||||
#[ORM\OneToMany(targetEntity: TaskDocument::class, mappedBy: 'task', cascade: ['remove'])]
|
||||
#[Groups(['task:read'])]
|
||||
private Collection $documents;
|
||||
|
||||
#[ORM\Column(type: 'datetime_immutable', nullable: true)]
|
||||
#[Groups(['task:read', 'task:write'])]
|
||||
private ?DateTimeImmutable $scheduledStart = null;
|
||||
|
||||
#[ORM\Column(type: 'datetime_immutable', nullable: true)]
|
||||
#[Groups(['task:read', 'task:write'])]
|
||||
private ?DateTimeImmutable $scheduledEnd = null;
|
||||
|
||||
#[ORM\Column(type: 'datetime_immutable', nullable: true)]
|
||||
#[Groups(['task:read', 'task:write'])]
|
||||
private ?DateTimeImmutable $deadline = null;
|
||||
|
||||
#[ORM\Column(type: 'boolean', options: ['default' => false])]
|
||||
#[Groups(['task:read', 'task:write'])]
|
||||
private bool $syncToCalendar = false;
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
private ?string $calendarEventUid = null;
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
private ?string $calendarTodoUid = null;
|
||||
|
||||
#[ORM\Column(type: 'text', nullable: true)]
|
||||
#[Groups(['task:read'])]
|
||||
private ?string $calendarSyncError = null;
|
||||
|
||||
#[ORM\ManyToOne(targetEntity: TaskRecurrence::class, inversedBy: 'tasks')]
|
||||
#[ORM\JoinColumn(nullable: true, onDelete: 'SET NULL')]
|
||||
#[Groups(['task:read', 'task:write'])]
|
||||
private ?TaskRecurrence $recurrence = null;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->tags = new ArrayCollection();
|
||||
$this->documents = new ArrayCollection();
|
||||
$this->collaborators = new ArrayCollection();
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getNumber(): ?int
|
||||
{
|
||||
return $this->number;
|
||||
}
|
||||
|
||||
public function setNumber(int $number): static
|
||||
{
|
||||
$this->number = $number;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTitle(): ?string
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function setTitle(string $title): static
|
||||
{
|
||||
$this->title = $title;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDescription(): ?string
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
public function setDescription(?string $description): static
|
||||
{
|
||||
$this->description = $description;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getStatus(): ?TaskStatus
|
||||
{
|
||||
return $this->status;
|
||||
}
|
||||
|
||||
public function setStatus(?TaskStatus $status): static
|
||||
{
|
||||
$this->status = $status;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getEffort(): ?TaskEffort
|
||||
{
|
||||
return $this->effort;
|
||||
}
|
||||
|
||||
public function setEffort(?TaskEffort $effort): static
|
||||
{
|
||||
$this->effort = $effort;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPriority(): ?TaskPriority
|
||||
{
|
||||
return $this->priority;
|
||||
}
|
||||
|
||||
public function setPriority(?TaskPriority $priority): static
|
||||
{
|
||||
$this->priority = $priority;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAssignee(): ?UserInterface
|
||||
{
|
||||
return $this->assignee;
|
||||
}
|
||||
|
||||
public function setAssignee(?UserInterface $assignee): static
|
||||
{
|
||||
$this->assignee = $assignee;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** @return Collection<int, UserInterface> */
|
||||
public function getCollaborators(): Collection
|
||||
{
|
||||
return $this->collaborators;
|
||||
}
|
||||
|
||||
public function addCollaborator(UserInterface $user): static
|
||||
{
|
||||
if (!$this->collaborators->contains($user)) {
|
||||
$this->collaborators->add($user);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeCollaborator(UserInterface $user): static
|
||||
{
|
||||
$this->collaborators->removeElement($user);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getGroup(): ?TaskGroup
|
||||
{
|
||||
return $this->group;
|
||||
}
|
||||
|
||||
public function setGroup(?TaskGroup $group): static
|
||||
{
|
||||
$this->group = $group;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getProject(): ?Project
|
||||
{
|
||||
return $this->project;
|
||||
}
|
||||
|
||||
public function setProject(?Project $project): static
|
||||
{
|
||||
$this->project = $project;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** @return Collection<int, TaskTag> */
|
||||
public function getTags(): Collection
|
||||
{
|
||||
return $this->tags;
|
||||
}
|
||||
|
||||
public function addTag(TaskTag $tag): static
|
||||
{
|
||||
if (!$this->tags->contains($tag)) {
|
||||
$this->tags->add($tag);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeTag(TaskTag $tag): static
|
||||
{
|
||||
$this->tags->removeElement($tag);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isArchived(): bool
|
||||
{
|
||||
return $this->archived;
|
||||
}
|
||||
|
||||
public function setArchived(bool $archived): static
|
||||
{
|
||||
$this->archived = $archived;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** @return Collection<int, TaskDocument> */
|
||||
public function getDocuments(): Collection
|
||||
{
|
||||
return $this->documents;
|
||||
}
|
||||
|
||||
public function getScheduledStart(): ?DateTimeImmutable
|
||||
{
|
||||
return $this->scheduledStart;
|
||||
}
|
||||
|
||||
public function setScheduledStart(?DateTimeImmutable $scheduledStart): static
|
||||
{
|
||||
$this->scheduledStart = $scheduledStart;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getScheduledEnd(): ?DateTimeImmutable
|
||||
{
|
||||
return $this->scheduledEnd;
|
||||
}
|
||||
|
||||
public function setScheduledEnd(?DateTimeImmutable $scheduledEnd): static
|
||||
{
|
||||
$this->scheduledEnd = $scheduledEnd;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDeadline(): ?DateTimeImmutable
|
||||
{
|
||||
return $this->deadline;
|
||||
}
|
||||
|
||||
public function setDeadline(?DateTimeImmutable $deadline): static
|
||||
{
|
||||
$this->deadline = $deadline;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isSyncToCalendar(): bool
|
||||
{
|
||||
return $this->syncToCalendar;
|
||||
}
|
||||
|
||||
public function setSyncToCalendar(bool $syncToCalendar): static
|
||||
{
|
||||
$this->syncToCalendar = $syncToCalendar;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCalendarEventUid(): ?string
|
||||
{
|
||||
return $this->calendarEventUid;
|
||||
}
|
||||
|
||||
public function setCalendarEventUid(?string $calendarEventUid): static
|
||||
{
|
||||
$this->calendarEventUid = $calendarEventUid;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCalendarTodoUid(): ?string
|
||||
{
|
||||
return $this->calendarTodoUid;
|
||||
}
|
||||
|
||||
public function setCalendarTodoUid(?string $calendarTodoUid): static
|
||||
{
|
||||
$this->calendarTodoUid = $calendarTodoUid;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCalendarSyncError(): ?string
|
||||
{
|
||||
return $this->calendarSyncError;
|
||||
}
|
||||
|
||||
public function setCalendarSyncError(?string $calendarSyncError): static
|
||||
{
|
||||
$this->calendarSyncError = $calendarSyncError;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getRecurrence(): ?TaskRecurrence
|
||||
{
|
||||
return $this->recurrence;
|
||||
}
|
||||
|
||||
public function setRecurrence(?TaskRecurrence $recurrence): static
|
||||
{
|
||||
$this->recurrence = $recurrence;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
#[Assert\Callback]
|
||||
public function validateScheduledDates(ExecutionContextInterface $context): void
|
||||
{
|
||||
if ((null === $this->scheduledStart) !== (null === $this->scheduledEnd)) {
|
||||
$context->buildViolation('scheduledStart and scheduledEnd must both be set or both be null.')
|
||||
->atPath('scheduledEnd')
|
||||
->addViolation()
|
||||
;
|
||||
}
|
||||
if (null !== $this->scheduledStart && null !== $this->scheduledEnd
|
||||
&& $this->scheduledEnd <= $this->scheduledStart) {
|
||||
$context->buildViolation('scheduledEnd must be after scheduledStart.')
|
||||
->atPath('scheduledEnd')
|
||||
->addViolation()
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
#[Assert\Callback]
|
||||
public function validateCollaborators(ExecutionContextInterface $context): void
|
||||
{
|
||||
if (null !== $this->assignee && $this->collaborators->contains($this->assignee)) {
|
||||
$context->buildViolation('The assignee cannot also be a collaborator.')
|
||||
->atPath('collaborators')
|
||||
->addViolation()
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
#[Assert\Callback]
|
||||
public function validateStatusBelongsToProjectWorkflow(ExecutionContextInterface $context): void
|
||||
{
|
||||
if (null === $this->status || null === $this->project) {
|
||||
return;
|
||||
}
|
||||
|
||||
$projectWorkflow = $this->project->getWorkflow();
|
||||
$statusWorkflow = $this->status->getWorkflow();
|
||||
|
||||
if (null === $projectWorkflow || null === $statusWorkflow) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($projectWorkflow->getId() !== $statusWorkflow->getId()) {
|
||||
$context->buildViolation('Status does not belong to this project\'s workflow.')
|
||||
->atPath('status')
|
||||
->addViolation()
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Module\ProjectManagement\Domain\Entity\Task;
|
||||
use App\Repository\TaskBookStackLinkRepository;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
@@ -1,191 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
|
||||
use ApiPlatform\Metadata\ApiFilter;
|
||||
use ApiPlatform\Metadata\ApiResource;
|
||||
use ApiPlatform\Metadata\Delete;
|
||||
use ApiPlatform\Metadata\Get;
|
||||
use ApiPlatform\Metadata\GetCollection;
|
||||
use ApiPlatform\Metadata\Post;
|
||||
use App\EventListener\TaskDocumentListener;
|
||||
use App\Shared\Domain\Contract\UserInterface;
|
||||
use App\State\TaskDocumentProcessor;
|
||||
use App\State\TaskDocumentProvider;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Serializer\Attribute\Groups;
|
||||
|
||||
#[ApiResource(
|
||||
operations: [
|
||||
new GetCollection(paginationEnabled: false, security: "is_granted('ROLE_USER')", provider: TaskDocumentProvider::class),
|
||||
new Get(security: "is_granted('ROLE_USER')", provider: TaskDocumentProvider::class),
|
||||
new Post(
|
||||
security: "is_granted('ROLE_ADMIN')",
|
||||
processor: TaskDocumentProcessor::class,
|
||||
deserialize: false,
|
||||
),
|
||||
new Delete(security: "is_granted('ROLE_ADMIN')"),
|
||||
],
|
||||
normalizationContext: ['groups' => ['task_document:read']],
|
||||
denormalizationContext: ['groups' => ['task_document:write']],
|
||||
order: ['id' => 'DESC'],
|
||||
)]
|
||||
#[ApiFilter(SearchFilter::class, properties: ['task' => 'exact'])]
|
||||
#[ORM\Entity]
|
||||
#[ORM\EntityListeners([TaskDocumentListener::class])]
|
||||
class TaskDocument
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
#[Groups(['task_document:read', 'task:read'])]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\ManyToOne(targetEntity: Task::class, inversedBy: 'documents')]
|
||||
#[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')]
|
||||
#[Groups(['task_document:read', 'task_document:write'])]
|
||||
private ?Task $task = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
#[Groups(['task_document:read', 'task:read'])]
|
||||
private ?string $originalName = null;
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
#[Groups(['task_document:read', 'task:read'])]
|
||||
private ?string $fileName = null;
|
||||
|
||||
/**
|
||||
* Chemin relatif sur le partage SMB lorsque le document est un lien vers un fichier du partage
|
||||
* (au lieu d'un fichier uploadé stocké sur disque). Mutuellement exclusif avec fileName.
|
||||
*/
|
||||
#[ORM\Column(length: 1024, nullable: true)]
|
||||
#[Groups(['task_document:read', 'task:read'])]
|
||||
private ?string $sharePath = null;
|
||||
|
||||
#[ORM\Column(length: 100)]
|
||||
#[Groups(['task_document:read', 'task:read'])]
|
||||
private ?string $mimeType = null;
|
||||
|
||||
#[ORM\Column]
|
||||
#[Groups(['task_document:read', 'task:read'])]
|
||||
private ?int $size = null;
|
||||
|
||||
#[ORM\Column(type: 'datetime_immutable')]
|
||||
#[Groups(['task_document:read', 'task:read'])]
|
||||
private ?DateTimeImmutable $createdAt = null;
|
||||
|
||||
#[ORM\ManyToOne(targetEntity: UserInterface::class)]
|
||||
#[ORM\JoinColumn(nullable: true, onDelete: 'SET NULL')]
|
||||
#[Groups(['task_document:read', 'task:read'])]
|
||||
private ?UserInterface $uploadedBy = null;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getTask(): ?Task
|
||||
{
|
||||
return $this->task;
|
||||
}
|
||||
|
||||
public function setTask(?Task $task): static
|
||||
{
|
||||
$this->task = $task;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getOriginalName(): ?string
|
||||
{
|
||||
return $this->originalName;
|
||||
}
|
||||
|
||||
public function setOriginalName(string $originalName): static
|
||||
{
|
||||
$this->originalName = $originalName;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getFileName(): ?string
|
||||
{
|
||||
return $this->fileName;
|
||||
}
|
||||
|
||||
public function setFileName(?string $fileName): static
|
||||
{
|
||||
$this->fileName = $fileName;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSharePath(): ?string
|
||||
{
|
||||
return $this->sharePath;
|
||||
}
|
||||
|
||||
public function setSharePath(?string $sharePath): static
|
||||
{
|
||||
$this->sharePath = $sharePath;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isShareLink(): bool
|
||||
{
|
||||
return null !== $this->sharePath;
|
||||
}
|
||||
|
||||
public function getMimeType(): ?string
|
||||
{
|
||||
return $this->mimeType;
|
||||
}
|
||||
|
||||
public function setMimeType(string $mimeType): static
|
||||
{
|
||||
$this->mimeType = $mimeType;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSize(): ?int
|
||||
{
|
||||
return $this->size;
|
||||
}
|
||||
|
||||
public function setSize(int $size): static
|
||||
{
|
||||
$this->size = $size;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCreatedAt(): ?DateTimeImmutable
|
||||
{
|
||||
return $this->createdAt;
|
||||
}
|
||||
|
||||
public function setCreatedAt(DateTimeImmutable $createdAt): static
|
||||
{
|
||||
$this->createdAt = $createdAt;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getUploadedBy(): ?UserInterface
|
||||
{
|
||||
return $this->uploadedBy;
|
||||
}
|
||||
|
||||
public function setUploadedBy(?UserInterface $uploadedBy): static
|
||||
{
|
||||
$this->uploadedBy = $uploadedBy;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use ApiPlatform\Metadata\ApiResource;
|
||||
use ApiPlatform\Metadata\Delete;
|
||||
use ApiPlatform\Metadata\Get;
|
||||
use ApiPlatform\Metadata\GetCollection;
|
||||
use ApiPlatform\Metadata\Patch;
|
||||
use ApiPlatform\Metadata\Post;
|
||||
use App\Repository\TaskEffortRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Serializer\Attribute\Groups;
|
||||
|
||||
#[ApiResource(
|
||||
operations: [
|
||||
new GetCollection(paginationEnabled: false, security: "is_granted('ROLE_USER')"),
|
||||
new Get(security: "is_granted('ROLE_USER')"),
|
||||
new Post(security: "is_granted('ROLE_ADMIN')"),
|
||||
new Patch(security: "is_granted('ROLE_ADMIN')"),
|
||||
new Delete(security: "is_granted('ROLE_ADMIN')"),
|
||||
],
|
||||
normalizationContext: ['groups' => ['task_effort:read']],
|
||||
denormalizationContext: ['groups' => ['task_effort:write']],
|
||||
order: ['label' => 'ASC'],
|
||||
)]
|
||||
#[ORM\Entity(repositoryClass: TaskEffortRepository::class)]
|
||||
class TaskEffort
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
#[Groups(['task_effort:read', 'task:read'])]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column(length: 50)]
|
||||
#[Groups(['task_effort:read', 'task_effort:write', 'task:read'])]
|
||||
private ?string $label = null;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getLabel(): ?string
|
||||
{
|
||||
return $this->label;
|
||||
}
|
||||
|
||||
public function setLabel(string $label): static
|
||||
{
|
||||
$this->label = $label;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use ApiPlatform\Doctrine\Orm\Filter\BooleanFilter;
|
||||
use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
|
||||
use ApiPlatform\Metadata\ApiFilter;
|
||||
use ApiPlatform\Metadata\ApiResource;
|
||||
use ApiPlatform\Metadata\Delete;
|
||||
use ApiPlatform\Metadata\Get;
|
||||
use ApiPlatform\Metadata\GetCollection;
|
||||
use ApiPlatform\Metadata\Patch;
|
||||
use ApiPlatform\Metadata\Post;
|
||||
use App\Repository\TaskGroupRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Serializer\Attribute\Groups;
|
||||
|
||||
#[ApiResource(
|
||||
operations: [
|
||||
new GetCollection(paginationEnabled: false, security: "is_granted('ROLE_USER')"),
|
||||
new Get(security: "is_granted('ROLE_USER')"),
|
||||
new Post(security: "is_granted('ROLE_ADMIN')"),
|
||||
new Patch(security: "is_granted('ROLE_ADMIN')"),
|
||||
new Delete(security: "is_granted('ROLE_ADMIN')"),
|
||||
],
|
||||
normalizationContext: ['groups' => ['task_group:read']],
|
||||
denormalizationContext: ['groups' => ['task_group:write']],
|
||||
order: ['title' => 'ASC'],
|
||||
)]
|
||||
#[ApiFilter(SearchFilter::class, properties: ['project' => 'exact'])]
|
||||
#[ApiFilter(BooleanFilter::class, properties: ['archived'])]
|
||||
#[ORM\Entity(repositoryClass: TaskGroupRepository::class)]
|
||||
class TaskGroup
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
#[Groups(['task_group:read', 'task:read'])]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
#[Groups(['task_group:read', 'task_group:write', 'task:read'])]
|
||||
private ?string $title = null;
|
||||
|
||||
#[ORM\Column(type: 'text', nullable: true)]
|
||||
#[Groups(['task_group:read', 'task_group:write'])]
|
||||
private ?string $description = null;
|
||||
|
||||
#[ORM\Column(length: 7)]
|
||||
#[Groups(['task_group:read', 'task_group:write', 'task:read'])]
|
||||
private ?string $color = '#222783';
|
||||
|
||||
#[ORM\ManyToOne(targetEntity: Project::class)]
|
||||
#[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')]
|
||||
#[Groups(['task_group:read', 'task_group:write'])]
|
||||
private ?Project $project = null;
|
||||
|
||||
#[ORM\Column(type: 'boolean')]
|
||||
#[Groups(['task_group:read', 'task_group:write', 'task:read'])]
|
||||
private bool $archived = false;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getTitle(): ?string
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function setTitle(string $title): static
|
||||
{
|
||||
$this->title = $title;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDescription(): ?string
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
public function setDescription(?string $description): static
|
||||
{
|
||||
$this->description = $description;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getColor(): ?string
|
||||
{
|
||||
return $this->color;
|
||||
}
|
||||
|
||||
public function setColor(string $color): static
|
||||
{
|
||||
$this->color = $color;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getProject(): ?Project
|
||||
{
|
||||
return $this->project;
|
||||
}
|
||||
|
||||
public function setProject(?Project $project): static
|
||||
{
|
||||
$this->project = $project;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isArchived(): bool
|
||||
{
|
||||
return $this->archived;
|
||||
}
|
||||
|
||||
public function setArchived(bool $archived): static
|
||||
{
|
||||
$this->archived = $archived;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Module\ProjectManagement\Domain\Entity\Task;
|
||||
use App\Repository\TaskMailLinkRepository;
|
||||
use App\Shared\Domain\Contract\UserInterface;
|
||||
use DateTimeImmutable;
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use ApiPlatform\Metadata\ApiResource;
|
||||
use ApiPlatform\Metadata\Delete;
|
||||
use ApiPlatform\Metadata\Get;
|
||||
use ApiPlatform\Metadata\GetCollection;
|
||||
use ApiPlatform\Metadata\Patch;
|
||||
use ApiPlatform\Metadata\Post;
|
||||
use App\Repository\TaskPriorityRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Serializer\Attribute\Groups;
|
||||
|
||||
#[ApiResource(
|
||||
operations: [
|
||||
new GetCollection(paginationEnabled: false, security: "is_granted('ROLE_USER')"),
|
||||
new Get(security: "is_granted('ROLE_USER')"),
|
||||
new Post(security: "is_granted('ROLE_ADMIN')"),
|
||||
new Patch(security: "is_granted('ROLE_ADMIN')"),
|
||||
new Delete(security: "is_granted('ROLE_ADMIN')"),
|
||||
],
|
||||
normalizationContext: ['groups' => ['task_priority:read']],
|
||||
denormalizationContext: ['groups' => ['task_priority:write']],
|
||||
order: ['label' => 'ASC'],
|
||||
)]
|
||||
#[ORM\Entity(repositoryClass: TaskPriorityRepository::class)]
|
||||
class TaskPriority
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
#[Groups(['task_priority:read', 'task:read'])]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
#[Groups(['task_priority:read', 'task_priority:write', 'task:read'])]
|
||||
private ?string $label = null;
|
||||
|
||||
#[ORM\Column(length: 7)]
|
||||
#[Groups(['task_priority:read', 'task_priority:write', 'task:read'])]
|
||||
private ?string $color = '#222783';
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getLabel(): ?string
|
||||
{
|
||||
return $this->label;
|
||||
}
|
||||
|
||||
public function setLabel(string $label): static
|
||||
{
|
||||
$this->label = $label;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getColor(): ?string
|
||||
{
|
||||
return $this->color;
|
||||
}
|
||||
|
||||
public function setColor(string $color): static
|
||||
{
|
||||
$this->color = $color;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -1,197 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use ApiPlatform\Metadata\ApiResource;
|
||||
use ApiPlatform\Metadata\Delete;
|
||||
use ApiPlatform\Metadata\Get;
|
||||
use ApiPlatform\Metadata\GetCollection;
|
||||
use ApiPlatform\Metadata\Patch;
|
||||
use ApiPlatform\Metadata\Post;
|
||||
use App\Enum\RecurrenceType;
|
||||
use App\Repository\TaskRecurrenceRepository;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Serializer\Attribute\Groups;
|
||||
|
||||
#[ApiResource(
|
||||
operations: [
|
||||
new GetCollection(paginationEnabled: false, security: "is_granted('ROLE_USER')"),
|
||||
new Get(security: "is_granted('ROLE_USER')"),
|
||||
new Post(security: "is_granted('ROLE_ADMIN')"),
|
||||
new Patch(security: "is_granted('ROLE_ADMIN')"),
|
||||
new Delete(security: "is_granted('ROLE_ADMIN')"),
|
||||
],
|
||||
normalizationContext: ['groups' => ['task_recurrence:read']],
|
||||
denormalizationContext: ['groups' => ['task_recurrence:write']],
|
||||
)]
|
||||
#[ORM\Entity(repositoryClass: TaskRecurrenceRepository::class)]
|
||||
class TaskRecurrence
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
#[Groups(['task_recurrence:read', 'task:read'])]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column(type: 'string', enumType: RecurrenceType::class)]
|
||||
#[Groups(['task_recurrence:read', 'task_recurrence:write', 'task:read'])]
|
||||
private ?RecurrenceType $type = null;
|
||||
|
||||
#[ORM\Column(type: 'integer')]
|
||||
#[Groups(['task_recurrence:read', 'task_recurrence:write', 'task:read'])]
|
||||
private int $interval = 1;
|
||||
|
||||
#[ORM\Column(type: 'json', nullable: true)]
|
||||
#[Groups(['task_recurrence:read', 'task_recurrence:write', 'task:read'])]
|
||||
private ?array $daysOfWeek = null;
|
||||
|
||||
#[ORM\Column(type: 'integer', nullable: true)]
|
||||
#[Groups(['task_recurrence:read', 'task_recurrence:write', 'task:read'])]
|
||||
private ?int $dayOfMonth = null;
|
||||
|
||||
#[ORM\Column(type: 'integer', nullable: true)]
|
||||
#[Groups(['task_recurrence:read', 'task_recurrence:write', 'task:read'])]
|
||||
private ?int $weekOfMonth = null;
|
||||
|
||||
#[ORM\Column(type: 'date_immutable', nullable: true)]
|
||||
#[Groups(['task_recurrence:read', 'task_recurrence:write', 'task:read'])]
|
||||
private ?DateTimeImmutable $endDate = null;
|
||||
|
||||
#[ORM\Column(type: 'integer', nullable: true)]
|
||||
#[Groups(['task_recurrence:read', 'task_recurrence:write', 'task:read'])]
|
||||
private ?int $maxOccurrences = null;
|
||||
|
||||
#[ORM\Column(type: 'integer')]
|
||||
#[Groups(['task_recurrence:read'])]
|
||||
private int $occurrenceCount = 0;
|
||||
|
||||
#[ORM\Version]
|
||||
#[ORM\Column(type: 'integer')]
|
||||
private int $version = 1;
|
||||
|
||||
/** @var Collection<int, Task> */
|
||||
#[ORM\OneToMany(targetEntity: Task::class, mappedBy: 'recurrence')]
|
||||
private Collection $tasks;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->tasks = new ArrayCollection();
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getType(): ?RecurrenceType
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
public function setType(RecurrenceType $type): static
|
||||
{
|
||||
$this->type = $type;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getInterval(): int
|
||||
{
|
||||
return $this->interval;
|
||||
}
|
||||
|
||||
public function setInterval(int $interval): static
|
||||
{
|
||||
$this->interval = $interval;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDaysOfWeek(): ?array
|
||||
{
|
||||
return $this->daysOfWeek;
|
||||
}
|
||||
|
||||
public function setDaysOfWeek(?array $daysOfWeek): static
|
||||
{
|
||||
$this->daysOfWeek = $daysOfWeek;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDayOfMonth(): ?int
|
||||
{
|
||||
return $this->dayOfMonth;
|
||||
}
|
||||
|
||||
public function setDayOfMonth(?int $dayOfMonth): static
|
||||
{
|
||||
$this->dayOfMonth = $dayOfMonth;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getWeekOfMonth(): ?int
|
||||
{
|
||||
return $this->weekOfMonth;
|
||||
}
|
||||
|
||||
public function setWeekOfMonth(?int $weekOfMonth): static
|
||||
{
|
||||
$this->weekOfMonth = $weekOfMonth;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getEndDate(): ?DateTimeImmutable
|
||||
{
|
||||
return $this->endDate;
|
||||
}
|
||||
|
||||
public function setEndDate(?DateTimeImmutable $endDate): static
|
||||
{
|
||||
$this->endDate = $endDate;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMaxOccurrences(): ?int
|
||||
{
|
||||
return $this->maxOccurrences;
|
||||
}
|
||||
|
||||
public function setMaxOccurrences(?int $maxOccurrences): static
|
||||
{
|
||||
$this->maxOccurrences = $maxOccurrences;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getOccurrenceCount(): int
|
||||
{
|
||||
return $this->occurrenceCount;
|
||||
}
|
||||
|
||||
public function getVersion(): int
|
||||
{
|
||||
return $this->version;
|
||||
}
|
||||
|
||||
/** @return Collection<int, Task> */
|
||||
public function getTasks(): Collection
|
||||
{
|
||||
return $this->tasks;
|
||||
}
|
||||
|
||||
public function incrementOccurrenceCount(): static
|
||||
{
|
||||
++$this->occurrenceCount;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use ApiPlatform\Metadata\ApiResource;
|
||||
use ApiPlatform\Metadata\Delete;
|
||||
use ApiPlatform\Metadata\Get;
|
||||
use ApiPlatform\Metadata\GetCollection;
|
||||
use ApiPlatform\Metadata\Patch;
|
||||
use ApiPlatform\Metadata\Post;
|
||||
use App\Enum\StatusCategory;
|
||||
use App\Repository\TaskStatusRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Serializer\Attribute\Groups;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
#[ApiResource(
|
||||
operations: [
|
||||
new GetCollection(paginationEnabled: false, security: "is_granted('ROLE_USER')"),
|
||||
new Get(security: "is_granted('ROLE_USER')"),
|
||||
new Post(security: "is_granted('ROLE_ADMIN')"),
|
||||
new Patch(security: "is_granted('ROLE_ADMIN')"),
|
||||
new Delete(security: "is_granted('ROLE_ADMIN')"),
|
||||
],
|
||||
normalizationContext: ['groups' => ['task_status:read']],
|
||||
denormalizationContext: ['groups' => ['task_status:write']],
|
||||
order: ['position' => 'ASC'],
|
||||
)]
|
||||
#[ORM\Entity(repositoryClass: TaskStatusRepository::class)]
|
||||
class TaskStatus
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
#[Groups(['task_status:read', 'task:read', 'workflow:read', 'project:read'])]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
#[Groups(['task_status:read', 'task_status:write', 'task:read', 'workflow:read', 'project:read'])]
|
||||
private ?string $label = null;
|
||||
|
||||
#[ORM\Column(length: 7)]
|
||||
#[Groups(['task_status:read', 'task_status:write', 'task:read', 'workflow:read', 'project:read'])]
|
||||
private ?string $color = '#222783';
|
||||
|
||||
#[ORM\Column]
|
||||
#[Groups(['task_status:read', 'task_status:write', 'task:read', 'workflow:read', 'project:read'])]
|
||||
private ?int $position = 0;
|
||||
|
||||
#[ORM\Column(type: 'boolean')]
|
||||
#[Groups(['task_status:read', 'task_status:write', 'task:read', 'workflow:read', 'project:read'])]
|
||||
private bool $isFinal = false;
|
||||
|
||||
#[ORM\ManyToOne(targetEntity: Workflow::class, inversedBy: 'statuses')]
|
||||
#[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')]
|
||||
#[Groups(['task_status:read', 'task_status:write', 'task:read'])]
|
||||
#[Assert\NotNull]
|
||||
private ?Workflow $workflow = null;
|
||||
|
||||
#[ORM\Column(type: 'string', length: 32, enumType: StatusCategory::class)]
|
||||
#[Groups(['task_status:read', 'task_status:write', 'task:read', 'workflow:read', 'project:read'])]
|
||||
#[Assert\NotNull]
|
||||
private ?StatusCategory $category = null;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getLabel(): ?string
|
||||
{
|
||||
return $this->label;
|
||||
}
|
||||
|
||||
public function setLabel(string $label): static
|
||||
{
|
||||
$this->label = $label;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getColor(): ?string
|
||||
{
|
||||
return $this->color;
|
||||
}
|
||||
|
||||
public function setColor(string $color): static
|
||||
{
|
||||
$this->color = $color;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPosition(): ?int
|
||||
{
|
||||
return $this->position;
|
||||
}
|
||||
|
||||
public function setPosition(int $position): static
|
||||
{
|
||||
$this->position = $position;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getIsFinal(): bool
|
||||
{
|
||||
return $this->isFinal;
|
||||
}
|
||||
|
||||
public function setIsFinal(bool $isFinal): static
|
||||
{
|
||||
$this->isFinal = $isFinal;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getWorkflow(): ?Workflow
|
||||
{
|
||||
return $this->workflow;
|
||||
}
|
||||
|
||||
public function setWorkflow(?Workflow $workflow): static
|
||||
{
|
||||
$this->workflow = $workflow;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCategory(): ?StatusCategory
|
||||
{
|
||||
return $this->category;
|
||||
}
|
||||
|
||||
public function setCategory(StatusCategory $category): static
|
||||
{
|
||||
$this->category = $category;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use ApiPlatform\Metadata\ApiResource;
|
||||
use ApiPlatform\Metadata\Delete;
|
||||
use ApiPlatform\Metadata\Get;
|
||||
use ApiPlatform\Metadata\GetCollection;
|
||||
use ApiPlatform\Metadata\Patch;
|
||||
use ApiPlatform\Metadata\Post;
|
||||
use App\Repository\TaskTagRepository;
|
||||
use App\Shared\Domain\Contract\TaskTagInterface;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Serializer\Attribute\Groups;
|
||||
|
||||
#[ApiResource(
|
||||
operations: [
|
||||
new GetCollection(paginationEnabled: false, security: "is_granted('ROLE_USER')"),
|
||||
new Get(security: "is_granted('ROLE_USER')"),
|
||||
new Post(security: "is_granted('ROLE_ADMIN')"),
|
||||
new Patch(security: "is_granted('ROLE_ADMIN')"),
|
||||
new Delete(security: "is_granted('ROLE_ADMIN')"),
|
||||
],
|
||||
normalizationContext: ['groups' => ['task_tag:read']],
|
||||
denormalizationContext: ['groups' => ['task_tag:write']],
|
||||
order: ['label' => 'ASC'],
|
||||
)]
|
||||
#[ORM\Entity(repositoryClass: TaskTagRepository::class)]
|
||||
#[ORM\Table(name: 'task_type')]
|
||||
class TaskTag implements TaskTagInterface
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
#[Groups(['task_tag:read', 'task:read', 'time_entry:read'])]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
#[Groups(['task_tag:read', 'task_tag:write', 'task:read', 'time_entry:read'])]
|
||||
private ?string $label = null;
|
||||
|
||||
#[ORM\Column(length: 7)]
|
||||
#[Groups(['task_tag:read', 'task_tag:write', 'task:read', 'time_entry:read'])]
|
||||
private ?string $color = '#222783';
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getLabel(): ?string
|
||||
{
|
||||
return $this->label;
|
||||
}
|
||||
|
||||
public function setLabel(string $label): static
|
||||
{
|
||||
$this->label = $label;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getColor(): ?string
|
||||
{
|
||||
return $this->color;
|
||||
}
|
||||
|
||||
public function setColor(string $color): static
|
||||
{
|
||||
$this->color = $color;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -1,131 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use ApiPlatform\Metadata\ApiResource;
|
||||
use ApiPlatform\Metadata\Delete;
|
||||
use ApiPlatform\Metadata\Get;
|
||||
use ApiPlatform\Metadata\GetCollection;
|
||||
use ApiPlatform\Metadata\Patch;
|
||||
use ApiPlatform\Metadata\Post;
|
||||
use App\Repository\WorkflowRepository;
|
||||
use App\State\WorkflowDeleteProcessor;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
|
||||
use Symfony\Component\Serializer\Attribute\Groups;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
#[ApiResource(
|
||||
operations: [
|
||||
new GetCollection(paginationEnabled: false, security: "is_granted('ROLE_USER')"),
|
||||
new Get(security: "is_granted('ROLE_USER')"),
|
||||
new Post(security: "is_granted('ROLE_ADMIN')"),
|
||||
new Patch(security: "is_granted('ROLE_ADMIN')"),
|
||||
new Delete(security: "is_granted('ROLE_ADMIN')", processor: WorkflowDeleteProcessor::class),
|
||||
],
|
||||
normalizationContext: ['groups' => ['workflow:read']],
|
||||
denormalizationContext: ['groups' => ['workflow:write']],
|
||||
order: ['position' => 'ASC'],
|
||||
)]
|
||||
#[ORM\Entity(repositoryClass: WorkflowRepository::class)]
|
||||
#[UniqueEntity(fields: ['name'], message: 'Ce nom de workflow est déjà utilisé.')]
|
||||
class Workflow
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
#[Groups(['workflow:read', 'project:read', 'task_status:read'])]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column(length: 255, unique: true)]
|
||||
#[Groups(['workflow:read', 'workflow:write', 'project:read'])]
|
||||
#[Assert\NotBlank]
|
||||
private ?string $name = null;
|
||||
|
||||
#[ORM\Column(type: 'boolean', options: ['default' => false])]
|
||||
#[Groups(['workflow:read', 'workflow:write'])]
|
||||
private bool $isDefault = false;
|
||||
|
||||
#[ORM\Column(type: 'integer', options: ['default' => 0])]
|
||||
#[Groups(['workflow:read', 'workflow:write'])]
|
||||
private int $position = 0;
|
||||
|
||||
/** @var Collection<int, TaskStatus> */
|
||||
#[ORM\OneToMany(targetEntity: TaskStatus::class, mappedBy: 'workflow', cascade: ['persist', 'remove'], orphanRemoval: true)]
|
||||
#[ORM\OrderBy(['position' => 'ASC'])]
|
||||
#[Groups(['workflow:read', 'project:read'])]
|
||||
private Collection $statuses;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->statuses = new ArrayCollection();
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getName(): ?string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function setName(string $name): static
|
||||
{
|
||||
$this->name = $name;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isDefault(): bool
|
||||
{
|
||||
return $this->isDefault;
|
||||
}
|
||||
|
||||
public function setIsDefault(bool $isDefault): static
|
||||
{
|
||||
$this->isDefault = $isDefault;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPosition(): int
|
||||
{
|
||||
return $this->position;
|
||||
}
|
||||
|
||||
public function setPosition(int $position): static
|
||||
{
|
||||
$this->position = $position;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** @return Collection<int, TaskStatus> */
|
||||
public function getStatuses(): Collection
|
||||
{
|
||||
return $this->statuses;
|
||||
}
|
||||
|
||||
public function addStatus(TaskStatus $status): static
|
||||
{
|
||||
if (!$this->statuses->contains($status)) {
|
||||
$this->statuses->add($status);
|
||||
$status->setWorkflow($this);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeStatus(TaskStatus $status): static
|
||||
{
|
||||
$this->statuses->removeElement($status);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user