<?php
namespace App\Entity;
use App\Repository\TicketRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: TicketRepository::class)]
#[ORM\Index(columns: ['statut'], name: 'idx_ticket_statut')]
#[ORM\Index(columns: ['priorite'], name: 'idx_ticket_priorite')]
#[ORM\Index(columns: ['date_creation'], name: 'idx_ticket_date')]
class Ticket
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 20, unique: true)]
private ?string $numeroTicket = null;
#[ORM\Column(length: 255)]
private ?string $titre = null;
#[ORM\Column(type: Types::TEXT)]
private ?string $description = null;
#[ORM\Column(length: 50)]
private ?string $type = 'support';
// support, bug, demande_fonctionnalite, demande_client, tache, incident, autre
#[ORM\Column(length: 50)]
private ?string $statut = 'ouvert';
// ouvert, en_cours, en_attente, resolu, ferme, annule
#[ORM\Column(length: 50)]
private ?string $priorite = 'moyenne';
// basse, moyenne, haute, urgente, critique
#[ORM\Column(length: 50, nullable: true)]
private ?string $categorie = null;
// technique, commercial, administratif, rh, finance, stock, autre
#[ORM\ManyToOne(targetEntity: User::class)]
#[ORM\JoinColumn(nullable: false)]
private ?User $createdBy = null;
#[ORM\ManyToOne(targetEntity: User::class)]
private ?User $assignedTo = null;
#[ORM\ManyToOne(targetEntity: Client::class)]
private ?Client $client = null;
#[ORM\ManyToOne(targetEntity: Boutique::class)]
private ?Boutique $boutique = null;
#[ORM\Column(type: Types::DATETIME_MUTABLE)]
private ?\DateTimeInterface $dateCreation = null;
#[ORM\Column(type: Types::DATETIME_MUTABLE, nullable: true)]
private ?\DateTimeInterface $dateEcheance = null;
#[ORM\Column(type: Types::DATETIME_MUTABLE, nullable: true)]
private ?\DateTimeInterface $dateResolution = null;
#[ORM\Column(type: Types::DATETIME_MUTABLE, nullable: true)]
private ?\DateTimeInterface $dateFermeture = null;
#[ORM\Column(nullable: true)]
private ?\DateTimeImmutable $updatedAt = null;
#[ORM\Column(type: Types::INTEGER, nullable: true)]
private ?int $tempsEstime = null; // en minutes
#[ORM\Column(type: Types::INTEGER, nullable: true)]
private ?int $tempsReel = null; // en minutes
#[ORM\OneToMany(mappedBy: 'ticket', targetEntity: CommentaireTicket::class, orphanRemoval: true)]
#[ORM\OrderBy(['createdAt' => 'DESC'])]
private Collection $commentaires;
#[ORM\OneToMany(mappedBy: 'ticket', targetEntity: PieceJointeTicket::class, orphanRemoval: true)]
private Collection $piecesJointes;
#[ORM\Column(type: Types::TEXT, nullable: true)]
private ?string $resolution = null;
#[ORM\ManyToOne(targetEntity: User::class)]
private ?User $resolvedBy = null;
#[ORM\ManyToMany(targetEntity: User::class)]
#[ORM\JoinTable(name: 'ticket_watchers')]
private Collection $watchers;
#[ORM\Column(type: Types::JSON, nullable: true)]
private ?array $tags = [];
public function __construct()
{
$this->commentaires = new ArrayCollection();
$this->piecesJointes = new ArrayCollection();
$this->watchers = new ArrayCollection();
$this->dateCreation = new \DateTime();
}
public function getId(): ?int
{
return $this->id;
}
public function getNumeroTicket(): ?string
{
return $this->numeroTicket;
}
public function setNumeroTicket(string $numeroTicket): static
{
$this->numeroTicket = $numeroTicket;
return $this;
}
public function getTitre(): ?string
{
return $this->titre;
}
public function setTitre(string $titre): static
{
$this->titre = $titre;
return $this;
}
public function getDescription(): ?string
{
return $this->description;
}
public function setDescription(string $description): static
{
$this->description = $description;
return $this;
}
public function getType(): ?string
{
return $this->type;
}
public function setType(string $type): static
{
$this->type = $type;
return $this;
}
public function getStatut(): ?string
{
return $this->statut;
}
public function setStatut(string $statut): static
{
$oldStatut = $this->statut;
$this->statut = $statut;
// Enregistrer les dates selon le statut
if ($statut === 'resolu' && $oldStatut !== 'resolu') {
$this->dateResolution = new \DateTime();
}
if ($statut === 'ferme' && $oldStatut !== 'ferme') {
$this->dateFermeture = new \DateTime();
}
return $this;
}
public function getPriorite(): ?string
{
return $this->priorite;
}
public function setPriorite(string $priorite): static
{
$this->priorite = $priorite;
return $this;
}
public function getCategorie(): ?string
{
return $this->categorie;
}
public function setCategorie(?string $categorie): static
{
$this->categorie = $categorie;
return $this;
}
public function getCreatedBy(): ?User
{
return $this->createdBy;
}
public function setCreatedBy(?User $createdBy): static
{
$this->createdBy = $createdBy;
return $this;
}
public function getAssignedTo(): ?User
{
return $this->assignedTo;
}
public function setAssignedTo(?User $assignedTo): static
{
$this->assignedTo = $assignedTo;
return $this;
}
public function getClient(): ?Client
{
return $this->client;
}
public function setClient(?Client $client): static
{
$this->client = $client;
return $this;
}
public function getBoutique(): ?Boutique
{
return $this->boutique;
}
public function setBoutique(?Boutique $boutique): static
{
$this->boutique = $boutique;
return $this;
}
public function getDateCreation(): ?\DateTimeInterface
{
return $this->dateCreation;
}
public function setDateCreation(\DateTimeInterface $dateCreation): static
{
$this->dateCreation = $dateCreation;
return $this;
}
public function getDateEcheance(): ?\DateTimeInterface
{
return $this->dateEcheance;
}
public function setDateEcheance(?\DateTimeInterface $dateEcheance): static
{
$this->dateEcheance = $dateEcheance;
return $this;
}
public function getDateResolution(): ?\DateTimeInterface
{
return $this->dateResolution;
}
public function setDateResolution(?\DateTimeInterface $dateResolution): static
{
$this->dateResolution = $dateResolution;
return $this;
}
public function getDateFermeture(): ?\DateTimeInterface
{
return $this->dateFermeture;
}
public function setDateFermeture(?\DateTimeInterface $dateFermeture): static
{
$this->dateFermeture = $dateFermeture;
return $this;
}
public function getUpdatedAt(): ?\DateTimeImmutable
{
return $this->updatedAt;
}
public function setUpdatedAt(?\DateTimeImmutable $updatedAt): static
{
$this->updatedAt = $updatedAt;
return $this;
}
public function getTempsEstime(): ?int
{
return $this->tempsEstime;
}
public function setTempsEstime(?int $tempsEstime): static
{
$this->tempsEstime = $tempsEstime;
return $this;
}
public function getTempsReel(): ?int
{
return $this->tempsReel;
}
public function setTempsReel(?int $tempsReel): static
{
$this->tempsReel = $tempsReel;
return $this;
}
/**
* @return Collection<int, CommentaireTicket>
*/
public function getCommentaires(): Collection
{
return $this->commentaires;
}
public function addCommentaire(CommentaireTicket $commentaire): static
{
if (!$this->commentaires->contains($commentaire)) {
$this->commentaires->add($commentaire);
$commentaire->setTicket($this);
}
return $this;
}
public function removeCommentaire(CommentaireTicket $commentaire): static
{
if ($this->commentaires->removeElement($commentaire)) {
if ($commentaire->getTicket() === $this) {
$commentaire->setTicket(null);
}
}
return $this;
}
/**
* @return Collection<int, PieceJointeTicket>
*/
public function getPiecesJointes(): Collection
{
return $this->piecesJointes;
}
public function addPiecesJointe(PieceJointeTicket $piecesJointe): static
{
if (!$this->piecesJointes->contains($piecesJointe)) {
$this->piecesJointes->add($piecesJointe);
$piecesJointe->setTicket($this);
}
return $this;
}
public function removePiecesJointe(PieceJointeTicket $piecesJointe): static
{
if ($this->piecesJointes->removeElement($piecesJointe)) {
if ($piecesJointe->getTicket() === $this) {
$piecesJointe->setTicket(null);
}
}
return $this;
}
public function getResolution(): ?string
{
return $this->resolution;
}
public function setResolution(?string $resolution): static
{
$this->resolution = $resolution;
return $this;
}
public function getResolvedBy(): ?User
{
return $this->resolvedBy;
}
public function setResolvedBy(?User $resolvedBy): static
{
$this->resolvedBy = $resolvedBy;
return $this;
}
/**
* @return Collection<int, User>
*/
public function getWatchers(): Collection
{
return $this->watchers;
}
public function addWatcher(User $watcher): static
{
if (!$this->watchers->contains($watcher)) {
$this->watchers->add($watcher);
}
return $this;
}
public function removeWatcher(User $watcher): static
{
$this->watchers->removeElement($watcher);
return $this;
}
public function getTags(): ?array
{
return $this->tags;
}
public function setTags(?array $tags): static
{
$this->tags = $tags;
return $this;
}
public function genererNumeroTicket(): string
{
$prefixes = [
'support' => 'SUP',
'bug' => 'BUG',
'demande_fonctionnalite' => 'FEAT',
'demande_client' => 'CLI',
'tache' => 'TSK',
'incident' => 'INC',
];
$prefix = $prefixes[$this->type] ?? 'TKT';
$date = $this->dateCreation ?? new \DateTime();
return $prefix . '-' . $date->format('Ymd') . '-' . str_pad($this->id ?? 0, 4, '0', STR_PAD_LEFT);
}
public function estEnRetard(): bool
{
if (!$this->dateEcheance || in_array($this->statut, ['resolu', 'ferme', 'annule'])) {
return false;
}
return new \DateTime() > $this->dateEcheance;
}
public function getDelai(): ?int
{
if (!$this->dateEcheance) {
return null;
}
$now = new \DateTime();
$diff = $now->diff($this->dateEcheance);
return $diff->invert ? -$diff->days : $diff->days;
}
public function getDureeTraitement(): ?int
{
if (!$this->dateResolution) {
return null;
}
$diff = $this->dateCreation->diff($this->dateResolution);
return $diff->days;
}
public function __toString(): string
{
return $this->numeroTicket ?? $this->titre ?? 'Nouveau ticket';
}
}