<?php
namespace App\Entity;
use App\Repository\ProjectOrderTaskJobsRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups;
/**
* @ORM\Entity(repositoryClass=ProjectOrderTaskJobsRepository::class)
*/
class ProjectOrderTaskJobs
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
* @Groups({
* "project_order_task_job@base",
* "project_order_task_job@core"
* })
*/
private $id;
/**
* @ORM\ManyToOne(targetEntity=ProjectOrderTasks::class, inversedBy="project_order_task_jobs")
* @ORM\JoinColumn(onDelete="CASCADE")
*/
// TODO Replace with task $order_task > $task !!!
private $order_task;
/**
* @ORM\Column(type="float", nullable=true)
* @Groups({
* "project_order_task_job@core"
* })
*/
private $job_weight;
/**
* @ORM\Column(type="integer")
* @Groups({
* "project_order_task_job@core"
* })
*/
private $job_index;
/**
* @ORM\Column(type="date_immutable", nullable=true)
* @Groups({
* "project_order_task_job@core"
* })
*/
private $start_at;
/**
* @ORM\Column(type="date_immutable", nullable=true)
* @Groups({
* "project_order_task_job@core"
* })
*/
private $end_at;
/**
* @ORM\Column(type="float", nullable=true)
* @Groups({
* "project_order_task_job@core"
* })
*/
private $rating;
/**
* @ORM\Column(type="integer", nullable=true)
* @Groups({
* "project_order_task_job@core"
* })
*/
private $real_days;
/**
* @ORM\Column(type="text", nullable=true)
* @Groups({
* "project_order_task_job@core"
* })
*/
private $job_description;
/**
* @ORM\Column(type="float", nullable=true)
* @Groups({
* "project_order_task_job@core"
* })
*/
private $duration_days;
public function getId(): ?int
{
return $this->id;
}
public function getOrderTask(): ?ProjectOrderTasks
{
return $this->order_task;
}
public function setOrderTask(?ProjectOrderTasks $order_task): self
{
$this->order_task = $order_task;
return $this;
}
public function getJobWeight(): ?float
{
return $this->job_weight;
}
public function setJobWeight(?float $job_weight): self
{
$this->job_weight = $job_weight;
return $this;
}
public function getJobIndex(): ?int
{
return $this->job_index;
}
public function setJobIndex(int $job_index): self
{
$this->job_index = $job_index;
return $this;
}
public function getStartAt(): ?\DateTimeImmutable
{
return $this->start_at;
}
public function setStartAt(?\DateTimeImmutable $start_at): self
{
$this->start_at = $start_at;
return $this;
}
public function getEndAt(): ?\DateTimeImmutable
{
return $this->end_at;
}
public function setEndAt(?\DateTimeImmutable $end_at): self
{
$this->end_at = $end_at;
return $this;
}
/**
* Tamamlama kilidi — PLAN job'undan OKUNUR (read-through; order kendi tutmaz).
* Plan = lock otoritesi. Order bu değere göre editlenemez; unlock yetkisi plandadır.
* Plana bağlı olmayan order job'unda (source_plan_job null) her zaman false.
*
* @Groups({
* "project_order_task_job@core"
* })
*/
public function getLocked(): bool
{
return $this->source_plan_job !== null && $this->source_plan_job->getLocked();
}
/**
* Tamamlama kilidi — SERVIS HELPER'ı (serialize EDİLMEZ). Job tamamlanmamış (end_at null)
* VE başlangıç günü gelmemişse (today < start_at) doldurulur; aksi halde null.
* Job tamamlama "live request" (job-completion-check endpoint → checkJobCompletion) bunu
* çağırır; eski serialize-edilen `completion_block` alanı kaldırıldı (FE artık endpoint kullanıyor).
*
* @return array|null
*/
public function getCompletionBlock(): ?array
{
if ($this->end_at !== null) {
return null;
}
// Plan-bağlı job: start OTORİTESİ plan (order kopyası stale olabilir — plan ayrı yerde
// değişip order reload edilmemişse). Canlı check stale start yüzünden haksız blok vermesin.
$start = $this->source_plan_job !== null ? $this->source_plan_job->getStartAt() : $this->start_at;
if ($start === null) {
return null;
}
// Tarih-string: bugün == başlangıç günü ise tamamlanabilir (saat bileşeni şişirmesin).
$todayStr = (new \DateTimeImmutable('today'))->format('Y-m-d');
if ($start->format('Y-m-d') > $todayStr) {
return ['reason' => 'not_started', 'start_at' => $start->format('Y-m-d')];
}
return null;
}
public function getRating(): ?float
{
return $this->rating;
}
public function setRating(?float $rating): self
{
$this->rating = $rating;
return $this;
}
public function getRealDays(): ?int
{
return $this->real_days;
}
public function setRealDays(?int $real_days): self
{
$this->real_days = $real_days;
return $this;
}
public function getJobDescription(): ?string
{
return $this->job_description;
}
public function setJobDescription(?string $job_description): self
{
$this->job_description = $job_description;
return $this;
}
public function getDurationDays(): ?float
{
return $this->duration_days;
}
public function setDurationDays(?float $duration_days): self
{
$this->duration_days = $duration_days;
return $this;
}
/**
* Materialization mapping — bu Order TaskJob hangi WorksPlanJob'dan üretildi.
* end_at değiştiğinde OrderEndAtBackSyncListener bu kolondan Plan job'una ulaşıp
* is_end_at'i set eder. Plan job silinirse NULL'a düşer (Order satırı kalır).
*
* @ORM\ManyToOne(targetEntity=WorksPlanJob::class)
* @ORM\JoinColumn(name="source_plan_job_id", referencedColumnName="id", nullable=true, onDelete="SET NULL")
*
* NOT: Serialize EDİLMEZ (group YOK) — order FE'sine sızarsa save'de generic
* hydrator setSourcePlanJob(array) ile patlar. Yalnızca BE Materializer/back-sync kullanır.
*/
private $source_plan_job;
public function getSourcePlanJob(): ?WorksPlanJob
{
return $this->source_plan_job;
}
public function setSourcePlanJob(?WorksPlanJob $source_plan_job): self
{
$this->source_plan_job = $source_plan_job;
return $this;
}
}