<?php
namespace MedBrief\MSR\Entity;
use DateTime;
use DH\Auditor\Provider\Doctrine\Auditing\Annotation as Audit;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use MedBrief\MSR\Entity\Serviceable\ServiceableInterface;
use MedBrief\MSR\Entity\Serviceable\ServiceableTrait;
use MedBrief\MSR\Traits\FilterableClassConstantsTrait;
/**
* @ORM\Table(name="ExpertReportRequest")
*
* @ORM\Entity
*
* @ORM\HasLifecycleCallbacks
*
* @Gedmo\SoftDeleteable(fieldName="deletedAt", timeAware=false)
*
* @Audit\Auditable
*
* @Audit\Security(view={"ROLE_ALLOWED_TO_AUDIT"})
*/
class ExpertReportRequest implements ServiceableInterface
{
use ServiceableTrait;
use FilterableClassConstantsTrait;
// Turn-around time constants
public const TURN_AROUND_TIME_1_2_WEEKS = '1-2-weeks';
public const TURN_AROUND_TIME_2_4_WEEKS = '2-4-weeks';
public const TURN_AROUND_TIME_4_6_WEEKS = '4-6-weeks';
public const TURN_AROUND_TIME_6_8_WEEKS = '6-8-weeks';
public const TURN_AROUND_TIME_8_12_WEEKS = '8-12-weeks';
public const TURN_AROUND_TIME_12_WEEKS = '12+-weeks';
/**
* @var int
*
* @ORM\Column(name="id", type="integer")
*
* @ORM\Id
*
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
protected int $id;
/**
* @var bool|null
*
* @ORM\Column(name="isUpdate", type="boolean", nullable=true)
*/
protected ?bool $isUpdate = null;
/**
* @var string|null
*
* @ORM\Column(name="hourlyRate", type="decimal", precision=10, scale=2, nullable=true)
*/
protected ?string $hourlyRate = null;
/**
* @var string|null
*
* @ORM\Column(name="hoursEstimated", type="decimal", precision=7, scale=2, nullable=true)
*/
protected ?string $hoursEstimated = null;
/**
* @var string|null
*
* @ORM\Column(name="turnAroundTime", type="string", length=24, nullable=true)
*/
protected ?string $turnAroundTime = null;
/**
* @var DateTime|null
*
* @ORM\Column(name="dateInstructed", type="date", nullable=true)
*/
protected ?DateTime $dateInstructed = null;
/**
* @var DateTime|null
*
* @ORM\Column(name="dateDue", type="date", nullable=true)
*/
protected ?DateTime $dateDue = null;
/**
* @var DateTime|null
*
* @ORM\Column(name="dateDraftReportReceived", type="date", nullable=true)
*/
protected ?DateTime $dateDraftReportReceived = null;
/**
* @var DateTime|null
*
* @ORM\Column(name="dateFinalReportReceived", type="date", nullable=true)
*/
protected ?DateTime $dateFinalReportReceived = null;
/**
* @var DateTime|null
*
* @ORM\Column(name="dateInvoiceReceived", type="date", nullable=true)
*/
protected ?DateTime $dateInvoiceReceived = null;
/**
* @var string|null
*
* @ORM\Column(name="invoiceReference", type="string", length=255, nullable=true)
*/
protected ?string $invoiceReference = null;
/**
* @var string|null
*
* @ORM\Column(name="hoursInvoiced", type="decimal", precision=7, scale=2, nullable=true)
*/
protected ?string $hoursInvoiced = null;
/**
* @var string|null
*
* @ORM\Column(name="nettAmount", type="decimal", precision=10, scale=2, nullable=true)
*/
protected ?string $nettAmount = null;
/**
* @var string|null
*
* @ORM\Column(name="vatPercentage", type="decimal", precision=5, scale=2, nullable=true)
*/
protected ?string $vatPercentage = null;
/**
* @var string|null
*
* @ORM\Column(name="totalAmount", type="decimal", precision=10, scale=2, nullable=true)
*/
protected ?string $totalAmount = null;
/**
* @var DateTime
*
* @ORM\Column(name="created", type="datetime")
*
* @Gedmo\Timestampable(on="create")
*/
protected DateTime $created;
/**
* @var DateTime
*
* @ORM\Column(name="updated", type="datetime")
*
* @Gedmo\Timestampable(on="update")
*/
protected DateTime $updated;
/**
* @var DateTime|null
*
* @ORM\Column(name="deletedAt", type="datetime", nullable=true)
*/
protected ?DateTime $deletedAt = null;
/**
* @var Project
*
* @ORM\ManyToOne(targetEntity="MedBrief\MSR\Entity\Project", inversedBy="expertReportRequests")
*
* @ORM\JoinColumns({
*
* @ORM\JoinColumn(name="project_id", referencedColumnName="id")
* })
*/
protected Project $project;
/**
* @var Collection|ExpertReportRequestDetail[]
*
* @ORM\OneToMany(targetEntity="MedBrief\MSR\Entity\ExpertReportRequestDetail", mappedBy="expertReportRequest", cascade={"persist", "remove"})
*
* @ORM\OrderBy({"displayDate" = "DESC"})
*/
protected Collection $details;
/**
* @var ServiceRequest|null
*
* @ORM\OneToOne(targetEntity="MedBrief\MSR\Entity\ServiceRequest", mappedBy="expertReportRequest", cascade={"persist"})
*/
protected ?ServiceRequest $serviceRequest = null;
/**
* @var Expert|null
*
* @ORM\ManyToOne(targetEntity="MedBrief\MSR\Entity\Expert")
*
* @ORM\JoinColumn(name="expert_id", referencedColumnName="id", nullable=true, onDelete="SET NULL")
*/
protected ?Expert $expert = null;
/**
* Denormalised expert name, stored for display if the Expert record is removed.
*
* @var string|null
*
* @ORM\Column(name="expertName", type="string", length=255, nullable=true)
*/
private ?string $expertName = null;
/**
* Constructor
*/
public function __construct()
{
$this->details = new ArrayCollection();
}
/**
* __toString
*
* @return string
*/
public function __toString()
{
return (string) $this->getId();
}
/**
* Get id
*
* @return int|null
*/
public function getId(): ?int
{
return $this->id;
}
/**
* Set isUpdate
*
* @param bool|null $isUpdate
*
* @return self
*/
public function setIsUpdate(?bool $isUpdate): self
{
$this->isUpdate = $isUpdate;
return $this;
}
/**
* Get isUpdate
*
* @return bool|null
*/
public function getIsUpdate(): ?bool
{
return $this->isUpdate;
}
/**
* Get isUpdateLabel
*
* @return string
*/
public function getIsUpdateLabel(): string
{
return $this->isUpdate ? 'Update' : 'New';
}
/**
* Get isUpdateOptions
*
* @return array
*/
public static function getIsUpdateOptions(): array
{
return [
'New' => false,
'Update' => true,
];
}
/**
* Get expertName
*
* @return string|null
*/
public function getExpertName(): ?string
{
return $this->expertName;
}
/**
* Set expertName
*
* @param string|null $expertName
*
* @return self
*/
public function setExpertName(?string $expertName): self
{
$this->expertName = $expertName;
return $this;
}
/**
* Get expert
*
* @return Expert|null
*/
public function getExpert(): ?Expert
{
return $this->expert;
}
/**
* Set expert
*
* @param Expert|null $expert
*
* @return self
*/
public function setExpert(?Expert $expert): self
{
$this->expert = $expert;
// Keep denormalised name in sync for display if the Expert record is later removed
if ($expert !== null) {
$this->setExpertName((string) $expert);
}
return $this;
}
/**
* Set hourlyRate
*
* @param string|null $hourlyRate
*
* @return self
*/
public function setHourlyRate(?string $hourlyRate): self
{
$this->hourlyRate = $hourlyRate;
return $this;
}
/**
* Get hourlyRate
*
* @return string|null
*/
public function getHourlyRate(): ?string
{
return $this->hourlyRate;
}
/**
* Get hourlyRate formatted
*
* @return string|null
*/
public function getHourlyRateFormatted(): ?string
{
if ($this->getHourlyRate() === null) {
return null;
}
return sprintf('£ %s', $this->getHourlyRate());
}
/**
* Set hoursEstimated
*
* @param string|null $hoursEstimated
*
* @return self
*/
public function setHoursEstimated(?string $hoursEstimated): self
{
$this->hoursEstimated = $hoursEstimated;
return $this;
}
/**
* Get hoursEstimated
*
* @return string|null
*/
public function getHoursEstimated(): ?string
{
return $this->hoursEstimated;
}
/**
* Get hoursEstimated formatted
*
* @return string|null
*/
public function getHoursEstimatedFormatted(): ?string
{
if ($this->getHoursEstimated() === null) {
return null;
}
return sprintf('%s Hour%s', $this->getHoursEstimated(), $this->getHoursEstimated() > 1 ? 's' : '');
}
/**
* Set turnAroundTime
*
* @param string|null $turnAroundTime
*
* @return self
*/
public function setTurnAroundTime(?string $turnAroundTime): self
{
$this->turnAroundTime = $turnAroundTime;
return $this;
}
/**
* Get turnAroundTime
*
* @return string|null
*/
public function getTurnAroundTime(): ?string
{
return $this->turnAroundTime;
}
/**
* Get turnAroundTimeOptions
*
* @return array
*/
public static function getTurnAroundTimeOptions(): array
{
return [
'1 - 2 weeks' => self::TURN_AROUND_TIME_1_2_WEEKS,
'2 - 4 weeks' => self::TURN_AROUND_TIME_2_4_WEEKS,
'4 - 6 weeks' => self::TURN_AROUND_TIME_4_6_WEEKS,
'6 - 8 weeks' => self::TURN_AROUND_TIME_6_8_WEEKS,
'8 - 12 weeks' => self::TURN_AROUND_TIME_8_12_WEEKS,
'12+ weeks' => self::TURN_AROUND_TIME_12_WEEKS,
];
}
/**
* Get turnAroundTimeLabel
*
* @return string
*/
public function getTurnAroundTimeLabel(): string
{
$options = array_flip(self::getTurnAroundTimeOptions());
return $options[$this->getTurnAroundTime()] ?? '';
}
/**
* Get time required formatted for widget display.
* e.g. "6 Hours (2 - 4 weeks turn-around)"
*
* @return string|null
*/
public function getTimeRequiredFormatted(): ?string
{
$hoursFormatted = $this->getHoursEstimatedFormatted();
$turnAroundLabel = $this->getTurnAroundTimeLabel();
if ($hoursFormatted && $turnAroundLabel) {
return sprintf('%s (%s turn-around)', $hoursFormatted, $turnAroundLabel);
}
if ($hoursFormatted) {
return $hoursFormatted;
}
if ($turnAroundLabel) {
return sprintf('%s turn-around', $turnAroundLabel);
}
return null;
}
/**
* Set dateInstructed
*
* @param DateTime|null $dateInstructed
*
* @return self
*/
public function setDateInstructed(?DateTime $dateInstructed): self
{
$this->dateInstructed = $dateInstructed;
return $this;
}
/**
* Get dateInstructed
*
* @return DateTime|null
*/
public function getDateInstructed(): ?DateTime
{
return $this->dateInstructed;
}
/**
* Calculates the dateInstructed as the current date.
*
* @return DateTime
*/
public function calculateDateInstructed(): DateTime
{
return new DateTime();
}
/**
* Set dateDue
*
* @param DateTime|null $dateDue
*
* @return self
*/
public function setDateDue(?DateTime $dateDue): self
{
$this->dateDue = $dateDue;
return $this;
}
/**
* Get dateDue
*
* @return DateTime|null
*/
public function getDateDue(): ?DateTime
{
return $this->dateDue;
}
/**
* Calculates the dateDue based on the turnAroundTime selected.
* Uses the maximum weeks from the turn-around range + dateInstructed.
*
* @return DateTime|null
*/
public function calculateDateDue(): ?DateTime
{
$turnAroundTime = $this->getTurnAroundTime();
if (!$turnAroundTime) {
return null;
}
$weeksMap = [
self::TURN_AROUND_TIME_1_2_WEEKS => 2,
self::TURN_AROUND_TIME_2_4_WEEKS => 4,
self::TURN_AROUND_TIME_4_6_WEEKS => 6,
self::TURN_AROUND_TIME_6_8_WEEKS => 8,
self::TURN_AROUND_TIME_8_12_WEEKS => 12,
self::TURN_AROUND_TIME_12_WEEKS => 14,
];
$weeks = $weeksMap[$turnAroundTime] ?? null;
if (!$weeks) {
return null;
}
$baseDate = $this->getDateInstructed() ?? new DateTime();
$dueDate = clone $baseDate;
$dueDate->modify(sprintf('+%d weeks', $weeks));
return $dueDate;
}
/**
* Set dateDraftReportReceived
*
* @param DateTime|null $dateDraftReportReceived
*
* @return self
*/
public function setDateDraftReportReceived(?DateTime $dateDraftReportReceived): self
{
$this->dateDraftReportReceived = $dateDraftReportReceived;
return $this;
}
/**
* Get dateDraftReportReceived
*
* @return DateTime|null
*/
public function getDateDraftReportReceived(): ?DateTime
{
return $this->dateDraftReportReceived;
}
/**
* Set dateFinalReportReceived
*
* @param DateTime|null $dateFinalReportReceived
*
* @return self
*/
public function setDateFinalReportReceived(?DateTime $dateFinalReportReceived): self
{
$this->dateFinalReportReceived = $dateFinalReportReceived;
return $this;
}
/**
* Get dateFinalReportReceived
*
* @return DateTime|null
*/
public function getDateFinalReportReceived(): ?DateTime
{
return $this->dateFinalReportReceived;
}
/**
* Set dateInvoiceReceived
*
* @param DateTime|null $dateInvoiceReceived
*
* @return self
*/
public function setDateInvoiceReceived(?DateTime $dateInvoiceReceived): self
{
$this->dateInvoiceReceived = $dateInvoiceReceived;
return $this;
}
/**
* Get dateInvoiceReceived
*
* @return DateTime|null
*/
public function getDateInvoiceReceived(): ?DateTime
{
return $this->dateInvoiceReceived;
}
/**
* Set invoiceReference
*
* @param string|null $invoiceReference
*
* @return self
*/
public function setInvoiceReference(?string $invoiceReference): self
{
$this->invoiceReference = $invoiceReference;
return $this;
}
/**
* Get invoiceReference
*
* @return string|null
*/
public function getInvoiceReference(): ?string
{
return $this->invoiceReference;
}
/**
* Set hoursInvoiced
*
* @param string|null $hoursInvoiced
*
* @return self
*/
public function setHoursInvoiced(?string $hoursInvoiced): self
{
$this->hoursInvoiced = $hoursInvoiced;
return $this;
}
/**
* Get hoursInvoiced
*
* @return string|null
*/
public function getHoursInvoiced(): ?string
{
return $this->hoursInvoiced;
}
/**
* Set nettAmount
*
* @param string|null $nettAmount
*
* @return self
*/
public function setNettAmount(?string $nettAmount): self
{
$this->nettAmount = $nettAmount;
return $this;
}
/**
* Get nettAmount
*
* @return string|null
*/
public function getNettAmount(): ?string
{
return $this->nettAmount;
}
/**
* Set vatPercentage
*
* @param string|null $vatPercentage
*
* @return self
*/
public function setVatPercentage(?string $vatPercentage): self
{
$this->vatPercentage = $vatPercentage;
return $this;
}
/**
* Get vatPercentage
*
* @return string|null
*/
public function getVatPercentage(): ?string
{
return $this->vatPercentage;
}
/**
* Set totalAmount
*
* @param string|null $totalAmount
*
* @return self
*/
public function setTotalAmount(?string $totalAmount): self
{
$this->totalAmount = $totalAmount;
return $this;
}
/**
* Get totalAmount
*
* @return string|null
*/
public function getTotalAmount(): ?string
{
return $this->totalAmount;
}
/**
* Set created
*
* @param DateTime $created
*
* @return self
*/
public function setCreated(DateTime $created): self
{
$this->created = $created;
return $this;
}
/**
* Get created
*
* @return DateTime
*/
public function getCreated(): ?DateTime
{
return $this->created;
}
/**
* Set updated
*
* @param DateTime $updated
*
* @return self
*/
public function setUpdated(DateTime $updated): self
{
$this->updated = $updated;
return $this;
}
/**
* Get updated
*
* @return DateTime
*/
public function getUpdated(): ?DateTime
{
return $this->updated;
}
/**
* Set deletedAt
*
* @param DateTime|null $deletedAt
*
* @return self
*/
public function setDeletedAt(?DateTime $deletedAt): self
{
$this->deletedAt = $deletedAt;
return $this;
}
/**
* Get deletedAt
*
* @return DateTime|null
*/
public function getDeletedAt(): ?DateTime
{
return $this->deletedAt;
}
/**
* Set project
*
* @param Project|null $project
*
* @return self
*/
public function setProject(?Project $project): self
{
$this->project = $project;
return $this;
}
/**
* Get project
*
* @return Project|null
*/
public function getProject(): ?Project
{
return $this->project;
}
/**
* Get details
*
* @return Collection|ExpertReportRequestDetail[]
*/
public function getDetails(): Collection
{
return $this->details;
}
/**
* Add detail
*
* @param ExpertReportRequestDetail $detail
*
* @return self
*/
public function addDetail(ExpertReportRequestDetail $detail): self
{
if (!$this->details->contains($detail)) {
$this->details[] = $detail;
$detail->setExpertReportRequest($this);
}
return $this;
}
/**
* Remove detail
*
* @param ExpertReportRequestDetail $detail
*
* @return self
*/
public function removeDetail(ExpertReportRequestDetail $detail): self
{
$this->details->removeElement($detail);
return $this;
}
/**
* Check if the expert report has any important notes.
*
* @return bool
*/
public function getHasImportantNotes(): bool
{
foreach ($this->details as $detail) {
if ($detail->getIsImportant()) {
return true;
}
}
return false;
}
/**
* Get expert display name for templates.
*
* @return string
*/
public function getExpertDisplayName(): string
{
return $this->expertName ?? 'Not Set';
}
}