<?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="ChronologyRequest")
*
* @ORM\Entity
*
* @ORM\HasLifecycleCallbacks
*
* @Gedmo\SoftDeleteable(fieldName="deletedAt", timeAware=false)
*
* @Audit\Auditable
*
* @Audit\Security(view={"ROLE_ALLOWED_TO_AUDIT"})
*/
class ChronologyRequest implements ServiceableInterface
{
use ServiceableTrait;
use FilterableClassConstantsTrait;
// chronologyType constants
public const CHRONOLOGY_TYPE_DETAILED = 1;
public const CHRONOLOGY_TYPE_SUMMARY = 2;
// extensionRequestStatus constants
public const EXTENSION_REQUEST_STATUS_REQUESTED = 'requested';
public const EXTENSION_REQUEST_STATUS_REQUESTED__LABEL = 'Requested';
public const EXTENSION_REQUEST_STATUS_APPROVED = 'approved';
public const EXTENSION_REQUEST_STATUS_APPROVED__LABEL = 'Approved';
public const EXTENSION_REQUEST_STATUS_DECLINED = 'declined';
public const EXTENSION_REQUEST_STATUS_DECLINED__LABEL = 'Declined';
/**
* @var ServiceRequest
*
* @ORM\OneToOne(targetEntity="MedBrief\MSR\Entity\ServiceRequest", mappedBy="chronologyRequest", cascade={"persist"})
*/
protected $serviceRequest;
/**
* @var int
*
* @ORM\Column(name="id", type="integer")
*
* @ORM\Id
*
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
protected $id;
/**
* @deprecated 2023.12.01
*
* @var int|null
*
* @ORM\Column(name="chronologyType", type="integer", nullable=true)
*/
protected $chronologyType;
/**
* @var bool|null
*
* @ORM\Column(name="isUpdate", type="boolean", nullable=true)
*/
protected $isUpdate;
/**
* @var DateTime|null
*
* @ORM\Column(name="dateUploaded", type="date", nullable=true)
*/
protected $dateUploaded;
/**
* @var DateTime|null
*
* @ORM\Column(name="dateSupplierInstructed", type="date", nullable=true)
*/
protected $dateSupplierInstructed;
/**
* @var DateTime|null
*
* @ORM\Column(name="dateSupplierAccepted", type="date", nullable=true)
*/
protected $dateSupplierAccepted;
/**
* @var DateTime|null
*
* @ORM\Column(name="dateStarted", type="date", nullable=true)
*/
protected $dateStarted;
/**
* @var DateTime|null
*
* @ORM\Column(name="dateDue", type="date", nullable=true)
*/
protected $dateDue;
/**
* @var string|null
*
* @ORM\Column(name="timeAllowed", type="decimal", precision=7, scale=2, nullable=true)
*/
protected $timeAllowed;
// Not sure how to represent this figure
/**
* @var string|null
*
* @ORM\Column(name="timeToComplete", type="decimal", precision=7, scale=2, nullable=true)
*/
protected $timeToComplete;
/**
* @var string|null
*
* @ORM\Column(name="extensionLength", type="decimal", precision=7, scale=2, nullable=true)
*/
protected $extensionLength;
/**
* @var DateTime
*
* @ORM\Column(name="created", type="datetime")
*
* @Gedmo\Timestampable(on="create")
*/
protected $created;
/**
* @var DateTime
*
* @ORM\Column(name="updated", type="datetime")
*
* @Gedmo\Timestampable(on="update")
*/
protected $updated;
/**
* @var DateTime|null
*
* @ORM\Column(name="deletedAt", type="datetime", nullable=true)
*/
protected $deletedAt;
/**
* @var Project
*
* @ORM\ManyToOne(targetEntity="MedBrief\MSR\Entity\Project", inversedBy="chronologyRequests")
*
* @ORM\JoinColumns({
*
* @ORM\JoinColumn(name="project_id", referencedColumnName="id")
* })
*/
protected $project;
/**
* @var ArrayCollection
*
* @ORM\OneToMany(targetEntity="MedBrief\MSR\Entity\BatchRequest", mappedBy="chronologyRequest", cascade={"persist"})
*/
protected $batches;
/**
* @var HumanResource
*
* @ORM\ManyToOne(targetEntity="MedBrief\MSR\Entity\HumanResource")
*
* @ORM\JoinColumns({
*
* @ORM\JoinColumn(name="supplierResourceInstructed_id", referencedColumnName="id")
* })
*/
protected $supplierResourceInstructed;
/**
* @var HumanResource
*
* @ORM\ManyToOne(targetEntity="MedBrief\MSR\Entity\HumanResource")
*
* @ORM\JoinColumns({
*
* @ORM\JoinColumn(name="supplier_id", referencedColumnName="id")
* })
*/
protected $supplier;
/**
* @var HumanResource
*
* @ORM\ManyToOne(targetEntity="MedBrief\MSR\Entity\HumanResource")
*
* @ORM\JoinColumns({
*
* @ORM\JoinColumn(name="templateBy_id", referencedColumnName="id")
* })
*/
protected $templateBy;
/**
* @var int|null
*
* @ORM\Column(name="claimCategory", type="integer", nullable=true)
*/
protected $claimCategory;
/**
* @var string|null
*
* @ORM\Column(name="claimCategoryOther", type="string", length=255, nullable=true)
*/
protected $claimCategoryOther;
/**
* @var string|null
*
* @ORM\Column(name="extensionRequestStatus", type="string", length=24, nullable=true)
*/
protected $extensionRequestStatus;
/**
* @var bool
*
* @ORM\Column(name="scheduleOfRadiologyCompleted", type="boolean", options={"default"=false})
*/
protected $scheduleOfRadiologyCompleted = false;
/**
* @var string|null
*
* @ORM\Column(name="scheduleOfRadiologyTime", type="decimal", precision=7, scale=2, nullable=true)
*/
protected $scheduleOfRadiologyTime;
/**
* @var string|null
*
* @ORM\Column(name="qaTime", type="decimal", precision=7, scale=2, nullable=true)
*/
protected $qaTime;
/**
* @var ArrayCollection
*
* @ORM\OneToMany(targetEntity="MedBrief\MSR\Entity\ChronologyRequestDetail", mappedBy="chronologyRequest")
*/
protected $details;
/**
* @var bool
*
* @ORM\Column(name="isUrgent", type="boolean", options={"default": false})
*/
protected bool $isUrgent = false;
/**
* Constructor
*/
public function __construct()
{
$this->batches = new ArrayCollection();
$this->details = new ArrayCollection();
}
/**
* __toString
*
* @return string
*/
public function __toString()
{
return (string) $this->getId();
}
/**
* Get id
*
* @return int
*/
public function getId(): ?int
{
return $this->id;
}
/**
* Set chronologyType
*
* @param int|null $chronologyType
*
* @return ChronologyRequest
*/
public function setChronologyType(?int $chronologyType = null): self
{
$this->chronologyType = $chronologyType;
return $this;
}
/**
* Get chronologyType
*
* @return int|null
*/
public function getChronologyType(): ?int
{
return $this->chronologyType;
}
/**
* Get reasonForCancellationOptions
*
* @return array
*/
public static function getChronologyTypeOptions()
{
return [
self::CHRONOLOGY_TYPE_DETAILED => 'Detailed',
self::CHRONOLOGY_TYPE_SUMMARY => 'Summary',
];
}
/**
* Get reasonForCancellationLabel
*
* @return int
*/
public function getChronologyTypeLabel()
{
$options = self::getChronologyTypeOptions();
return $options[$this->getChronologyType()] ?? '';
}
/**
* Set isUpdate
*
* @param bool $isUpdate
*
* @return ChronologyRequest
*/
public function setIsUpdate($isUpdate)
{
$this->isUpdate = $isUpdate;
return $this;
}
/**
* Get isUpdate
*
* @return bool
*/
public function getIsUpdate()
{
return $this->isUpdate;
}
/**
* Set isUrgent
*
* @param bool $isUrgent
*
* @return self
*/
public function setIsUrgent(bool $isUrgent): self
{
$this->isUrgent = $isUrgent;
return $this;
}
/**
* Get isUrgent
*
* @return bool
*/
public function getIsUrgent(): bool
{
return $this->isUrgent;
}
/**
* Get isUrgentLabel
*
* @return string
*/
public function getIsUrgentLabel(): string
{
return $this->isUrgent ? 'Yes' : 'No';
}
/**
* Get isUpdateLabel
*
* @return string
*/
public function getIsUpdateLabel(): string
{
return $this->isUpdate ? 'Update' : 'New';
}
/**
* Get isUpdateOptions
*
* @return array
*/
public static function getIsUpdateOptions()
{
return [
'New' => false,
'Update' => true,
];
}
/**
* Set dateUploaded
*
* @param DateTime $dateUploaded
*
* @return ChronologyRequest
*/
public function setDateUploaded($dateUploaded)
{
$this->dateUploaded = $dateUploaded;
return $this;
}
/**
* Get dateUploaded
*
* @return DateTime
*/
public function getDateUploaded()
{
return $this->dateUploaded;
}
/**
* Calculates the dateUploaded as the current date.
* Keep in separate function in case more calculation needs to be applied.
*
* @return DateTime
*/
public function calculateDateUploaded(): DateTime
{
return new DateTime();
}
/**
* Set dateSupplierInstructed
*
* @param DateTime $dateSupplierInstructed
*
* @return ChronologyRequest
*/
public function setDateSupplierInstructed($dateSupplierInstructed)
{
$this->dateSupplierInstructed = $dateSupplierInstructed;
return $this;
}
/**
* Get dateSupplierInstructed
*
* @return DateTime
*/
public function getDateSupplierInstructed()
{
return $this->dateSupplierInstructed;
}
/**
* Calculates the dateSupplierInstructed as the current date.
* Keep in separate function in case more calculation needs to be applied.
*
* @return DateTime
*/
public function calculateDateSupplierInstructed(): DateTime
{
return new DateTime();
}
/**
* Set dateSupplierAccepted
*
* @param DateTime $dateSupplierAccepted
*
* @return ChronologyRequest
*/
public function setDateSupplierAccepted($dateSupplierAccepted)
{
$this->dateSupplierAccepted = $dateSupplierAccepted;
return $this;
}
/**
* Get dateSupplierAccepted
*
* @return DateTime
*/
public function getDateSupplierAccepted()
{
return $this->dateSupplierAccepted;
}
/**
* Set dateStarted
*
* @param DateTime $dateStarted
*
* @return ChronologyRequest
*/
public function setDateStarted($dateStarted)
{
$this->dateStarted = $dateStarted;
return $this;
}
/**
* Get dateStarted
*
* @return DateTime
*/
public function getDateStarted()
{
return $this->dateStarted;
}
/**
* Calculates the dateStarted as the current date.
* Keep in separate function in case more calculation needs to be applied.
*
* @return DateTime
*/
public function calculateDateStarted(): DateTime
{
return new DateTime();
}
/**
* Set dateDue
*
* @param DateTime $dateDue
*
* @return ChronologyRequest
*/
public function setDateDue($dateDue)
{
$this->dateDue = $dateDue;
return $this;
}
/**
* Get dateDue
*
* @return DateTime
*/
public function getDateDue()
{
return $this->dateDue;
}
/**
* Calculates the dateDue based on the ServiceOption selected.
*
* @return DateTime
*/
public function calculateDateDue(): DateTime
{
$serviceOption = $this->getServiceOption();
$dayOffset = 10;
if ($serviceOption) {
switch ($serviceOption->getAppliesToSubCategory()) {
case ServiceOption::APPLIES_TO_SUB_CATEGORY_CHRON:
case ServiceOption::APPLIES_TO_SUB_CATEGORY_CHRON_SOR:
$dayOffset = 15;
break;
case ServiceOption::APPLIES_TO_SUB_CATEGORY_CLINICAL_SUMMARY:
$dayOffset = 10;
break;
// Even thought the default is 10 days, still noting this explicitly for this sub category.
case ServiceOption::APPLIES_TO_SUB_CATEGORY_SOR:
$dayOffset = 10;
break;
default:
$dayOffset = 10;
break;
}
}
return new DateTime(sprintf('+%1$s weekdays', $dayOffset));
}
/**
* Set timeAllowed
*
* @param int $timeAllowed
*
* @return ChronologyRequest
*/
public function setTimeAllowed($timeAllowed)
{
$this->timeAllowed = $timeAllowed;
return $this;
}
/**
* Get timeAllowed
*
* @return int
*/
public function getTimeAllowed()
{
return $this->timeAllowed;
}
/**
* Returns the time allowed in hours as a formatted string.
*
* @return string|null
*/
public function getTimeAllowedFormatted(): ?string
{
if (!$this->getTimeAllowed()) {
return null;
}
$extensionLengthStr = '';
if ($this->getExtensionRequestStatus() === self::EXTENSION_REQUEST_STATUS_APPROVED) {
$extensionLengthStr = $this->getExtensionLength() ? '+ ' . $this->getExtensionLength() : '';
}
return sprintf('%1$s %3$s Hour%2$s', $this->getTimeAllowed(), $this->getTimeAllowed() > 1 ? 's' : '', $extensionLengthStr);
}
/**
* Set timeToComplete
*
* @param int $timeToComplete
*
* @return ChronologyRequest
*/
public function setTimeToComplete($timeToComplete)
{
$this->timeToComplete = $timeToComplete;
return $this;
}
/**
* Get timeToComplete
*
* @return int
*/
public function getTimeToComplete()
{
return $this->timeToComplete;
}
/**
* Set extensionLength
*
* @param int $extensionLength
*
* @return ChronologyRequest
*/
public function setExtensionLength($extensionLength)
{
$this->extensionLength = $extensionLength;
return $this;
}
/**
* Get extensionLength
*
* @return int
*/
public function getExtensionLength()
{
return $this->extensionLength;
}
/**
* Set created
*
* @param DateTime $created
*
* @return ChronologyRequest
*/
public function setCreated($created)
{
$this->created = $created;
return $this;
}
/**
* Get created
*
* @return DateTime
*/
public function getCreated()
{
return $this->created;
}
/**
* Set updated
*
* @param DateTime $updated
*
* @return ChronologyRequest
*/
public function setUpdated($updated)
{
$this->updated = $updated;
return $this;
}
/**
* Get updated
*
* @return DateTime
*/
public function getUpdated()
{
return $this->updated;
}
/**
* Set deletedAt
*
* @param DateTime $deletedAt
*
* @return ChronologyRequest
*/
public function setDeletedAt($deletedAt)
{
$this->deletedAt = $deletedAt;
return $this;
}
/**
* Get deletedAt
*
* @return DateTime
*/
public function getDeletedAt()
{
return $this->deletedAt;
}
/**
* Set project
*
* @param Project $project
*
* @return ChronologyRequest
*/
public function setProject(?Project $project = null)
{
$this->project = $project;
// Pre-populate the claim category from the project
if ($this->getProject()) {
$this->setClaimCategory($this->getProject()->getClaimCategory());
} else {
$this->setClaimCategory(null);
}
return $this;
}
/**
* Get project
*
* @return Project
*/
public function getProject()
{
return $this->project;
}
/**
* Add batch
*
* @param BatchRequest $batch
*
* @return ChronologyRequest
*/
public function addBatch(BatchRequest $batch)
{
$this->batches[] = $batch;
return $this;
}
/**
* Remove batch
*
* @param BatchRequest $batch
*/
public function removeBatch(BatchRequest $batch)
{
$this->batches->removeElement($batch);
}
/**
* Get batches
*
* @return Collection
*/
public function getBatches()
{
return $this->batches;
}
/**
* Removes all batches linked to this ChronologyRequest.
* Used as a preRemove lifecycle event, to avoid broken relationships when
* soft deleting a ChronologyRequest. Takes the place of a onDelete=NULL database
* call.
*
* @ORM\PreRemove
*
* @return void
*/
public function clearBatches()
{
foreach ($this->getBatches() as $batch) {
$batch->setChronologyRequest(null);
}
}
/**
* Set supplierResourceInstructed
*
* @param HumanResource $supplierResourceInstructed
*
* @return ChronologyRequest
*/
public function setSupplierResourceInstructed(?HumanResource $supplierResourceInstructed = null)
{
$this->supplierResourceInstructed = $supplierResourceInstructed;
return $this;
}
/**
* Get supplierResourceInstructed
*
* @return HumanResource
*/
public function getSupplierResourceInstructed()
{
return $this->supplierResourceInstructed;
}
/**
* Set supplier
*
* @param HumanResource $supplier
*
* @return ChronologyRequest
*/
public function setSupplier(?HumanResource $supplier = null)
{
$this->supplier = $supplier;
return $this;
}
/**
* Get supplier
*
* @return HumanResource
*/
public function getSupplier()
{
return $this->supplier;
}
/**
* Set templateBy
*
* @param HumanResource $templateBy
*
* @return ChronologyRequest
*/
public function setTemplateBy(?HumanResource $templateBy = null)
{
$this->templateBy = $templateBy;
return $this;
}
/**
* Get templateBy
*
* @return HumanResource
*/
public function getTemplateBy()
{
return $this->templateBy;
}
/**
* @return int|null
*/
public function getClaimCategory(): ?int
{
return $this->claimCategory;
}
/**
* @param int|null $claimCategory
*
* @return self
*/
public function setClaimCategory(?int $claimCategory): self
{
$this->claimCategory = $claimCategory;
return $this;
}
/**
* @return string|null
*/
public function getClaimCategoryLabel(): ?string
{
if ($this->getClaimCategory() === null) {
return '';
}
$options = Project::getClaimCategoryOptions();
return $options[$this->getClaimCategory()] ?? 'Other';
}
/**
* @return string|null
*/
public function getClaimCategoryOther(): ?string
{
return $this->claimCategoryOther;
}
/**
* @param string|null $claimCategoryOther
*
* @return self
*/
public function setClaimCategoryOther(?string $claimCategoryOther): self
{
$this->claimCategoryOther = $claimCategoryOther;
return $this;
}
/**
* @return string|null
*/
public function getExtensionRequestStatus(): ?string
{
return $this->extensionRequestStatus;
}
/**
* @param string|null $extensionRequestStatus
*
* @return self
*/
public function setExtensionRequestStatus(?string $extensionRequestStatus = null): self
{
$this->extensionRequestStatus = $extensionRequestStatus;
return $this;
}
/**
* @return array
*/
public static function getExtensionRequestStatusOptions(): array
{
return self::getConstantsWithLabelsAsChoices('EXTENSION_REQUEST_STATUS');
}
/**
* Returns an array of Labels to be readable by humans.
*
* @return string|null
*/
public function getExtensionRequestStatusLabel(): ?string
{
if ($this->getExtensionRequestStatus() === null) {
return '';
}
$options = array_flip(self::getExtensionRequestStatusOptions());
return $options[$this->getExtensionRequestStatus()] ?? '';
}
/**
* @return bool|null
*/
public function getScheduleOfRadiologyCompleted(): ?bool
{
return $this->scheduleOfRadiologyCompleted;
}
/**
* @param bool $scheduleOfRadiologyCompleted
*
* @return self
*/
public function setScheduleOfRadiologyCompleted(bool $scheduleOfRadiologyCompleted): self
{
$this->scheduleOfRadiologyCompleted = $scheduleOfRadiologyCompleted;
return $this;
}
/**
* @return float|null
*/
public function getScheduleOfRadiologyTime(): ?float
{
return $this->scheduleOfRadiologyTime;
}
/**
* @param float|null $scheduleOfRadiologyTime
*
* @return self
*/
public function setScheduleOfRadiologyTime(?float $scheduleOfRadiologyTime): self
{
$this->scheduleOfRadiologyTime = $scheduleOfRadiologyTime;
return $this;
}
/**
* @return float|null
*/
public function getQaTime(): ?float
{
return $this->qaTime;
}
/**
* @param float|null $qaTime
*
* @return self
*/
public function setQaTime(?float $qaTime): self
{
$this->qaTime = $qaTime;
return $this;
}
/**
* @return Collection|ChronologyRequestDetail[]
*/
public function getDetails(): Collection
{
return $this->details;
}
/**
* @param ChronologyRequestDetail $detail
*
* @return self
*/
public function addDetail(ChronologyRequestDetail $detail): self
{
if (!$this->details->contains($detail)) {
$this->details[] = $detail;
$detail->setChronologyRequest($this);
}
return $this;
}
/**
* @param ChronologyRequestDetail $detail
*
* @return self
*/
public function removeDetail(ChronologyRequestDetail $detail): self
{
if ($this->details->removeElement($detail)) {
// set the owning side to null (unless already changed)
if ($detail->getChronologyRequest() === $this) {
$detail->setChronologyRequest(null);
}
}
return $this;
}
/**
* @return float
*/
public function getTotalTime(): float
{
$serviceOption = $this->getServiceOption();
if (!$serviceOption) {
return 0;
}
$timeToComplete = $this->getTimeToComplete() ?? 0;
$scheduleOfRadiologyTime = $this->getScheduleOfRadiologyTime() ?? 0;
$qaTime = $this->getQaTime() ?? 0;
switch ($serviceOption->getAppliesToSubCategory()) {
case ServiceOption::APPLIES_TO_SUB_CATEGORY_CHRON:
case ServiceOption::APPLIES_TO_SUB_CATEGORY_CLINICAL_SUMMARY:
return $timeToComplete + $qaTime;
case ServiceOption::APPLIES_TO_SUB_CATEGORY_CHRON_SOR:
return $timeToComplete + $scheduleOfRadiologyTime + $qaTime;
case ServiceOption::APPLIES_TO_SUB_CATEGORY_SOR:
return $scheduleOfRadiologyTime + $qaTime;
default:
return 0;
}
}
/**
* @return null|string
*/
public function getLatestNoteContent(): ?string
{
/** @var ChronologyRequestDetail $latestNote */
$latestNote = $this->getDetails()->last();
return $latestNote ? $latestNote->getContent() : null;
}
/**
*
* @return null|DateTime
*/
public function getLatestNoteDate(): ?DateTime
{
/** @var ChronologyRequestDetail $latestNote */
$latestNote = $this->getDetails()->last();
return $latestNote ? $latestNote->getDisplayDate() : null;
}
/**
* @return bool
*/
public function hasImportantNotes(): bool
{
return $this->getDetails()->filter(function (ChronologyRequestDetail $detail) {
return $detail->getIsImportant();
})->count();
}
/**
* @return bool
*/
public function isClaimCategoryOther(): bool
{
return $this->getClaimCategory() === Project::CLAIM_CATEGORY_OTHER;
}
/**
* Returns true if the project the chronology is linked to requires firm records review before commencing with the chronology.
*
* @return bool
*/
public function isFirmRecordsReviewRequired(): bool
{
return $this->getProject()
&& $this->getProject()->getMatterRequest()
&& $this->getProject()->getMatterRequest()->getServiceSelectionDetails()
&& $this->getProject()->getMatterRequest()->getServiceSelectionDetails()->isFirmReviewChoiceYes();
}
/**
* Returns true if the project the chronology is linked to requires clinical summary unsorted before commencing with the chronology.
*
* @return bool
*/
public function isClinicalSummaryConclusionRequired(): bool
{
return $this->getProject()
&& $this->getProject()->getMatterRequest()
&& $this->getProject()->getMatterRequest()->getServiceSelectionDetails()
&& $this->getProject()->getMatterRequest()->getServiceSelectionDetails()->isReviewChoiceMedBrief();
}
/**
* @return bool
*/
public function canAutoCancel(): bool
{
return $this->getServiceRequest() && $this->getServiceRequest()->getStatus() === ServiceRequest::STATUS_AWAITING_RECORDS;
}
}