src/Entity/ExpertReportRequest.php line 28

Open in your IDE?
  1. <?php
  2. namespace MedBrief\MSR\Entity;
  3. use DateTime;
  4. use DH\Auditor\Provider\Doctrine\Auditing\Annotation as Audit;
  5. use Doctrine\Common\Collections\ArrayCollection;
  6. use Doctrine\Common\Collections\Collection;
  7. use Doctrine\ORM\Mapping as ORM;
  8. use Gedmo\Mapping\Annotation as Gedmo;
  9. use MedBrief\MSR\Entity\Serviceable\ServiceableInterface;
  10. use MedBrief\MSR\Entity\Serviceable\ServiceableTrait;
  11. use MedBrief\MSR\Traits\FilterableClassConstantsTrait;
  12. /**
  13. * @ORM\Table(name="ExpertReportRequest")
  14. *
  15. * @ORM\Entity
  16. *
  17. * @ORM\HasLifecycleCallbacks
  18. *
  19. * @Gedmo\SoftDeleteable(fieldName="deletedAt", timeAware=false)
  20. *
  21. * @Audit\Auditable
  22. *
  23. * @Audit\Security(view={"ROLE_ALLOWED_TO_AUDIT"})
  24. */
  25. class ExpertReportRequest implements ServiceableInterface
  26. {
  27. use ServiceableTrait;
  28. use FilterableClassConstantsTrait;
  29. // Turn-around time constants
  30. public const TURN_AROUND_TIME_1_2_WEEKS = '1-2-weeks';
  31. public const TURN_AROUND_TIME_2_4_WEEKS = '2-4-weeks';
  32. public const TURN_AROUND_TIME_4_6_WEEKS = '4-6-weeks';
  33. public const TURN_AROUND_TIME_6_8_WEEKS = '6-8-weeks';
  34. public const TURN_AROUND_TIME_8_12_WEEKS = '8-12-weeks';
  35. public const TURN_AROUND_TIME_12_WEEKS = '12+-weeks';
  36. /**
  37. * @var int
  38. *
  39. * @ORM\Column(name="id", type="integer")
  40. *
  41. * @ORM\Id
  42. *
  43. * @ORM\GeneratedValue(strategy="IDENTITY")
  44. */
  45. protected int $id;
  46. /**
  47. * @var bool|null
  48. *
  49. * @ORM\Column(name="isUpdate", type="boolean", nullable=true)
  50. */
  51. protected ?bool $isUpdate = null;
  52. /**
  53. * @var string|null
  54. *
  55. * @ORM\Column(name="hourlyRate", type="decimal", precision=10, scale=2, nullable=true)
  56. */
  57. protected ?string $hourlyRate = null;
  58. /**
  59. * @var string|null
  60. *
  61. * @ORM\Column(name="hoursEstimated", type="decimal", precision=7, scale=2, nullable=true)
  62. */
  63. protected ?string $hoursEstimated = null;
  64. /**
  65. * @var string|null
  66. *
  67. * @ORM\Column(name="turnAroundTime", type="string", length=24, nullable=true)
  68. */
  69. protected ?string $turnAroundTime = null;
  70. /**
  71. * @var DateTime|null
  72. *
  73. * @ORM\Column(name="dateInstructed", type="date", nullable=true)
  74. */
  75. protected ?DateTime $dateInstructed = null;
  76. /**
  77. * @var DateTime|null
  78. *
  79. * @ORM\Column(name="dateDue", type="date", nullable=true)
  80. */
  81. protected ?DateTime $dateDue = null;
  82. /**
  83. * @var DateTime|null
  84. *
  85. * @ORM\Column(name="dateDraftReportReceived", type="date", nullable=true)
  86. */
  87. protected ?DateTime $dateDraftReportReceived = null;
  88. /**
  89. * @var DateTime|null
  90. *
  91. * @ORM\Column(name="dateFinalReportReceived", type="date", nullable=true)
  92. */
  93. protected ?DateTime $dateFinalReportReceived = null;
  94. /**
  95. * @var DateTime|null
  96. *
  97. * @ORM\Column(name="dateInvoiceReceived", type="date", nullable=true)
  98. */
  99. protected ?DateTime $dateInvoiceReceived = null;
  100. /**
  101. * @var string|null
  102. *
  103. * @ORM\Column(name="invoiceReference", type="string", length=255, nullable=true)
  104. */
  105. protected ?string $invoiceReference = null;
  106. /**
  107. * @var string|null
  108. *
  109. * @ORM\Column(name="hoursInvoiced", type="decimal", precision=7, scale=2, nullable=true)
  110. */
  111. protected ?string $hoursInvoiced = null;
  112. /**
  113. * @var string|null
  114. *
  115. * @ORM\Column(name="nettAmount", type="decimal", precision=10, scale=2, nullable=true)
  116. */
  117. protected ?string $nettAmount = null;
  118. /**
  119. * @var string|null
  120. *
  121. * @ORM\Column(name="vatPercentage", type="decimal", precision=5, scale=2, nullable=true)
  122. */
  123. protected ?string $vatPercentage = null;
  124. /**
  125. * @var string|null
  126. *
  127. * @ORM\Column(name="totalAmount", type="decimal", precision=10, scale=2, nullable=true)
  128. */
  129. protected ?string $totalAmount = null;
  130. /**
  131. * @var DateTime
  132. *
  133. * @ORM\Column(name="created", type="datetime")
  134. *
  135. * @Gedmo\Timestampable(on="create")
  136. */
  137. protected DateTime $created;
  138. /**
  139. * @var DateTime
  140. *
  141. * @ORM\Column(name="updated", type="datetime")
  142. *
  143. * @Gedmo\Timestampable(on="update")
  144. */
  145. protected DateTime $updated;
  146. /**
  147. * @var DateTime|null
  148. *
  149. * @ORM\Column(name="deletedAt", type="datetime", nullable=true)
  150. */
  151. protected ?DateTime $deletedAt = null;
  152. /**
  153. * @var Project
  154. *
  155. * @ORM\ManyToOne(targetEntity="MedBrief\MSR\Entity\Project", inversedBy="expertReportRequests")
  156. *
  157. * @ORM\JoinColumns({
  158. *
  159. * @ORM\JoinColumn(name="project_id", referencedColumnName="id")
  160. * })
  161. */
  162. protected Project $project;
  163. /**
  164. * @var Collection|ExpertReportRequestDetail[]
  165. *
  166. * @ORM\OneToMany(targetEntity="MedBrief\MSR\Entity\ExpertReportRequestDetail", mappedBy="expertReportRequest", cascade={"persist", "remove"})
  167. *
  168. * @ORM\OrderBy({"displayDate" = "DESC"})
  169. */
  170. protected Collection $details;
  171. /**
  172. * @var ServiceRequest|null
  173. *
  174. * @ORM\OneToOne(targetEntity="MedBrief\MSR\Entity\ServiceRequest", mappedBy="expertReportRequest", cascade={"persist"})
  175. */
  176. protected ?ServiceRequest $serviceRequest = null;
  177. /**
  178. * @var Expert|null
  179. *
  180. * @ORM\ManyToOne(targetEntity="MedBrief\MSR\Entity\Expert")
  181. *
  182. * @ORM\JoinColumn(name="expert_id", referencedColumnName="id", nullable=true, onDelete="SET NULL")
  183. */
  184. protected ?Expert $expert = null;
  185. /**
  186. * Denormalised expert name, stored for display if the Expert record is removed.
  187. *
  188. * @var string|null
  189. *
  190. * @ORM\Column(name="expertName", type="string", length=255, nullable=true)
  191. */
  192. private ?string $expertName = null;
  193. /**
  194. * Constructor
  195. */
  196. public function __construct()
  197. {
  198. $this->details = new ArrayCollection();
  199. }
  200. /**
  201. * __toString
  202. *
  203. * @return string
  204. */
  205. public function __toString()
  206. {
  207. return (string) $this->getId();
  208. }
  209. /**
  210. * Get id
  211. *
  212. * @return int|null
  213. */
  214. public function getId(): ?int
  215. {
  216. return $this->id;
  217. }
  218. /**
  219. * Set isUpdate
  220. *
  221. * @param bool|null $isUpdate
  222. *
  223. * @return self
  224. */
  225. public function setIsUpdate(?bool $isUpdate): self
  226. {
  227. $this->isUpdate = $isUpdate;
  228. return $this;
  229. }
  230. /**
  231. * Get isUpdate
  232. *
  233. * @return bool|null
  234. */
  235. public function getIsUpdate(): ?bool
  236. {
  237. return $this->isUpdate;
  238. }
  239. /**
  240. * Get isUpdateLabel
  241. *
  242. * @return string
  243. */
  244. public function getIsUpdateLabel(): string
  245. {
  246. return $this->isUpdate ? 'Update' : 'New';
  247. }
  248. /**
  249. * Get isUpdateOptions
  250. *
  251. * @return array
  252. */
  253. public static function getIsUpdateOptions(): array
  254. {
  255. return [
  256. 'New' => false,
  257. 'Update' => true,
  258. ];
  259. }
  260. /**
  261. * Get expertName
  262. *
  263. * @return string|null
  264. */
  265. public function getExpertName(): ?string
  266. {
  267. return $this->expertName;
  268. }
  269. /**
  270. * Set expertName
  271. *
  272. * @param string|null $expertName
  273. *
  274. * @return self
  275. */
  276. public function setExpertName(?string $expertName): self
  277. {
  278. $this->expertName = $expertName;
  279. return $this;
  280. }
  281. /**
  282. * Get expert
  283. *
  284. * @return Expert|null
  285. */
  286. public function getExpert(): ?Expert
  287. {
  288. return $this->expert;
  289. }
  290. /**
  291. * Set expert
  292. *
  293. * @param Expert|null $expert
  294. *
  295. * @return self
  296. */
  297. public function setExpert(?Expert $expert): self
  298. {
  299. $this->expert = $expert;
  300. // Keep denormalised name in sync for display if the Expert record is later removed
  301. if ($expert !== null) {
  302. $this->setExpertName((string) $expert);
  303. }
  304. return $this;
  305. }
  306. /**
  307. * Set hourlyRate
  308. *
  309. * @param string|null $hourlyRate
  310. *
  311. * @return self
  312. */
  313. public function setHourlyRate(?string $hourlyRate): self
  314. {
  315. $this->hourlyRate = $hourlyRate;
  316. return $this;
  317. }
  318. /**
  319. * Get hourlyRate
  320. *
  321. * @return string|null
  322. */
  323. public function getHourlyRate(): ?string
  324. {
  325. return $this->hourlyRate;
  326. }
  327. /**
  328. * Get hourlyRate formatted
  329. *
  330. * @return string|null
  331. */
  332. public function getHourlyRateFormatted(): ?string
  333. {
  334. if ($this->getHourlyRate() === null) {
  335. return null;
  336. }
  337. return sprintf('£ %s', $this->getHourlyRate());
  338. }
  339. /**
  340. * Set hoursEstimated
  341. *
  342. * @param string|null $hoursEstimated
  343. *
  344. * @return self
  345. */
  346. public function setHoursEstimated(?string $hoursEstimated): self
  347. {
  348. $this->hoursEstimated = $hoursEstimated;
  349. return $this;
  350. }
  351. /**
  352. * Get hoursEstimated
  353. *
  354. * @return string|null
  355. */
  356. public function getHoursEstimated(): ?string
  357. {
  358. return $this->hoursEstimated;
  359. }
  360. /**
  361. * Get hoursEstimated formatted
  362. *
  363. * @return string|null
  364. */
  365. public function getHoursEstimatedFormatted(): ?string
  366. {
  367. if ($this->getHoursEstimated() === null) {
  368. return null;
  369. }
  370. return sprintf('%s Hour%s', $this->getHoursEstimated(), $this->getHoursEstimated() > 1 ? 's' : '');
  371. }
  372. /**
  373. * Set turnAroundTime
  374. *
  375. * @param string|null $turnAroundTime
  376. *
  377. * @return self
  378. */
  379. public function setTurnAroundTime(?string $turnAroundTime): self
  380. {
  381. $this->turnAroundTime = $turnAroundTime;
  382. return $this;
  383. }
  384. /**
  385. * Get turnAroundTime
  386. *
  387. * @return string|null
  388. */
  389. public function getTurnAroundTime(): ?string
  390. {
  391. return $this->turnAroundTime;
  392. }
  393. /**
  394. * Get turnAroundTimeOptions
  395. *
  396. * @return array
  397. */
  398. public static function getTurnAroundTimeOptions(): array
  399. {
  400. return [
  401. '1 - 2 weeks' => self::TURN_AROUND_TIME_1_2_WEEKS,
  402. '2 - 4 weeks' => self::TURN_AROUND_TIME_2_4_WEEKS,
  403. '4 - 6 weeks' => self::TURN_AROUND_TIME_4_6_WEEKS,
  404. '6 - 8 weeks' => self::TURN_AROUND_TIME_6_8_WEEKS,
  405. '8 - 12 weeks' => self::TURN_AROUND_TIME_8_12_WEEKS,
  406. '12+ weeks' => self::TURN_AROUND_TIME_12_WEEKS,
  407. ];
  408. }
  409. /**
  410. * Get turnAroundTimeLabel
  411. *
  412. * @return string
  413. */
  414. public function getTurnAroundTimeLabel(): string
  415. {
  416. $options = array_flip(self::getTurnAroundTimeOptions());
  417. return $options[$this->getTurnAroundTime()] ?? '';
  418. }
  419. /**
  420. * Get time required formatted for widget display.
  421. * e.g. "6 Hours (2 - 4 weeks turn-around)"
  422. *
  423. * @return string|null
  424. */
  425. public function getTimeRequiredFormatted(): ?string
  426. {
  427. $hoursFormatted = $this->getHoursEstimatedFormatted();
  428. $turnAroundLabel = $this->getTurnAroundTimeLabel();
  429. if ($hoursFormatted && $turnAroundLabel) {
  430. return sprintf('%s (%s turn-around)', $hoursFormatted, $turnAroundLabel);
  431. }
  432. if ($hoursFormatted) {
  433. return $hoursFormatted;
  434. }
  435. if ($turnAroundLabel) {
  436. return sprintf('%s turn-around', $turnAroundLabel);
  437. }
  438. return null;
  439. }
  440. /**
  441. * Set dateInstructed
  442. *
  443. * @param DateTime|null $dateInstructed
  444. *
  445. * @return self
  446. */
  447. public function setDateInstructed(?DateTime $dateInstructed): self
  448. {
  449. $this->dateInstructed = $dateInstructed;
  450. return $this;
  451. }
  452. /**
  453. * Get dateInstructed
  454. *
  455. * @return DateTime|null
  456. */
  457. public function getDateInstructed(): ?DateTime
  458. {
  459. return $this->dateInstructed;
  460. }
  461. /**
  462. * Calculates the dateInstructed as the current date.
  463. *
  464. * @return DateTime
  465. */
  466. public function calculateDateInstructed(): DateTime
  467. {
  468. return new DateTime();
  469. }
  470. /**
  471. * Set dateDue
  472. *
  473. * @param DateTime|null $dateDue
  474. *
  475. * @return self
  476. */
  477. public function setDateDue(?DateTime $dateDue): self
  478. {
  479. $this->dateDue = $dateDue;
  480. return $this;
  481. }
  482. /**
  483. * Get dateDue
  484. *
  485. * @return DateTime|null
  486. */
  487. public function getDateDue(): ?DateTime
  488. {
  489. return $this->dateDue;
  490. }
  491. /**
  492. * Calculates the dateDue based on the turnAroundTime selected.
  493. * Uses the maximum weeks from the turn-around range + dateInstructed.
  494. *
  495. * @return DateTime|null
  496. */
  497. public function calculateDateDue(): ?DateTime
  498. {
  499. $turnAroundTime = $this->getTurnAroundTime();
  500. if (!$turnAroundTime) {
  501. return null;
  502. }
  503. $weeksMap = [
  504. self::TURN_AROUND_TIME_1_2_WEEKS => 2,
  505. self::TURN_AROUND_TIME_2_4_WEEKS => 4,
  506. self::TURN_AROUND_TIME_4_6_WEEKS => 6,
  507. self::TURN_AROUND_TIME_6_8_WEEKS => 8,
  508. self::TURN_AROUND_TIME_8_12_WEEKS => 12,
  509. self::TURN_AROUND_TIME_12_WEEKS => 14,
  510. ];
  511. $weeks = $weeksMap[$turnAroundTime] ?? null;
  512. if (!$weeks) {
  513. return null;
  514. }
  515. $baseDate = $this->getDateInstructed() ?? new DateTime();
  516. $dueDate = clone $baseDate;
  517. $dueDate->modify(sprintf('+%d weeks', $weeks));
  518. return $dueDate;
  519. }
  520. /**
  521. * Set dateDraftReportReceived
  522. *
  523. * @param DateTime|null $dateDraftReportReceived
  524. *
  525. * @return self
  526. */
  527. public function setDateDraftReportReceived(?DateTime $dateDraftReportReceived): self
  528. {
  529. $this->dateDraftReportReceived = $dateDraftReportReceived;
  530. return $this;
  531. }
  532. /**
  533. * Get dateDraftReportReceived
  534. *
  535. * @return DateTime|null
  536. */
  537. public function getDateDraftReportReceived(): ?DateTime
  538. {
  539. return $this->dateDraftReportReceived;
  540. }
  541. /**
  542. * Set dateFinalReportReceived
  543. *
  544. * @param DateTime|null $dateFinalReportReceived
  545. *
  546. * @return self
  547. */
  548. public function setDateFinalReportReceived(?DateTime $dateFinalReportReceived): self
  549. {
  550. $this->dateFinalReportReceived = $dateFinalReportReceived;
  551. return $this;
  552. }
  553. /**
  554. * Get dateFinalReportReceived
  555. *
  556. * @return DateTime|null
  557. */
  558. public function getDateFinalReportReceived(): ?DateTime
  559. {
  560. return $this->dateFinalReportReceived;
  561. }
  562. /**
  563. * Set dateInvoiceReceived
  564. *
  565. * @param DateTime|null $dateInvoiceReceived
  566. *
  567. * @return self
  568. */
  569. public function setDateInvoiceReceived(?DateTime $dateInvoiceReceived): self
  570. {
  571. $this->dateInvoiceReceived = $dateInvoiceReceived;
  572. return $this;
  573. }
  574. /**
  575. * Get dateInvoiceReceived
  576. *
  577. * @return DateTime|null
  578. */
  579. public function getDateInvoiceReceived(): ?DateTime
  580. {
  581. return $this->dateInvoiceReceived;
  582. }
  583. /**
  584. * Set invoiceReference
  585. *
  586. * @param string|null $invoiceReference
  587. *
  588. * @return self
  589. */
  590. public function setInvoiceReference(?string $invoiceReference): self
  591. {
  592. $this->invoiceReference = $invoiceReference;
  593. return $this;
  594. }
  595. /**
  596. * Get invoiceReference
  597. *
  598. * @return string|null
  599. */
  600. public function getInvoiceReference(): ?string
  601. {
  602. return $this->invoiceReference;
  603. }
  604. /**
  605. * Set hoursInvoiced
  606. *
  607. * @param string|null $hoursInvoiced
  608. *
  609. * @return self
  610. */
  611. public function setHoursInvoiced(?string $hoursInvoiced): self
  612. {
  613. $this->hoursInvoiced = $hoursInvoiced;
  614. return $this;
  615. }
  616. /**
  617. * Get hoursInvoiced
  618. *
  619. * @return string|null
  620. */
  621. public function getHoursInvoiced(): ?string
  622. {
  623. return $this->hoursInvoiced;
  624. }
  625. /**
  626. * Set nettAmount
  627. *
  628. * @param string|null $nettAmount
  629. *
  630. * @return self
  631. */
  632. public function setNettAmount(?string $nettAmount): self
  633. {
  634. $this->nettAmount = $nettAmount;
  635. return $this;
  636. }
  637. /**
  638. * Get nettAmount
  639. *
  640. * @return string|null
  641. */
  642. public function getNettAmount(): ?string
  643. {
  644. return $this->nettAmount;
  645. }
  646. /**
  647. * Set vatPercentage
  648. *
  649. * @param string|null $vatPercentage
  650. *
  651. * @return self
  652. */
  653. public function setVatPercentage(?string $vatPercentage): self
  654. {
  655. $this->vatPercentage = $vatPercentage;
  656. return $this;
  657. }
  658. /**
  659. * Get vatPercentage
  660. *
  661. * @return string|null
  662. */
  663. public function getVatPercentage(): ?string
  664. {
  665. return $this->vatPercentage;
  666. }
  667. /**
  668. * Set totalAmount
  669. *
  670. * @param string|null $totalAmount
  671. *
  672. * @return self
  673. */
  674. public function setTotalAmount(?string $totalAmount): self
  675. {
  676. $this->totalAmount = $totalAmount;
  677. return $this;
  678. }
  679. /**
  680. * Get totalAmount
  681. *
  682. * @return string|null
  683. */
  684. public function getTotalAmount(): ?string
  685. {
  686. return $this->totalAmount;
  687. }
  688. /**
  689. * Set created
  690. *
  691. * @param DateTime $created
  692. *
  693. * @return self
  694. */
  695. public function setCreated(DateTime $created): self
  696. {
  697. $this->created = $created;
  698. return $this;
  699. }
  700. /**
  701. * Get created
  702. *
  703. * @return DateTime
  704. */
  705. public function getCreated(): ?DateTime
  706. {
  707. return $this->created;
  708. }
  709. /**
  710. * Set updated
  711. *
  712. * @param DateTime $updated
  713. *
  714. * @return self
  715. */
  716. public function setUpdated(DateTime $updated): self
  717. {
  718. $this->updated = $updated;
  719. return $this;
  720. }
  721. /**
  722. * Get updated
  723. *
  724. * @return DateTime
  725. */
  726. public function getUpdated(): ?DateTime
  727. {
  728. return $this->updated;
  729. }
  730. /**
  731. * Set deletedAt
  732. *
  733. * @param DateTime|null $deletedAt
  734. *
  735. * @return self
  736. */
  737. public function setDeletedAt(?DateTime $deletedAt): self
  738. {
  739. $this->deletedAt = $deletedAt;
  740. return $this;
  741. }
  742. /**
  743. * Get deletedAt
  744. *
  745. * @return DateTime|null
  746. */
  747. public function getDeletedAt(): ?DateTime
  748. {
  749. return $this->deletedAt;
  750. }
  751. /**
  752. * Set project
  753. *
  754. * @param Project|null $project
  755. *
  756. * @return self
  757. */
  758. public function setProject(?Project $project): self
  759. {
  760. $this->project = $project;
  761. return $this;
  762. }
  763. /**
  764. * Get project
  765. *
  766. * @return Project|null
  767. */
  768. public function getProject(): ?Project
  769. {
  770. return $this->project;
  771. }
  772. /**
  773. * Get details
  774. *
  775. * @return Collection|ExpertReportRequestDetail[]
  776. */
  777. public function getDetails(): Collection
  778. {
  779. return $this->details;
  780. }
  781. /**
  782. * Add detail
  783. *
  784. * @param ExpertReportRequestDetail $detail
  785. *
  786. * @return self
  787. */
  788. public function addDetail(ExpertReportRequestDetail $detail): self
  789. {
  790. if (!$this->details->contains($detail)) {
  791. $this->details[] = $detail;
  792. $detail->setExpertReportRequest($this);
  793. }
  794. return $this;
  795. }
  796. /**
  797. * Remove detail
  798. *
  799. * @param ExpertReportRequestDetail $detail
  800. *
  801. * @return self
  802. */
  803. public function removeDetail(ExpertReportRequestDetail $detail): self
  804. {
  805. $this->details->removeElement($detail);
  806. return $this;
  807. }
  808. /**
  809. * Check if the expert report has any important notes.
  810. *
  811. * @return bool
  812. */
  813. public function getHasImportantNotes(): bool
  814. {
  815. foreach ($this->details as $detail) {
  816. if ($detail->getIsImportant()) {
  817. return true;
  818. }
  819. }
  820. return false;
  821. }
  822. /**
  823. * Get expert display name for templates.
  824. *
  825. * @return string
  826. */
  827. public function getExpertDisplayName(): string
  828. {
  829. return $this->expertName ?? 'Not Set';
  830. }
  831. }