Migration modular monolith DDD (0.1 → 3.3) (#17)
Auto Tag Develop / tag (push) Successful in 9s
Auto Tag Develop / tag (push) Successful in 9s
## Migration modular monolith DDD — Lesstime (0.1 → 3.3) Cette MR regroupe l'intégralité de la refonte en monolithe modulaire (strangler progressif, additif). Elle remplace les MR stackées de Phase 1 (#12–#16), désormais incluses ici. **Ne pas merger avant validation fonctionnelle** : branche destinée à être testée telle quelle. ### Périmètre — 9 modules sous `src/Module/` | Phase | Module | Contenu | |------|--------|---------| | 0.1 | (socle) | infrastructure modulaire, `ModuleInterface`, mapping Doctrine par module | | 0.2 | (socle front) | auto-détection des layers Nuxt sous `frontend/modules/*` | | 1.1 | **Core** | Identité (User/Auth), Notifications, Notifier | | 1.2 | Core | RBAC fin (permissions `module.resource.action`, sidebar gated) | | 1.3 | Core | Audit log (`#[Auditable]`, listener, provider DBAL) | | 2.1 | **TimeTracking** | TimeEntry + MCP + export | | 2.2 | **ProjectManagement** | cœur métier Projets/Tâches + 38 MCP tools | | 2.3 | **Absence** | demandes, soldes, policies, justificatifs | | 2.4 | **Directory** | Clients (migrés) + **Prospects** (nouveau, conversion → Client) | | 2.5 | **Mail** | intégration IMAP OVH + liens tâches | | 2.6 | **Integration** | Gitea / BookStack / Zimbra / Share | | 3.1 | **Reporting** | rapports transverses (DBAL read-only, 0 import inter-module) | | 3.2 | **ClientPortal** | portail client (ROLE_CLIENT cloisonné, tickets, notifications) | | 3.3 | (finition) | nettoyage legacy — `src/Entity` vide, app 100% modulaire | ### Architecture - Découplage inter-modules par **contrats** (`UserInterface`, `ProjectInterface`, `TaskInterface`, `TaskTagInterface`, `ClientInterface`, `ClientTicketInterface`, `LeaveProfileInterface`) + `resolve_target_entities` 100% modulaire (aucune cible legacy). - Repositories : interface `Domain/Repository` + implémentation `Infrastructure/Doctrine`, bindées. - Reporting en DBAL read-only pur (aucun import d'entité d'un autre module). - Chaque migration de module : déplacement à comportement préservé (API publique et noms d'outils MCP inchangés), migrations **additives** uniquement (zéro destructif). ### Sécurité - ROLE_CLIENT cloisonné : un utilisateur client n'accède qu'à `/portal` et à ses propres tickets (filtrés par `allowedProjects`), interdit sur toute l'API interne. - Correctif : interdiction pour un client de créer un lien vers le partage SMB (upload uniquement). ### QA non-régression (branche reconstruite from scratch) - Migrations from scratch + fixtures : OK. - Compilation dev + prod : OK. - **180 tests PHPUnit verts**, php-cs-fixer clean, ~96 routes, **66 outils MCP** tous sous `App\Module\*`. - Smoke test runtime multi-rôles (admin / ROLE_USER / ROLE_CLIENT) : 44 vérifications HTTP, **0 écart**, cloisonnement client étanche. - Build Nuxt OK, 9 layers, 0 import legacy résiduel. ### Points à arbitrer (hors périmètre de cette migration) - Durcissement MCP/IDOR pré-existant (`userId` explicite sans scoping sur certains tools TimeTracking/Absence/TaskDocument) — ticket dédié recommandé. - Validation fonctionnelle de **Prospect** et **ClientPortal** (conçus depuis les specs disque). - **Harmonisation visuelle Malio finale** (3.3) — finition esthétique inter-modules laissée au PO. --- ## ⚠️ Déploiement / migration des données — à ne pas oublier ### 1. Resynchroniser les séquences PostgreSQL après tout import/restore de dump Si la prod (ou tout environnement) est **montée depuis un dump** (`pg_restore` / `COPY`), les lignes sont chargées avec leurs `id` explicites **sans avancer les séquences** → au premier `INSERT` : `duplicate key value violates unique constraint "..._pkey"` (constaté en local sur `notification`, `task`, `time_entry`…). À lancer **juste après chaque restore/import** : ```sql DO $$ DECLARE r RECORD; maxid BIGINT; seq TEXT; BEGIN FOR r IN SELECT table_name, column_name FROM information_schema.columns WHERE table_schema='public' LOOP seq := pg_get_serial_sequence(quote_ident(r.table_name), r.column_name); IF seq IS NOT NULL THEN EXECUTE format('SELECT COALESCE(MAX(%I),0) FROM %I', r.column_name, r.table_name) INTO maxid; PERFORM setval(seq, GREATEST(maxid,1), maxid > 0); END IF; END LOOP; END $$; ``` > Ne concerne **pas** une prod qui tourne déjà (séquences avancées organiquement) — uniquement le cas restore/import. Idempotent, sans risque. ### 2. Fix dénormalisation des collections typées-contrat (code, inclus dans la branche) Les relations **to-many** typées par une interface `Shared\Domain\Contract\*` (`TimeEntry::tags` → `TaskTagInterface`, `Task::collaborators` → `UserInterface`) étaient **indénormalisables par API Platform** (mono-valué OK via IRI, collection KO) → **tout POST/PATCH portant une telle collection renvoyait 400/500**. Corrigé par un dénormaliseur générique `ContractRelationDenormalizer` (réutilise `resolve_target_entities`, zéro couplage par-entité) + test fonctionnel de non-régression. --------- Co-authored-by: Matthieu <contact@malio.fr> Reviewed-on: #17
This commit was merged in pull request #17.
This commit is contained in:
@@ -1,208 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use ApiPlatform\Metadata\ApiResource;
|
||||
use ApiPlatform\Metadata\Get;
|
||||
use ApiPlatform\Metadata\GetCollection;
|
||||
use ApiPlatform\Metadata\Patch;
|
||||
use App\Enum\AbsenceType;
|
||||
use App\Repository\AbsenceBalanceRepository;
|
||||
use App\State\AbsenceBalanceProvider;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Serializer\Attribute\Groups;
|
||||
|
||||
/**
|
||||
* Per-employee, per-type leave balance for a given reference period.
|
||||
*/
|
||||
#[ApiResource(
|
||||
operations: [
|
||||
new GetCollection(
|
||||
paginationEnabled: false,
|
||||
security: "is_granted('ROLE_USER')",
|
||||
provider: AbsenceBalanceProvider::class,
|
||||
),
|
||||
new Get(
|
||||
security: "is_granted('ROLE_USER')",
|
||||
provider: AbsenceBalanceProvider::class,
|
||||
),
|
||||
new Patch(security: "is_granted('ROLE_ADMIN')"),
|
||||
],
|
||||
normalizationContext: ['groups' => ['absence_balance:read']],
|
||||
denormalizationContext: ['groups' => ['absence_balance:write']],
|
||||
)]
|
||||
#[ORM\Entity(repositoryClass: AbsenceBalanceRepository::class)]
|
||||
#[ORM\Table(name: 'absence_balance')]
|
||||
#[ORM\UniqueConstraint(name: 'uniq_absence_balance_user_type_period', columns: ['user_id', 'type', 'period'])]
|
||||
class AbsenceBalance
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
#[Groups(['absence_balance:read'])]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\ManyToOne(targetEntity: User::class)]
|
||||
#[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')]
|
||||
#[Groups(['absence_balance:read'])]
|
||||
private ?User $user = null;
|
||||
|
||||
#[ORM\Column(type: Types::STRING, length: 32, enumType: AbsenceType::class)]
|
||||
#[Groups(['absence_balance:read'])]
|
||||
private AbsenceType $type;
|
||||
|
||||
/** Reference period, e.g. "2025-2026" for paid leave or "2025" for yearly. */
|
||||
#[ORM\Column(length: 16)]
|
||||
#[Groups(['absence_balance:read'])]
|
||||
private ?string $period = null;
|
||||
|
||||
/** Days acquired during the *previous* reference period (Congés N-1): fully available to take. */
|
||||
#[ORM\Column(type: Types::FLOAT)]
|
||||
#[Groups(['absence_balance:read', 'absence_balance:write'])]
|
||||
private float $acquired = 0.0;
|
||||
|
||||
/** Days being accrued during the *current* reference period (Congés N): "en cours d'acquisition". */
|
||||
#[ORM\Column(type: Types::FLOAT)]
|
||||
#[Groups(['absence_balance:read', 'absence_balance:write'])]
|
||||
private float $acquiring = 0.0;
|
||||
|
||||
#[ORM\Column(type: Types::FLOAT)]
|
||||
#[Groups(['absence_balance:read', 'absence_balance:write'])]
|
||||
private float $taken = 0.0;
|
||||
|
||||
/** Sum of days in PENDING requests, for information. */
|
||||
#[ORM\Column(type: Types::FLOAT)]
|
||||
#[Groups(['absence_balance:read'])]
|
||||
private float $pending = 0.0;
|
||||
|
||||
/** Last month (format YYYY-MM) for which the monthly accrual was applied. */
|
||||
#[ORM\Column(length: 7, nullable: true)]
|
||||
private ?string $lastAccruedMonth = null;
|
||||
|
||||
/** Total entitlement for the period, both finalized (N-1) and in-progress (N). */
|
||||
#[Groups(['absence_balance:read'])]
|
||||
public function getAcquiredTotal(): float
|
||||
{
|
||||
return $this->acquired + $this->acquiring;
|
||||
}
|
||||
|
||||
/**
|
||||
* Days the employee can still take: in this organisation the days being
|
||||
* accrued (N) are posable too, so they count towards what is available.
|
||||
*/
|
||||
#[Groups(['absence_balance:read'])]
|
||||
public function getAvailable(): float
|
||||
{
|
||||
return $this->acquired + $this->acquiring - $this->taken;
|
||||
}
|
||||
|
||||
#[Groups(['absence_balance:read'])]
|
||||
public function getLabel(): string
|
||||
{
|
||||
return $this->type->label();
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getUser(): ?User
|
||||
{
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
public function setUser(?User $user): static
|
||||
{
|
||||
$this->user = $user;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getType(): AbsenceType
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
public function setType(AbsenceType $type): static
|
||||
{
|
||||
$this->type = $type;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPeriod(): ?string
|
||||
{
|
||||
return $this->period;
|
||||
}
|
||||
|
||||
public function setPeriod(string $period): static
|
||||
{
|
||||
$this->period = $period;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAcquired(): float
|
||||
{
|
||||
return $this->acquired;
|
||||
}
|
||||
|
||||
public function setAcquired(float $acquired): static
|
||||
{
|
||||
$this->acquired = $acquired;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAcquiring(): float
|
||||
{
|
||||
return $this->acquiring;
|
||||
}
|
||||
|
||||
public function setAcquiring(float $acquiring): static
|
||||
{
|
||||
$this->acquiring = $acquiring;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTaken(): float
|
||||
{
|
||||
return $this->taken;
|
||||
}
|
||||
|
||||
public function setTaken(float $taken): static
|
||||
{
|
||||
$this->taken = $taken;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPending(): float
|
||||
{
|
||||
return $this->pending;
|
||||
}
|
||||
|
||||
public function setPending(float $pending): static
|
||||
{
|
||||
$this->pending = $pending;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getLastAccruedMonth(): ?string
|
||||
{
|
||||
return $this->lastAccruedMonth;
|
||||
}
|
||||
|
||||
public function setLastAccruedMonth(?string $lastAccruedMonth): static
|
||||
{
|
||||
$this->lastAccruedMonth = $lastAccruedMonth;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -1,171 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use ApiPlatform\Metadata\ApiResource;
|
||||
use ApiPlatform\Metadata\Get;
|
||||
use ApiPlatform\Metadata\GetCollection;
|
||||
use ApiPlatform\Metadata\Patch;
|
||||
use App\Enum\AbsenceType;
|
||||
use App\Repository\AbsencePolicyRepository;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Serializer\Attribute\Groups;
|
||||
|
||||
/**
|
||||
* Per-type configuration of absence rules. Overrides the legal defaults and
|
||||
* lets an admin tune days/year, days/event, notice period, etc.
|
||||
*/
|
||||
#[ApiResource(
|
||||
operations: [
|
||||
new GetCollection(
|
||||
paginationEnabled: false,
|
||||
security: "is_granted('ROLE_USER')",
|
||||
),
|
||||
new Get(security: "is_granted('ROLE_USER')"),
|
||||
new Patch(security: "is_granted('ROLE_ADMIN')"),
|
||||
],
|
||||
normalizationContext: ['groups' => ['absence_policy:read']],
|
||||
denormalizationContext: ['groups' => ['absence_policy:write']],
|
||||
order: ['type' => 'ASC'],
|
||||
)]
|
||||
#[ORM\Entity(repositoryClass: AbsencePolicyRepository::class)]
|
||||
#[ORM\Table(name: 'absence_policy')]
|
||||
#[ORM\UniqueConstraint(name: 'uniq_absence_policy_type', columns: ['type'])]
|
||||
class AbsencePolicy
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
#[Groups(['absence_policy:read'])]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column(type: Types::STRING, length: 32, enumType: AbsenceType::class)]
|
||||
#[Groups(['absence_policy:read', 'absence_balance:read', 'absence_request:read'])]
|
||||
private AbsenceType $type;
|
||||
|
||||
/** Yearly entitlement (e.g. 25 for paid leave); null when not relevant. */
|
||||
#[ORM\Column(type: Types::FLOAT, nullable: true)]
|
||||
#[Groups(['absence_policy:read', 'absence_policy:write'])]
|
||||
private ?float $daysPerYear = null;
|
||||
|
||||
/** Days granted per event (e.g. 4 for marriage); null when not relevant. */
|
||||
#[ORM\Column(type: Types::FLOAT, nullable: true)]
|
||||
#[Groups(['absence_policy:read', 'absence_policy:write'])]
|
||||
private ?float $daysPerEvent = null;
|
||||
|
||||
#[ORM\Column]
|
||||
#[Groups(['absence_policy:read', 'absence_policy:write'])]
|
||||
private bool $justificationRequired = false;
|
||||
|
||||
/** Minimum notice period in days (e.g. 30 for paid leave, 0 for sick leave). */
|
||||
#[ORM\Column]
|
||||
#[Groups(['absence_policy:read', 'absence_policy:write'])]
|
||||
private int $noticeDays = 0;
|
||||
|
||||
/** true => "jours ouvrés" (Mon-Fri), false => "jours ouvrables" (Mon-Sat). */
|
||||
#[ORM\Column]
|
||||
#[Groups(['absence_policy:read', 'absence_policy:write'])]
|
||||
private bool $countWorkingDaysOnly = true;
|
||||
|
||||
#[ORM\Column]
|
||||
#[Groups(['absence_policy:read', 'absence_policy:write'])]
|
||||
private bool $active = true;
|
||||
|
||||
#[Groups(['absence_policy:read'])]
|
||||
public function getLabel(): string
|
||||
{
|
||||
return $this->type->label();
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getType(): AbsenceType
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
public function setType(AbsenceType $type): static
|
||||
{
|
||||
$this->type = $type;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDaysPerYear(): ?float
|
||||
{
|
||||
return $this->daysPerYear;
|
||||
}
|
||||
|
||||
public function setDaysPerYear(?float $daysPerYear): static
|
||||
{
|
||||
$this->daysPerYear = $daysPerYear;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDaysPerEvent(): ?float
|
||||
{
|
||||
return $this->daysPerEvent;
|
||||
}
|
||||
|
||||
public function setDaysPerEvent(?float $daysPerEvent): static
|
||||
{
|
||||
$this->daysPerEvent = $daysPerEvent;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isJustificationRequired(): bool
|
||||
{
|
||||
return $this->justificationRequired;
|
||||
}
|
||||
|
||||
public function setJustificationRequired(bool $justificationRequired): static
|
||||
{
|
||||
$this->justificationRequired = $justificationRequired;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getNoticeDays(): int
|
||||
{
|
||||
return $this->noticeDays;
|
||||
}
|
||||
|
||||
public function setNoticeDays(int $noticeDays): static
|
||||
{
|
||||
$this->noticeDays = $noticeDays;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isCountWorkingDaysOnly(): bool
|
||||
{
|
||||
return $this->countWorkingDaysOnly;
|
||||
}
|
||||
|
||||
public function setCountWorkingDaysOnly(bool $countWorkingDaysOnly): static
|
||||
{
|
||||
$this->countWorkingDaysOnly = $countWorkingDaysOnly;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isActive(): bool
|
||||
{
|
||||
return $this->active;
|
||||
}
|
||||
|
||||
public function setActive(bool $active): static
|
||||
{
|
||||
$this->active = $active;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -1,326 +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\AbsenceStatus;
|
||||
use App\Enum\AbsenceType;
|
||||
use App\Enum\HalfDay;
|
||||
use App\Repository\AbsenceRequestRepository;
|
||||
use App\State\AbsenceCancelProcessor;
|
||||
use App\State\AbsenceRequestProcessor;
|
||||
use App\State\AbsenceRequestProvider;
|
||||
use App\State\AbsenceReviewProcessor;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
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')",
|
||||
provider: AbsenceRequestProvider::class,
|
||||
),
|
||||
new Get(
|
||||
security: "is_granted('ROLE_USER')",
|
||||
provider: AbsenceRequestProvider::class,
|
||||
),
|
||||
new Post(
|
||||
security: "is_granted('ROLE_USER')",
|
||||
processor: AbsenceRequestProcessor::class,
|
||||
),
|
||||
new Patch(
|
||||
uriTemplate: '/absence_requests/{id}/approve',
|
||||
security: "is_granted('ROLE_ADMIN')",
|
||||
processor: AbsenceReviewProcessor::class,
|
||||
provider: AbsenceRequestProvider::class,
|
||||
),
|
||||
new Patch(
|
||||
uriTemplate: '/absence_requests/{id}/reject',
|
||||
security: "is_granted('ROLE_ADMIN')",
|
||||
processor: AbsenceReviewProcessor::class,
|
||||
provider: AbsenceRequestProvider::class,
|
||||
),
|
||||
new Patch(
|
||||
uriTemplate: '/absence_requests/{id}/cancel',
|
||||
security: "is_granted('ROLE_USER')",
|
||||
processor: AbsenceCancelProcessor::class,
|
||||
provider: AbsenceRequestProvider::class,
|
||||
),
|
||||
new Delete(security: "is_granted('ROLE_ADMIN')"),
|
||||
],
|
||||
normalizationContext: ['groups' => ['absence_request:read']],
|
||||
denormalizationContext: ['groups' => ['absence_request:write']],
|
||||
order: ['createdAt' => 'DESC'],
|
||||
)]
|
||||
#[ORM\Entity(repositoryClass: AbsenceRequestRepository::class)]
|
||||
#[ORM\Table(name: 'absence_request')]
|
||||
class AbsenceRequest
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
#[Groups(['absence_request:read'])]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\ManyToOne(targetEntity: User::class)]
|
||||
#[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')]
|
||||
#[Groups(['absence_request:read'])]
|
||||
private ?User $user = null;
|
||||
|
||||
#[ORM\Column(type: Types::STRING, length: 32, enumType: AbsenceType::class)]
|
||||
#[Groups(['absence_request:read', 'absence_request:write'])]
|
||||
#[Assert\NotNull]
|
||||
private ?AbsenceType $type = null;
|
||||
|
||||
#[ORM\Column(type: Types::DATE_IMMUTABLE)]
|
||||
#[Groups(['absence_request:read', 'absence_request:write'])]
|
||||
#[Assert\NotNull]
|
||||
private ?DateTimeImmutable $startDate = null;
|
||||
|
||||
#[ORM\Column(type: Types::DATE_IMMUTABLE)]
|
||||
#[Groups(['absence_request:read', 'absence_request:write'])]
|
||||
#[Assert\NotNull]
|
||||
private ?DateTimeImmutable $endDate = null;
|
||||
|
||||
#[ORM\Column(type: Types::STRING, length: 16, nullable: true, enumType: HalfDay::class)]
|
||||
#[Groups(['absence_request:read', 'absence_request:write'])]
|
||||
private ?HalfDay $startHalfDay = null;
|
||||
|
||||
#[ORM\Column(type: Types::STRING, length: 16, nullable: true, enumType: HalfDay::class)]
|
||||
#[Groups(['absence_request:read', 'absence_request:write'])]
|
||||
private ?HalfDay $endHalfDay = null;
|
||||
|
||||
/** Number of deducted days, computed server-side at creation. */
|
||||
#[ORM\Column(type: Types::FLOAT)]
|
||||
#[Groups(['absence_request:read'])]
|
||||
private float $countedDays = 0.0;
|
||||
|
||||
#[ORM\Column(type: Types::TEXT, nullable: true)]
|
||||
#[Groups(['absence_request:read', 'absence_request:write'])]
|
||||
private ?string $reason = null;
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
#[Groups(['absence_request:read'])]
|
||||
private ?string $justificationFileName = null;
|
||||
|
||||
#[ORM\Column(type: Types::STRING, length: 16, enumType: AbsenceStatus::class)]
|
||||
#[Groups(['absence_request:read'])]
|
||||
private AbsenceStatus $status = AbsenceStatus::Pending;
|
||||
|
||||
#[ORM\Column(type: Types::TEXT, nullable: true)]
|
||||
#[Groups(['absence_request:read', 'absence_request:write'])]
|
||||
private ?string $rejectionReason = null;
|
||||
|
||||
#[ORM\Column(type: Types::DATETIME_IMMUTABLE)]
|
||||
#[Groups(['absence_request:read'])]
|
||||
private ?DateTimeImmutable $createdAt = null;
|
||||
|
||||
#[ORM\Column(type: Types::DATETIME_IMMUTABLE, nullable: true)]
|
||||
#[Groups(['absence_request:read'])]
|
||||
private ?DateTimeImmutable $reviewedAt = null;
|
||||
|
||||
#[ORM\ManyToOne(targetEntity: User::class)]
|
||||
#[ORM\JoinColumn(nullable: true, onDelete: 'SET NULL')]
|
||||
#[Groups(['absence_request:read'])]
|
||||
private ?User $reviewedBy = null;
|
||||
|
||||
#[Groups(['absence_request:read'])]
|
||||
public function getLabel(): ?string
|
||||
{
|
||||
return $this->type?->label();
|
||||
}
|
||||
|
||||
#[Groups(['absence_request:read'])]
|
||||
public function getJustificationUrl(): ?string
|
||||
{
|
||||
if (null === $this->justificationFileName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return '/api/absence_requests/'.$this->id.'/justificatif';
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getUser(): ?User
|
||||
{
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
public function setUser(?User $user): static
|
||||
{
|
||||
$this->user = $user;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getType(): ?AbsenceType
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
public function setType(?AbsenceType $type): static
|
||||
{
|
||||
$this->type = $type;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getStartDate(): ?DateTimeImmutable
|
||||
{
|
||||
return $this->startDate;
|
||||
}
|
||||
|
||||
public function setStartDate(?DateTimeImmutable $startDate): static
|
||||
{
|
||||
$this->startDate = $startDate;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getEndDate(): ?DateTimeImmutable
|
||||
{
|
||||
return $this->endDate;
|
||||
}
|
||||
|
||||
public function setEndDate(?DateTimeImmutable $endDate): static
|
||||
{
|
||||
$this->endDate = $endDate;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getStartHalfDay(): ?HalfDay
|
||||
{
|
||||
return $this->startHalfDay;
|
||||
}
|
||||
|
||||
public function setStartHalfDay(?HalfDay $startHalfDay): static
|
||||
{
|
||||
$this->startHalfDay = $startHalfDay;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getEndHalfDay(): ?HalfDay
|
||||
{
|
||||
return $this->endHalfDay;
|
||||
}
|
||||
|
||||
public function setEndHalfDay(?HalfDay $endHalfDay): static
|
||||
{
|
||||
$this->endHalfDay = $endHalfDay;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCountedDays(): float
|
||||
{
|
||||
return $this->countedDays;
|
||||
}
|
||||
|
||||
public function setCountedDays(float $countedDays): static
|
||||
{
|
||||
$this->countedDays = $countedDays;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getReason(): ?string
|
||||
{
|
||||
return $this->reason;
|
||||
}
|
||||
|
||||
public function setReason(?string $reason): static
|
||||
{
|
||||
$this->reason = $reason;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getJustificationFileName(): ?string
|
||||
{
|
||||
return $this->justificationFileName;
|
||||
}
|
||||
|
||||
public function setJustificationFileName(?string $justificationFileName): static
|
||||
{
|
||||
$this->justificationFileName = $justificationFileName;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getStatus(): AbsenceStatus
|
||||
{
|
||||
return $this->status;
|
||||
}
|
||||
|
||||
public function setStatus(AbsenceStatus $status): static
|
||||
{
|
||||
$this->status = $status;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getRejectionReason(): ?string
|
||||
{
|
||||
return $this->rejectionReason;
|
||||
}
|
||||
|
||||
public function setRejectionReason(?string $rejectionReason): static
|
||||
{
|
||||
$this->rejectionReason = $rejectionReason;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCreatedAt(): ?DateTimeImmutable
|
||||
{
|
||||
return $this->createdAt;
|
||||
}
|
||||
|
||||
public function setCreatedAt(DateTimeImmutable $createdAt): static
|
||||
{
|
||||
$this->createdAt = $createdAt;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getReviewedAt(): ?DateTimeImmutable
|
||||
{
|
||||
return $this->reviewedAt;
|
||||
}
|
||||
|
||||
public function setReviewedAt(?DateTimeImmutable $reviewedAt): static
|
||||
{
|
||||
$this->reviewedAt = $reviewedAt;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getReviewedBy(): ?User
|
||||
{
|
||||
return $this->reviewedBy;
|
||||
}
|
||||
|
||||
public function setReviewedBy(?User $reviewedBy): static
|
||||
{
|
||||
$this->reviewedBy = $reviewedBy;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\BookStackConfigurationRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
#[ORM\Entity(repositoryClass: BookStackConfigurationRepository::class)]
|
||||
class BookStackConfiguration
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
#[Assert\Url]
|
||||
private ?string $url = null;
|
||||
|
||||
#[ORM\Column(type: 'text', nullable: true)]
|
||||
private ?string $encryptedTokenId = null;
|
||||
|
||||
#[ORM\Column(type: 'text', nullable: true)]
|
||||
private ?string $encryptedTokenSecret = null;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getUrl(): ?string
|
||||
{
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
public function setUrl(?string $url): static
|
||||
{
|
||||
$this->url = $url;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getEncryptedTokenId(): ?string
|
||||
{
|
||||
return $this->encryptedTokenId;
|
||||
}
|
||||
|
||||
public function setEncryptedTokenId(?string $encryptedTokenId): static
|
||||
{
|
||||
$this->encryptedTokenId = $encryptedTokenId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getEncryptedTokenSecret(): ?string
|
||||
{
|
||||
return $this->encryptedTokenSecret;
|
||||
}
|
||||
|
||||
public function setEncryptedTokenSecret(?string $encryptedTokenSecret): static
|
||||
{
|
||||
$this->encryptedTokenSecret = $encryptedTokenSecret;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function hasToken(): bool
|
||||
{
|
||||
return null !== $this->encryptedTokenId && null !== $this->encryptedTokenSecret;
|
||||
}
|
||||
}
|
||||
@@ -1,155 +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\ClientRepository;
|
||||
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' => ['client:read']],
|
||||
denormalizationContext: ['groups' => ['client:write']],
|
||||
order: ['name' => 'ASC'],
|
||||
)]
|
||||
#[ORM\Entity(repositoryClass: ClientRepository::class)]
|
||||
class Client
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
#[Groups(['client:read', 'project:read', 'user:list'])]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
#[Groups(['client:read', 'client:write', 'project:read', 'user:list'])]
|
||||
private ?string $name = null;
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
#[Groups(['client:read', 'client:write'])]
|
||||
private ?string $email = null;
|
||||
|
||||
#[ORM\Column(length: 50, nullable: true)]
|
||||
#[Groups(['client:read', 'client:write'])]
|
||||
private ?string $phone = null;
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
#[Groups(['client:read', 'client:write'])]
|
||||
private ?string $street = null;
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
#[Groups(['client:read', 'client:write'])]
|
||||
private ?string $city = null;
|
||||
|
||||
#[ORM\Column(length: 20, nullable: true)]
|
||||
#[Groups(['client:read', 'client:write'])]
|
||||
private ?string $postalCode = null;
|
||||
|
||||
/** @var Collection<int, Project> */
|
||||
#[ORM\OneToMany(targetEntity: Project::class, mappedBy: 'client')]
|
||||
private Collection $projects;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->projects = 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 getEmail(): ?string
|
||||
{
|
||||
return $this->email;
|
||||
}
|
||||
|
||||
public function setEmail(?string $email): static
|
||||
{
|
||||
$this->email = $email;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPhone(): ?string
|
||||
{
|
||||
return $this->phone;
|
||||
}
|
||||
|
||||
public function setPhone(?string $phone): static
|
||||
{
|
||||
$this->phone = $phone;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getStreet(): ?string
|
||||
{
|
||||
return $this->street;
|
||||
}
|
||||
|
||||
public function setStreet(?string $street): static
|
||||
{
|
||||
$this->street = $street;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCity(): ?string
|
||||
{
|
||||
return $this->city;
|
||||
}
|
||||
|
||||
public function setCity(?string $city): static
|
||||
{
|
||||
$this->city = $city;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPostalCode(): ?string
|
||||
{
|
||||
return $this->postalCode;
|
||||
}
|
||||
|
||||
public function setPostalCode(?string $postalCode): static
|
||||
{
|
||||
$this->postalCode = $postalCode;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** @return Collection<int, Project> */
|
||||
public function getProjects(): Collection
|
||||
{
|
||||
return $this->projects;
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\GiteaConfigurationRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
#[ORM\Entity(repositoryClass: GiteaConfigurationRepository::class)]
|
||||
class GiteaConfiguration
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
#[Assert\Url]
|
||||
private ?string $url = null;
|
||||
|
||||
#[ORM\Column(type: 'text', nullable: true)]
|
||||
private ?string $encryptedToken = null;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getUrl(): ?string
|
||||
{
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
public function setUrl(?string $url): static
|
||||
{
|
||||
$this->url = $url;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getEncryptedToken(): ?string
|
||||
{
|
||||
return $this->encryptedToken;
|
||||
}
|
||||
|
||||
public function setEncryptedToken(?string $encryptedToken): static
|
||||
{
|
||||
$this->encryptedToken = $encryptedToken;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function hasToken(): bool
|
||||
{
|
||||
return null !== $this->encryptedToken;
|
||||
}
|
||||
}
|
||||
@@ -1,193 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\MailConfigurationRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[ORM\Entity(repositoryClass: MailConfigurationRepository::class)]
|
||||
#[ORM\Table(name: 'mail_configuration')]
|
||||
class MailConfiguration
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column(length: 10)]
|
||||
private string $protocol = 'imap';
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
private ?string $imapHost = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private int $imapPort = 993;
|
||||
|
||||
#[ORM\Column(length: 10)]
|
||||
private string $imapEncryption = 'ssl';
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
private ?string $smtpHost = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private int $smtpPort = 465;
|
||||
|
||||
#[ORM\Column(length: 10)]
|
||||
private string $smtpEncryption = 'ssl';
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
private ?string $username = null;
|
||||
|
||||
#[ORM\Column(type: 'text', nullable: true)]
|
||||
private ?string $encryptedPassword = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
private string $sentFolderPath = 'Sent';
|
||||
|
||||
#[ORM\Column(type: 'boolean')]
|
||||
private bool $enabled = false;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getProtocol(): string
|
||||
{
|
||||
return $this->protocol;
|
||||
}
|
||||
|
||||
public function setProtocol(string $protocol): static
|
||||
{
|
||||
$this->protocol = $protocol;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getImapHost(): ?string
|
||||
{
|
||||
return $this->imapHost;
|
||||
}
|
||||
|
||||
public function setImapHost(?string $imapHost): static
|
||||
{
|
||||
$this->imapHost = $imapHost;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getImapPort(): int
|
||||
{
|
||||
return $this->imapPort;
|
||||
}
|
||||
|
||||
public function setImapPort(int $imapPort): static
|
||||
{
|
||||
$this->imapPort = $imapPort;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getImapEncryption(): string
|
||||
{
|
||||
return $this->imapEncryption;
|
||||
}
|
||||
|
||||
public function setImapEncryption(string $imapEncryption): static
|
||||
{
|
||||
$this->imapEncryption = $imapEncryption;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSmtpHost(): ?string
|
||||
{
|
||||
return $this->smtpHost;
|
||||
}
|
||||
|
||||
public function setSmtpHost(?string $smtpHost): static
|
||||
{
|
||||
$this->smtpHost = $smtpHost;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSmtpPort(): int
|
||||
{
|
||||
return $this->smtpPort;
|
||||
}
|
||||
|
||||
public function setSmtpPort(int $smtpPort): static
|
||||
{
|
||||
$this->smtpPort = $smtpPort;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSmtpEncryption(): string
|
||||
{
|
||||
return $this->smtpEncryption;
|
||||
}
|
||||
|
||||
public function setSmtpEncryption(string $smtpEncryption): static
|
||||
{
|
||||
$this->smtpEncryption = $smtpEncryption;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getUsername(): ?string
|
||||
{
|
||||
return $this->username;
|
||||
}
|
||||
|
||||
public function setUsername(?string $username): static
|
||||
{
|
||||
$this->username = $username;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getEncryptedPassword(): ?string
|
||||
{
|
||||
return $this->encryptedPassword;
|
||||
}
|
||||
|
||||
public function setEncryptedPassword(?string $encryptedPassword): static
|
||||
{
|
||||
$this->encryptedPassword = $encryptedPassword;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSentFolderPath(): string
|
||||
{
|
||||
return $this->sentFolderPath;
|
||||
}
|
||||
|
||||
public function setSentFolderPath(string $sentFolderPath): static
|
||||
{
|
||||
$this->sentFolderPath = $sentFolderPath;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isEnabled(): bool
|
||||
{
|
||||
return $this->enabled;
|
||||
}
|
||||
|
||||
public function setEnabled(bool $enabled): static
|
||||
{
|
||||
$this->enabled = $enabled;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function hasPassword(): bool
|
||||
{
|
||||
return null !== $this->encryptedPassword;
|
||||
}
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\MailFolderRepository;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[ORM\Entity(repositoryClass: MailFolderRepository::class)]
|
||||
#[ORM\Table(name: 'mail_folder')]
|
||||
#[ORM\Index(columns: ['parent_path'], name: 'idx_mail_folder_parent_path')]
|
||||
class MailFolder
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column(length: 500, unique: true)]
|
||||
private string $path;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
private string $displayName;
|
||||
|
||||
#[ORM\Column(length: 500, nullable: true)]
|
||||
private ?string $parentPath = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private int $unreadCount = 0;
|
||||
|
||||
#[ORM\Column]
|
||||
private int $totalCount = 0;
|
||||
|
||||
#[ORM\Column(type: 'datetimetz_immutable', nullable: true)]
|
||||
private ?DateTimeImmutable $lastSyncedAt = null;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getPath(): string
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
public function setPath(string $path): static
|
||||
{
|
||||
$this->path = $path;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDisplayName(): string
|
||||
{
|
||||
return $this->displayName;
|
||||
}
|
||||
|
||||
public function setDisplayName(string $displayName): static
|
||||
{
|
||||
$this->displayName = $displayName;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getParentPath(): ?string
|
||||
{
|
||||
return $this->parentPath;
|
||||
}
|
||||
|
||||
public function setParentPath(?string $parentPath): static
|
||||
{
|
||||
$this->parentPath = $parentPath;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getUnreadCount(): int
|
||||
{
|
||||
return $this->unreadCount;
|
||||
}
|
||||
|
||||
public function setUnreadCount(int $unreadCount): static
|
||||
{
|
||||
$this->unreadCount = $unreadCount;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTotalCount(): int
|
||||
{
|
||||
return $this->totalCount;
|
||||
}
|
||||
|
||||
public function setTotalCount(int $totalCount): static
|
||||
{
|
||||
$this->totalCount = $totalCount;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getLastSyncedAt(): ?DateTimeImmutable
|
||||
{
|
||||
return $this->lastSyncedAt;
|
||||
}
|
||||
|
||||
public function setLastSyncedAt(?DateTimeImmutable $lastSyncedAt): static
|
||||
{
|
||||
$this->lastSyncedAt = $lastSyncedAt;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -1,239 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\MailMessageRepository;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[ORM\Entity(repositoryClass: MailMessageRepository::class)]
|
||||
#[ORM\Table(name: 'mail_message')]
|
||||
#[ORM\UniqueConstraint(name: 'uq_mail_message_folder_uid', columns: ['folder_id', 'uid'])]
|
||||
#[ORM\Index(columns: ['sent_at'], name: 'idx_mail_message_sent_at')]
|
||||
#[ORM\Index(columns: ['is_read'], name: 'idx_mail_message_is_read')]
|
||||
#[ORM\Index(columns: ['message_id'], name: 'idx_mail_message_message_id')]
|
||||
class MailMessage
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column(length: 500)]
|
||||
private string $messageId;
|
||||
|
||||
#[ORM\ManyToOne(targetEntity: MailFolder::class)]
|
||||
#[ORM\JoinColumn(name: 'folder_id', referencedColumnName: 'id', nullable: false, onDelete: 'CASCADE')]
|
||||
private MailFolder $folder;
|
||||
|
||||
#[ORM\Column]
|
||||
private int $uid;
|
||||
|
||||
#[ORM\Column(length: 500, nullable: true)]
|
||||
private ?string $subject = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
private string $fromAddress;
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
private ?string $fromName = null;
|
||||
|
||||
#[ORM\Column(type: 'json')]
|
||||
private array $toAddresses = [];
|
||||
|
||||
#[ORM\Column(type: 'json', nullable: true)]
|
||||
private ?array $ccAddresses = null;
|
||||
|
||||
#[ORM\Column(type: 'datetimetz_immutable')]
|
||||
private DateTimeImmutable $sentAt;
|
||||
|
||||
#[ORM\Column(type: 'boolean')]
|
||||
private bool $isRead = false;
|
||||
|
||||
#[ORM\Column(type: 'boolean')]
|
||||
private bool $isFlagged = false;
|
||||
|
||||
#[ORM\Column(type: 'boolean')]
|
||||
private bool $hasAttachments = false;
|
||||
|
||||
#[ORM\Column(type: 'text', nullable: true)]
|
||||
private ?string $snippet = null;
|
||||
|
||||
#[ORM\Column(type: 'datetimetz_immutable')]
|
||||
private DateTimeImmutable $syncedAt;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getMessageId(): string
|
||||
{
|
||||
return $this->messageId;
|
||||
}
|
||||
|
||||
public function setMessageId(string $messageId): static
|
||||
{
|
||||
$this->messageId = $messageId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getFolder(): MailFolder
|
||||
{
|
||||
return $this->folder;
|
||||
}
|
||||
|
||||
public function setFolder(MailFolder $folder): static
|
||||
{
|
||||
$this->folder = $folder;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getUid(): int
|
||||
{
|
||||
return $this->uid;
|
||||
}
|
||||
|
||||
public function setUid(int $uid): static
|
||||
{
|
||||
$this->uid = $uid;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSubject(): ?string
|
||||
{
|
||||
return $this->subject;
|
||||
}
|
||||
|
||||
public function setSubject(?string $subject): static
|
||||
{
|
||||
$this->subject = $subject;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getFromAddress(): string
|
||||
{
|
||||
return $this->fromAddress;
|
||||
}
|
||||
|
||||
public function setFromAddress(string $fromAddress): static
|
||||
{
|
||||
$this->fromAddress = $fromAddress;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getFromName(): ?string
|
||||
{
|
||||
return $this->fromName;
|
||||
}
|
||||
|
||||
public function setFromName(?string $fromName): static
|
||||
{
|
||||
$this->fromName = $fromName;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getToAddresses(): array
|
||||
{
|
||||
return $this->toAddresses;
|
||||
}
|
||||
|
||||
public function setToAddresses(array $toAddresses): static
|
||||
{
|
||||
$this->toAddresses = $toAddresses;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCcAddresses(): ?array
|
||||
{
|
||||
return $this->ccAddresses;
|
||||
}
|
||||
|
||||
public function setCcAddresses(?array $ccAddresses): static
|
||||
{
|
||||
$this->ccAddresses = $ccAddresses;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSentAt(): DateTimeImmutable
|
||||
{
|
||||
return $this->sentAt;
|
||||
}
|
||||
|
||||
public function setSentAt(DateTimeImmutable $sentAt): static
|
||||
{
|
||||
$this->sentAt = $sentAt;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isRead(): bool
|
||||
{
|
||||
return $this->isRead;
|
||||
}
|
||||
|
||||
public function setIsRead(bool $isRead): static
|
||||
{
|
||||
$this->isRead = $isRead;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isFlagged(): bool
|
||||
{
|
||||
return $this->isFlagged;
|
||||
}
|
||||
|
||||
public function setIsFlagged(bool $isFlagged): static
|
||||
{
|
||||
$this->isFlagged = $isFlagged;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function hasAttachments(): bool
|
||||
{
|
||||
return $this->hasAttachments;
|
||||
}
|
||||
|
||||
public function setHasAttachments(bool $hasAttachments): static
|
||||
{
|
||||
$this->hasAttachments = $hasAttachments;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSnippet(): ?string
|
||||
{
|
||||
return $this->snippet;
|
||||
}
|
||||
|
||||
public function setSnippet(?string $snippet): static
|
||||
{
|
||||
$this->snippet = $snippet;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSyncedAt(): DateTimeImmutable
|
||||
{
|
||||
return $this->syncedAt;
|
||||
}
|
||||
|
||||
public function setSyncedAt(DateTimeImmutable $syncedAt): static
|
||||
{
|
||||
$this->syncedAt = $syncedAt;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use ApiPlatform\Metadata\ApiResource;
|
||||
use ApiPlatform\Metadata\GetCollection;
|
||||
use ApiPlatform\Metadata\Patch;
|
||||
use App\Repository\NotificationRepository;
|
||||
use App\State\NotificationProvider;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Serializer\Attribute\Groups;
|
||||
|
||||
#[ApiResource(
|
||||
operations: [
|
||||
new GetCollection(
|
||||
provider: NotificationProvider::class,
|
||||
security: "is_granted('IS_AUTHENTICATED_FULLY')",
|
||||
),
|
||||
new Patch(
|
||||
security: "is_granted('IS_AUTHENTICATED_FULLY') and object.getUser() == user",
|
||||
),
|
||||
],
|
||||
normalizationContext: ['groups' => ['notification:read']],
|
||||
denormalizationContext: ['groups' => ['notification:write']],
|
||||
order: ['createdAt' => 'DESC'],
|
||||
)]
|
||||
#[ORM\Entity(repositoryClass: NotificationRepository::class)]
|
||||
#[ORM\Index(columns: ['user_id'], name: 'idx_notification_user')]
|
||||
#[ORM\Index(columns: ['user_id', 'is_read'], name: 'idx_notification_user_read')]
|
||||
class Notification
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
#[Groups(['notification:read'])]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\ManyToOne(targetEntity: User::class)]
|
||||
#[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')]
|
||||
#[Groups(['notification:read'])]
|
||||
private ?User $user = null;
|
||||
|
||||
#[ORM\Column(length: 50)]
|
||||
#[Groups(['notification:read'])]
|
||||
private ?string $type = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
#[Groups(['notification:read'])]
|
||||
private ?string $title = null;
|
||||
|
||||
#[ORM\Column(type: Types::TEXT)]
|
||||
#[Groups(['notification:read'])]
|
||||
private ?string $message = null;
|
||||
|
||||
#[ORM\Column]
|
||||
#[Groups(['notification:read', 'notification:write'])]
|
||||
private bool $isRead = false;
|
||||
|
||||
#[ORM\Column]
|
||||
#[Groups(['notification:read'])]
|
||||
private ?DateTimeImmutable $createdAt = null;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getUser(): ?User
|
||||
{
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
public function setUser(?User $user): static
|
||||
{
|
||||
$this->user = $user;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getType(): ?string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
public function setType(string $type): static
|
||||
{
|
||||
$this->type = $type;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTitle(): ?string
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function setTitle(string $title): static
|
||||
{
|
||||
$this->title = $title;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMessage(): ?string
|
||||
{
|
||||
return $this->message;
|
||||
}
|
||||
|
||||
public function setMessage(string $message): static
|
||||
{
|
||||
$this->message = $message;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isRead(): bool
|
||||
{
|
||||
return $this->isRead;
|
||||
}
|
||||
|
||||
public function setIsRead(bool $isRead): static
|
||||
{
|
||||
$this->isRead = $isRead;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCreatedAt(): ?DateTimeImmutable
|
||||
{
|
||||
return $this->createdAt;
|
||||
}
|
||||
|
||||
public function setCreatedAt(DateTimeImmutable $createdAt): static
|
||||
{
|
||||
$this->createdAt = $createdAt;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -1,270 +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\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
|
||||
{
|
||||
#[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: Client::class, inversedBy: 'projects')]
|
||||
#[ORM\JoinColumn(nullable: true, onDelete: 'SET NULL')]
|
||||
#[Groups(['project:read', 'project:write'])]
|
||||
private ?Client $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(): ?Client
|
||||
{
|
||||
return $this->client;
|
||||
}
|
||||
|
||||
public function setClient(?Client $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,139 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\ShareConfigurationRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[ORM\Entity(repositoryClass: ShareConfigurationRepository::class)]
|
||||
class ShareConfiguration
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
private ?string $host = null;
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
private ?string $shareName = null;
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
private ?string $basePath = null;
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
private ?string $domain = null;
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
private ?string $username = null;
|
||||
|
||||
#[ORM\Column(type: 'text', nullable: true)]
|
||||
private ?string $encryptedPassword = null;
|
||||
|
||||
#[ORM\Column(type: 'boolean')]
|
||||
private bool $enabled = false;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getHost(): ?string
|
||||
{
|
||||
return $this->host;
|
||||
}
|
||||
|
||||
public function setHost(?string $host): static
|
||||
{
|
||||
$this->host = $host;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getShareName(): ?string
|
||||
{
|
||||
return $this->shareName;
|
||||
}
|
||||
|
||||
public function setShareName(?string $shareName): static
|
||||
{
|
||||
$this->shareName = $shareName;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getBasePath(): ?string
|
||||
{
|
||||
return $this->basePath;
|
||||
}
|
||||
|
||||
public function setBasePath(?string $basePath): static
|
||||
{
|
||||
$this->basePath = $basePath;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDomain(): ?string
|
||||
{
|
||||
return $this->domain;
|
||||
}
|
||||
|
||||
public function setDomain(?string $domain): static
|
||||
{
|
||||
$this->domain = $domain;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getUsername(): ?string
|
||||
{
|
||||
return $this->username;
|
||||
}
|
||||
|
||||
public function setUsername(?string $username): static
|
||||
{
|
||||
$this->username = $username;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getEncryptedPassword(): ?string
|
||||
{
|
||||
return $this->encryptedPassword;
|
||||
}
|
||||
|
||||
public function setEncryptedPassword(?string $encryptedPassword): static
|
||||
{
|
||||
$this->encryptedPassword = $encryptedPassword;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isEnabled(): bool
|
||||
{
|
||||
return $this->enabled;
|
||||
}
|
||||
|
||||
public function setEnabled(bool $enabled): static
|
||||
{
|
||||
$this->enabled = $enabled;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function hasPassword(): bool
|
||||
{
|
||||
return null !== $this->encryptedPassword;
|
||||
}
|
||||
|
||||
public function isUsable(): bool
|
||||
{
|
||||
return $this->enabled
|
||||
&& null !== $this->host && '' !== $this->host
|
||||
&& null !== $this->shareName && '' !== $this->shareName;
|
||||
}
|
||||
}
|
||||
@@ -1,486 +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\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
|
||||
{
|
||||
#[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: User::class)]
|
||||
#[ORM\JoinColumn(nullable: true, onDelete: 'SET NULL')]
|
||||
#[Groups(['task:read', 'task:write'])]
|
||||
private ?User $assignee = null;
|
||||
|
||||
/** @var Collection<int, User> */
|
||||
#[ORM\ManyToMany(targetEntity: User::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(): ?User
|
||||
{
|
||||
return $this->assignee;
|
||||
}
|
||||
|
||||
public function setAssignee(?User $assignee): static
|
||||
{
|
||||
$this->assignee = $assignee;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** @return Collection<int, User> */
|
||||
public function getCollaborators(): Collection
|
||||
{
|
||||
return $this->collaborators;
|
||||
}
|
||||
|
||||
public function addCollaborator(User $user): static
|
||||
{
|
||||
if (!$this->collaborators->contains($user)) {
|
||||
$this->collaborators->add($user);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function removeCollaborator(User $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()
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\TaskBookStackLinkRepository;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
#[ORM\Entity(repositoryClass: TaskBookStackLinkRepository::class)]
|
||||
#[ORM\UniqueConstraint(name: 'UNIQ_task_bookstack_link', columns: ['task_id', 'bookstack_id', 'bookstack_type'])]
|
||||
class TaskBookStackLink
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\ManyToOne(targetEntity: Task::class)]
|
||||
#[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')]
|
||||
private Task $task;
|
||||
|
||||
#[ORM\Column]
|
||||
private int $bookstackId;
|
||||
|
||||
#[ORM\Column(length: 10)]
|
||||
private string $bookstackType;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
private string $title;
|
||||
|
||||
#[ORM\Column(length: 500)]
|
||||
#[Assert\Url]
|
||||
private string $url;
|
||||
|
||||
#[ORM\Column]
|
||||
private DateTimeImmutable $createdAt;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->createdAt = new DateTimeImmutable();
|
||||
}
|
||||
|
||||
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 getBookstackId(): int
|
||||
{
|
||||
return $this->bookstackId;
|
||||
}
|
||||
|
||||
public function setBookstackId(int $bookstackId): static
|
||||
{
|
||||
$this->bookstackId = $bookstackId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getBookstackType(): string
|
||||
{
|
||||
return $this->bookstackType;
|
||||
}
|
||||
|
||||
public function setBookstackType(string $bookstackType): static
|
||||
{
|
||||
$this->bookstackType = $bookstackType;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function setTitle(string $title): static
|
||||
{
|
||||
$this->title = $title;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getUrl(): string
|
||||
{
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
public function setUrl(string $url): static
|
||||
{
|
||||
$this->url = $url;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCreatedAt(): DateTimeImmutable
|
||||
{
|
||||
return $this->createdAt;
|
||||
}
|
||||
}
|
||||
@@ -1,190 +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\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: User::class)]
|
||||
#[ORM\JoinColumn(nullable: true, onDelete: 'SET NULL')]
|
||||
#[Groups(['task_document:read', 'task:read'])]
|
||||
private ?User $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(): ?User
|
||||
{
|
||||
return $this->uploadedBy;
|
||||
}
|
||||
|
||||
public function setUploadedBy(?User $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;
|
||||
}
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\TaskMailLinkRepository;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[ORM\Entity(repositoryClass: TaskMailLinkRepository::class)]
|
||||
#[ORM\Table(name: 'task_mail_link')]
|
||||
#[ORM\UniqueConstraint(name: 'uq_task_mail_link', columns: ['task_id', 'mail_message_id'])]
|
||||
class TaskMailLink
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\ManyToOne(targetEntity: Task::class)]
|
||||
#[ORM\JoinColumn(name: 'task_id', referencedColumnName: 'id', nullable: false, onDelete: 'CASCADE')]
|
||||
private Task $task;
|
||||
|
||||
#[ORM\ManyToOne(targetEntity: MailMessage::class)]
|
||||
#[ORM\JoinColumn(name: 'mail_message_id', referencedColumnName: 'id', nullable: false, onDelete: 'CASCADE')]
|
||||
private MailMessage $mailMessage;
|
||||
|
||||
#[ORM\Column(type: 'datetimetz_immutable')]
|
||||
private DateTimeImmutable $linkedAt;
|
||||
|
||||
#[ORM\ManyToOne(targetEntity: User::class)]
|
||||
#[ORM\JoinColumn(name: 'linked_by_id', referencedColumnName: 'id', nullable: true, onDelete: 'SET NULL')]
|
||||
private ?User $linkedBy = 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 getMailMessage(): MailMessage
|
||||
{
|
||||
return $this->mailMessage;
|
||||
}
|
||||
|
||||
public function setMailMessage(MailMessage $mailMessage): static
|
||||
{
|
||||
$this->mailMessage = $mailMessage;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getLinkedAt(): DateTimeImmutable
|
||||
{
|
||||
return $this->linkedAt;
|
||||
}
|
||||
|
||||
public function setLinkedAt(DateTimeImmutable $linkedAt): static
|
||||
{
|
||||
$this->linkedAt = $linkedAt;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getLinkedBy(): ?User
|
||||
{
|
||||
return $this->linkedBy;
|
||||
}
|
||||
|
||||
public function setLinkedBy(?User $linkedBy): static
|
||||
{
|
||||
$this->linkedBy = $linkedBy;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -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,75 +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 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
|
||||
{
|
||||
#[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,220 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use ApiPlatform\Doctrine\Orm\Filter\DateFilter;
|
||||
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\TimeEntryRepository;
|
||||
use App\State\ActiveTimeEntryProvider;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Serializer\Attribute\Groups;
|
||||
|
||||
#[ApiResource(
|
||||
operations: [
|
||||
new GetCollection(security: "is_granted('ROLE_USER')"),
|
||||
new GetCollection(
|
||||
name: 'time_entries_range',
|
||||
uriTemplate: '/time_entries/range',
|
||||
description: 'List time entries for a bounded date range without pagination (used by the time-tracking calendar)',
|
||||
paginationEnabled: false,
|
||||
security: "is_granted('ROLE_USER')",
|
||||
),
|
||||
new GetCollection(
|
||||
name: 'active_time_entry',
|
||||
uriTemplate: '/time_entries/active',
|
||||
provider: ActiveTimeEntryProvider::class,
|
||||
description: 'Get the active timer for the current user',
|
||||
paginationEnabled: false,
|
||||
security: "is_granted('ROLE_USER')",
|
||||
),
|
||||
new Get(security: "is_granted('ROLE_USER')"),
|
||||
new Post(security: "is_granted('ROLE_USER')"),
|
||||
new Patch(security: "is_granted('ROLE_ADMIN') or object.getUser() == user"),
|
||||
new Delete(security: "is_granted('ROLE_ADMIN') or object.getUser() == user"),
|
||||
],
|
||||
normalizationContext: ['groups' => ['time_entry:read']],
|
||||
denormalizationContext: ['groups' => ['time_entry:write']],
|
||||
order: ['startedAt' => 'DESC'],
|
||||
)]
|
||||
#[ApiFilter(SearchFilter::class, properties: ['user' => 'exact', 'project' => 'exact', 'tags' => 'exact'])]
|
||||
#[ApiFilter(DateFilter::class, properties: ['startedAt'])]
|
||||
#[ORM\Entity(repositoryClass: TimeEntryRepository::class)]
|
||||
#[ORM\UniqueConstraint(name: 'uniq_active_timer', columns: ['user_id'], options: ['where' => '(stopped_at IS NULL)'])]
|
||||
class TimeEntry
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
#[Groups(['time_entry:read'])]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
#[Groups(['time_entry:read', 'time_entry:write'])]
|
||||
private ?string $title = null;
|
||||
|
||||
#[ORM\Column(type: Types::TEXT, nullable: true)]
|
||||
#[Groups(['time_entry:read', 'time_entry:write'])]
|
||||
private ?string $description = null;
|
||||
|
||||
#[ORM\Column(type: Types::DATETIMETZ_IMMUTABLE)]
|
||||
#[Groups(['time_entry:read', 'time_entry:write'])]
|
||||
private ?DateTimeImmutable $startedAt = null;
|
||||
|
||||
#[ORM\Column(type: Types::DATETIMETZ_IMMUTABLE, nullable: true)]
|
||||
#[Groups(['time_entry:read', 'time_entry:write'])]
|
||||
private ?DateTimeImmutable $stoppedAt = null;
|
||||
|
||||
#[ORM\ManyToOne(targetEntity: User::class)]
|
||||
#[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')]
|
||||
#[Groups(['time_entry:read', 'time_entry:write'])]
|
||||
private ?User $user = null;
|
||||
|
||||
#[ORM\ManyToOne(targetEntity: Project::class)]
|
||||
#[ORM\JoinColumn(nullable: true, onDelete: 'SET NULL')]
|
||||
#[Groups(['time_entry:read', 'time_entry:write'])]
|
||||
private ?Project $project = null;
|
||||
|
||||
#[ORM\ManyToOne(targetEntity: Task::class)]
|
||||
#[ORM\JoinColumn(nullable: true, onDelete: 'SET NULL')]
|
||||
#[Groups(['time_entry:read', 'time_entry:write'])]
|
||||
private ?Task $task = null;
|
||||
|
||||
/** @var Collection<int, TaskTag> */
|
||||
#[ORM\ManyToMany(targetEntity: TaskTag::class)]
|
||||
#[ORM\JoinTable(
|
||||
name: 'time_entry_task_type',
|
||||
joinColumns: [new ORM\JoinColumn(name: 'time_entry_id', referencedColumnName: 'id')],
|
||||
inverseJoinColumns: [new ORM\JoinColumn(name: 'task_type_id', referencedColumnName: 'id')],
|
||||
)]
|
||||
#[Groups(['time_entry:read', 'time_entry:write'])]
|
||||
private Collection $tags;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->tags = new ArrayCollection();
|
||||
}
|
||||
|
||||
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 getStartedAt(): ?DateTimeImmutable
|
||||
{
|
||||
return $this->startedAt;
|
||||
}
|
||||
|
||||
public function setStartedAt(DateTimeImmutable $startedAt): static
|
||||
{
|
||||
$this->startedAt = $startedAt;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getStoppedAt(): ?DateTimeImmutable
|
||||
{
|
||||
return $this->stoppedAt;
|
||||
}
|
||||
|
||||
public function setStoppedAt(?DateTimeImmutable $stoppedAt): static
|
||||
{
|
||||
$this->stoppedAt = $stoppedAt;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getUser(): ?User
|
||||
{
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
public function setUser(?User $user): static
|
||||
{
|
||||
$this->user = $user;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getProject(): ?Project
|
||||
{
|
||||
return $this->project;
|
||||
}
|
||||
|
||||
public function setProject(?Project $project): static
|
||||
{
|
||||
$this->project = $project;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTask(): ?Task
|
||||
{
|
||||
return $this->task;
|
||||
}
|
||||
|
||||
public function setTask(?Task $task): static
|
||||
{
|
||||
$this->task = $task;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,375 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use ApiPlatform\Metadata\ApiProperty;
|
||||
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\ContractType;
|
||||
use App\Repository\UserRepository;
|
||||
use App\State\MeProvider;
|
||||
use App\State\UserPasswordHasherProcessor;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
use Symfony\Component\Serializer\Attribute\Groups;
|
||||
|
||||
#[ApiResource(
|
||||
operations: [
|
||||
new Get(
|
||||
uriTemplate: '/me',
|
||||
provider: MeProvider::class,
|
||||
normalizationContext: ['groups' => ['me:read']],
|
||||
),
|
||||
new Get(
|
||||
normalizationContext: ['groups' => ['user:list']],
|
||||
),
|
||||
new GetCollection(
|
||||
paginationEnabled: false,
|
||||
normalizationContext: ['groups' => ['user:list']],
|
||||
),
|
||||
new Post(security: "is_granted('ROLE_ADMIN')", processor: UserPasswordHasherProcessor::class),
|
||||
new Patch(security: "is_granted('ROLE_ADMIN')", processor: UserPasswordHasherProcessor::class),
|
||||
new Delete(security: "is_granted('ROLE_ADMIN')"),
|
||||
],
|
||||
denormalizationContext: ['groups' => ['user:write']],
|
||||
)]
|
||||
#[ORM\Entity(repositoryClass: UserRepository::class)]
|
||||
#[ORM\Table(name: '`user`')]
|
||||
class User implements UserInterface, PasswordAuthenticatedUserInterface
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
#[Groups(['me:read', 'task:read', 'user:list', 'time_entry:read', 'absence_request:read', 'absence_balance:read'])]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column(length: 180, unique: true)]
|
||||
#[Groups(['me:read', 'task:read', 'user:list', 'user:write', 'time_entry:read', 'absence_request:read', 'absence_balance:read'])]
|
||||
private ?string $username = null;
|
||||
|
||||
#[ORM\Column(length: 100, nullable: true)]
|
||||
#[Groups(['me:read', 'user:list', 'user:write'])]
|
||||
private ?string $firstName = null;
|
||||
|
||||
#[ORM\Column(length: 100, nullable: true)]
|
||||
#[Groups(['me:read', 'user:list', 'user:write'])]
|
||||
private ?string $lastName = null;
|
||||
|
||||
/** @var list<string> */
|
||||
#[ORM\Column]
|
||||
#[ApiProperty(security: "is_granted('ROLE_ADMIN') or object == user")]
|
||||
#[Groups(['me:read', 'user:list', 'user:write'])]
|
||||
private array $roles = [];
|
||||
|
||||
#[ORM\Column]
|
||||
private ?string $password = null;
|
||||
|
||||
#[Groups(['user:write'])]
|
||||
private ?string $plainPassword = null;
|
||||
|
||||
#[ORM\Column(type: Types::DATETIME_IMMUTABLE)]
|
||||
private ?DateTimeImmutable $createdAt = null;
|
||||
|
||||
#[ORM\Column(length: 64, unique: true, nullable: true)]
|
||||
#[Groups(['me:read'])]
|
||||
private ?string $apiToken = null;
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
private ?string $avatarFileName = null;
|
||||
|
||||
// --- HR / absence management fields (readable only by an admin or the user themselves) ---
|
||||
|
||||
/** Whether this user is an employee subject to absence management. */
|
||||
#[ORM\Column]
|
||||
#[ApiProperty(security: "is_granted('ROLE_ADMIN') or object == user")]
|
||||
#[Groups(['me:read', 'user:list', 'user:write'])]
|
||||
private bool $isEmployee = false;
|
||||
|
||||
/** Hiring date — start of paid-leave acquisition. */
|
||||
#[ORM\Column(type: Types::DATE_IMMUTABLE, nullable: true)]
|
||||
#[ApiProperty(security: "is_granted('ROLE_ADMIN') or object == user")]
|
||||
#[Groups(['me:read', 'user:list', 'user:write'])]
|
||||
private ?DateTimeImmutable $hireDate = null;
|
||||
|
||||
#[ORM\Column(type: Types::DATE_IMMUTABLE, nullable: true)]
|
||||
#[ApiProperty(security: "is_granted('ROLE_ADMIN') or object == user")]
|
||||
#[Groups(['me:read', 'user:list', 'user:write'])]
|
||||
private ?DateTimeImmutable $endDate = null;
|
||||
|
||||
#[ORM\Column(type: Types::STRING, length: 16, nullable: true, enumType: ContractType::class)]
|
||||
#[ApiProperty(security: "is_granted('ROLE_ADMIN') or object == user")]
|
||||
#[Groups(['me:read', 'user:list', 'user:write'])]
|
||||
private ?ContractType $contractType = null;
|
||||
|
||||
/** Work-time ratio: 1.0 = full time, 0.8 = 4 days out of 5. */
|
||||
#[ORM\Column(type: Types::FLOAT)]
|
||||
#[ApiProperty(security: "is_granted('ROLE_ADMIN') or object == user")]
|
||||
#[Groups(['me:read', 'user:list', 'user:write'])]
|
||||
private float $workTimeRatio = 1.0;
|
||||
|
||||
/** Yearly paid-leave entitlement in worked days (default 25 = jours ouvrés). */
|
||||
#[ORM\Column(type: Types::FLOAT)]
|
||||
#[ApiProperty(security: "is_granted('ROLE_ADMIN') or object == user")]
|
||||
#[Groups(['me:read', 'user:list', 'user:write'])]
|
||||
private float $annualLeaveDays = 25.0;
|
||||
|
||||
/** Reference period start as MM-DD (default 06-01, 1st of June). */
|
||||
#[ORM\Column(length: 5)]
|
||||
#[ApiProperty(security: "is_granted('ROLE_ADMIN') or object == user")]
|
||||
#[Groups(['me:read', 'user:list', 'user:write'])]
|
||||
private string $referencePeriodStart = '06-01';
|
||||
|
||||
/** Paid-leave already acquired when the module is rolled out. */
|
||||
#[ORM\Column(type: Types::FLOAT)]
|
||||
#[ApiProperty(security: "is_granted('ROLE_ADMIN') or object == user")]
|
||||
#[Groups(['me:read', 'user:list', 'user:write'])]
|
||||
private float $initialLeaveBalance = 0.0;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->createdAt = new DateTimeImmutable();
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getUsername(): ?string
|
||||
{
|
||||
return $this->username;
|
||||
}
|
||||
|
||||
public function setUsername(string $username): static
|
||||
{
|
||||
$this->username = $username;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getFirstName(): ?string
|
||||
{
|
||||
return $this->firstName;
|
||||
}
|
||||
|
||||
public function setFirstName(?string $firstName): static
|
||||
{
|
||||
$this->firstName = $firstName;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getLastName(): ?string
|
||||
{
|
||||
return $this->lastName;
|
||||
}
|
||||
|
||||
public function setLastName(?string $lastName): static
|
||||
{
|
||||
$this->lastName = $lastName;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getUserIdentifier(): string
|
||||
{
|
||||
return (string) $this->username;
|
||||
}
|
||||
|
||||
/** @return list<string> */
|
||||
public function getRoles(): array
|
||||
{
|
||||
$roles = $this->roles;
|
||||
$roles[] = 'ROLE_USER';
|
||||
|
||||
return array_values(array_unique($roles));
|
||||
}
|
||||
|
||||
/** @param list<string> $roles */
|
||||
public function setRoles(array $roles): static
|
||||
{
|
||||
$this->roles = $roles;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPassword(): ?string
|
||||
{
|
||||
return $this->password;
|
||||
}
|
||||
|
||||
public function setPassword(string $password): static
|
||||
{
|
||||
$this->password = $password;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCreatedAt(): ?DateTimeImmutable
|
||||
{
|
||||
return $this->createdAt;
|
||||
}
|
||||
|
||||
public function setCreatedAt(DateTimeImmutable $createdAt): static
|
||||
{
|
||||
$this->createdAt = $createdAt;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getApiToken(): ?string
|
||||
{
|
||||
return $this->apiToken;
|
||||
}
|
||||
|
||||
public function setApiToken(?string $apiToken): static
|
||||
{
|
||||
$this->apiToken = $apiToken;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAvatarFileName(): ?string
|
||||
{
|
||||
return $this->avatarFileName;
|
||||
}
|
||||
|
||||
public function setAvatarFileName(?string $avatarFileName): static
|
||||
{
|
||||
$this->avatarFileName = $avatarFileName;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
#[Groups(['me:read', 'task:read', 'user:list', 'time_entry:read', 'absence_request:read', 'absence_balance:read'])]
|
||||
public function getAvatarUrl(): ?string
|
||||
{
|
||||
if (null === $this->avatarFileName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return '/api/users/'.$this->id.'/avatar';
|
||||
}
|
||||
|
||||
public function getPlainPassword(): ?string
|
||||
{
|
||||
return $this->plainPassword;
|
||||
}
|
||||
|
||||
public function setPlainPassword(?string $plainPassword): static
|
||||
{
|
||||
$this->plainPassword = $plainPassword;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function eraseCredentials(): void
|
||||
{
|
||||
$this->plainPassword = null;
|
||||
}
|
||||
|
||||
public function getIsEmployee(): bool
|
||||
{
|
||||
return $this->isEmployee;
|
||||
}
|
||||
|
||||
public function setIsEmployee(bool $isEmployee): static
|
||||
{
|
||||
$this->isEmployee = $isEmployee;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getHireDate(): ?DateTimeImmutable
|
||||
{
|
||||
return $this->hireDate;
|
||||
}
|
||||
|
||||
public function setHireDate(?DateTimeImmutable $hireDate): static
|
||||
{
|
||||
$this->hireDate = $hireDate;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getEndDate(): ?DateTimeImmutable
|
||||
{
|
||||
return $this->endDate;
|
||||
}
|
||||
|
||||
public function setEndDate(?DateTimeImmutable $endDate): static
|
||||
{
|
||||
$this->endDate = $endDate;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getContractType(): ?ContractType
|
||||
{
|
||||
return $this->contractType;
|
||||
}
|
||||
|
||||
public function setContractType(?ContractType $contractType): static
|
||||
{
|
||||
$this->contractType = $contractType;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getWorkTimeRatio(): float
|
||||
{
|
||||
return $this->workTimeRatio;
|
||||
}
|
||||
|
||||
public function setWorkTimeRatio(float $workTimeRatio): static
|
||||
{
|
||||
$this->workTimeRatio = $workTimeRatio;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAnnualLeaveDays(): float
|
||||
{
|
||||
return $this->annualLeaveDays;
|
||||
}
|
||||
|
||||
public function setAnnualLeaveDays(float $annualLeaveDays): static
|
||||
{
|
||||
$this->annualLeaveDays = $annualLeaveDays;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getReferencePeriodStart(): string
|
||||
{
|
||||
return $this->referencePeriodStart;
|
||||
}
|
||||
|
||||
public function setReferencePeriodStart(string $referencePeriodStart): static
|
||||
{
|
||||
$this->referencePeriodStart = $referencePeriodStart;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getInitialLeaveBalance(): float
|
||||
{
|
||||
return $this->initialLeaveBalance;
|
||||
}
|
||||
|
||||
public function setInitialLeaveBalance(float $initialLeaveBalance): static
|
||||
{
|
||||
$this->initialLeaveBalance = $initialLeaveBalance;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\ZimbraConfigurationRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
#[ORM\Entity(repositoryClass: ZimbraConfigurationRepository::class)]
|
||||
class ZimbraConfiguration
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
#[Assert\Url]
|
||||
private ?string $serverUrl = null;
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
private ?string $username = null;
|
||||
|
||||
#[ORM\Column(type: 'text', nullable: true)]
|
||||
private ?string $encryptedPassword = null;
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
private ?string $calendarPath = null;
|
||||
|
||||
#[ORM\Column(type: 'boolean')]
|
||||
private bool $enabled = false;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getServerUrl(): ?string
|
||||
{
|
||||
return $this->serverUrl;
|
||||
}
|
||||
|
||||
public function setServerUrl(?string $serverUrl): static
|
||||
{
|
||||
$this->serverUrl = $serverUrl;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getUsername(): ?string
|
||||
{
|
||||
return $this->username;
|
||||
}
|
||||
|
||||
public function setUsername(?string $username): static
|
||||
{
|
||||
$this->username = $username;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getEncryptedPassword(): ?string
|
||||
{
|
||||
return $this->encryptedPassword;
|
||||
}
|
||||
|
||||
public function setEncryptedPassword(?string $encryptedPassword): static
|
||||
{
|
||||
$this->encryptedPassword = $encryptedPassword;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCalendarPath(): ?string
|
||||
{
|
||||
return $this->calendarPath;
|
||||
}
|
||||
|
||||
public function setCalendarPath(?string $calendarPath): static
|
||||
{
|
||||
$this->calendarPath = $calendarPath;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isEnabled(): bool
|
||||
{
|
||||
return $this->enabled;
|
||||
}
|
||||
|
||||
public function setEnabled(bool $enabled): static
|
||||
{
|
||||
$this->enabled = $enabled;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function hasPassword(): bool
|
||||
{
|
||||
return null !== $this->encryptedPassword;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user