src/Entity/InterpartyDisclosure.php line 16

Open in your IDE?
  1. <?php
  2. namespace MedBrief\MSR\Entity;
  3. use Doctrine\Common\Collections\ArrayCollection;
  4. use Doctrine\Common\Collections\Collection as DoctrineCollection;
  5. use Doctrine\ORM\Mapping as ORM;
  6. use Gedmo\Mapping\Annotation as Gedmo;
  7. use MedBrief\MSR\Repository\InterpartyDisclosureRepository;
  8. use MedBrief\MSR\Traits\FilterableClassConstantsTrait;
  9. use Symfony\Component\Validator\Constraints as Assert;
  10. /**
  11. * @ORM\Entity(repositoryClass=InterpartyDisclosureRepository::class)
  12. */
  13. class InterpartyDisclosure
  14. {
  15. use FilterableClassConstantsTrait;
  16. public const STATUS_DRAFT = 'draft';
  17. public const STATUS_DRAFT__LABEL = 'Draft';
  18. public const STATUS_PENDING = 'pending';
  19. public const STATUS_PENDING__LABEL = 'Pending';
  20. public const STATUS_PROCESSING = 'processing';
  21. public const STATUS_PROCESSING__LABEL = 'Processing';
  22. public const STATUS_FAILED = 'failed';
  23. public const STATUS_FAILED__LABEL = 'Failed';
  24. public const STATUS_ACTIVE = 'active';
  25. public const STATUS_ACTIVE__LABEL = 'Active';
  26. public const STATUS_EXPIRED = 'expired';
  27. public const STATUS_EXPIRED__LABEL = 'Expired';
  28. public const STATUS_REVOKED = 'revoked';
  29. public const STATUS_REVOKED__LABEL = 'Revoked';
  30. public const STATUS_UPDATED = 'updated';
  31. public const STATUS_UPDATED__LABEL = 'Updated';
  32. public const DEFAULT_EXPIRY_DAYS = 30;
  33. public const DEFAULT_EXPIRY_EXTENSION_DAYS = 14;
  34. public const HAS_RADIOLOGY_STUDIES_YES = 'yes';
  35. public const HAS_RADIOLOGY_STUDIES_YES__LABEL = 'Yes';
  36. public const HAS_RADIOLOGY_STUDIES_NO = 'no';
  37. public const HAS_RADIOLOGY_STUDIES_NO__LABEL = 'No';
  38. public const RADIOLOGY_FAILED_DISCS_YES = 'yes';
  39. public const RADIOLOGY_FAILED_DISCS_YES__LABEL = 'Yes';
  40. public const RADIOLOGY_FAILED_DISCS_NO = 'no';
  41. public const RADIOLOGY_FAILED_DISCS_NO__LABEL = 'No';
  42. public const HAS_MEDICAL_RECORDS_YES = 'yes';
  43. public const HAS_MEDICAL_RECORDS_YES__LABEL = 'Yes';
  44. public const HAS_MEDICAL_RECORDS_NO = 'no';
  45. public const HAS_MEDICAL_RECORDS_NO__LABEL = 'No';
  46. /**
  47. * @ORM\Id
  48. *
  49. * @ORM\GeneratedValue
  50. *
  51. * @ORM\Column(type="integer")
  52. */
  53. private ?int $id;
  54. /**
  55. * @ORM\Column(type="string", length=32, options={"default":self::STATUS_ACTIVE})
  56. */
  57. private ?string $status = self::STATUS_ACTIVE;
  58. /**
  59. * @ORM\Column(type="datetime", nullable=true)
  60. */
  61. private ?\DateTime $expiryDate;
  62. /**
  63. * @ORM\Column(type="datetime")
  64. *
  65. * @Gedmo\Timestampable(on="create")
  66. */
  67. private ?\DateTime $created;
  68. /**
  69. * @ORM\Column(type="datetime", nullable=true)
  70. *
  71. * @Gedmo\Timestampable(on="update")
  72. */
  73. private ?\DateTime $updated;
  74. /**
  75. * @ORM\Column(type="datetime", nullable=true)
  76. */
  77. private ?\DateTime $recordsModifiedDate;
  78. /**
  79. * @ORM\ManyToOne(targetEntity=Project::class, inversedBy="interpartyDisclosures")
  80. *
  81. * @ORM\JoinColumn(nullable=false)
  82. */
  83. private ?Project $project;
  84. /**
  85. * @ORM\OneToMany(targetEntity=InterpartyDisclosureRecipientFirm::class, mappedBy="interpartyDisclosure", orphanRemoval=true, cascade={"persist", "remove"})
  86. *
  87. * @Assert\Valid(groups={"third-party"})
  88. *
  89. * @Assert\Count(min=1, minMessage="At least one recipient firm is required.", groups={"third-party"})
  90. */
  91. private ?DoctrineCollection $recipientFirms;
  92. /**
  93. * @ORM\Column(type="datetime", nullable=true)
  94. */
  95. private ?\DateTime $revokeDate;
  96. /**
  97. * @ORM\ManyToOne(targetEntity=User::class)
  98. */
  99. private ?User $createdBy;
  100. /**
  101. * @ORM\ManyToOne(targetEntity=User::class)
  102. */
  103. private ?User $revokedBy;
  104. /**
  105. * @ORM\Column(type="datetime", nullable=true)
  106. */
  107. private ?\DateTime $reactivateDate;
  108. /**
  109. * @ORM\Column(type="json", nullable=true)
  110. */
  111. private $selectedRecordsPayload = [];
  112. /**
  113. * @ORM\Column(type="json", nullable=true)
  114. */
  115. private $recordsSnapshot = [];
  116. /**
  117. * @ORM\OneToMany(targetEntity=InterpartyDisclosureStudy::class, mappedBy="interpartyDisclosure", orphanRemoval=true, cascade={"persist", "remove"})
  118. */
  119. private ?DoctrineCollection $interpartyDisclosureStudies;
  120. /**
  121. * @ORM\OneToMany(targetEntity=InterpartyDisclosureDisc::class, mappedBy="interpartyDisclosure", orphanRemoval=true, cascade={"persist", "remove"})
  122. */
  123. private ?DoctrineCollection $interpartyDisclosureDiscs;
  124. /**
  125. * @ORM\Column(type="integer", options={"default":0})
  126. */
  127. private ?int $medicalRecordsCount = 0;
  128. /**
  129. * @ORM\OneToOne(targetEntity="InterpartyDisclosure", inversedBy="child")
  130. */
  131. private ?InterpartyDisclosure $parent = null;
  132. /**
  133. * @ORM\OneToOne(targetEntity="InterpartyDisclosure", mappedBy="parent")
  134. */
  135. private ?InterpartyDisclosure $child = null;
  136. /**
  137. * Represents a child copy of a disclosure which has not been updated yet.
  138. *
  139. * @ORM\Column(type="boolean", options={"default":false})
  140. */
  141. private ?bool $pureCopy = false;
  142. /**
  143. * @ORM\ManyToOne(targetEntity=Project::class, inversedBy="disclosureSources")
  144. */
  145. private ?Project $disclosureTarget = null;
  146. /**
  147. * @ORM\Column(type="boolean", options={"default":false})
  148. */
  149. private ?bool $savedAsDraft = false;
  150. // Non-mapped property
  151. private bool $isClone = false;
  152. /**
  153. * @ORM\Column(type="boolean", options={"default":false})
  154. */
  155. private ?bool $recordsUpdated = false;
  156. /**
  157. * @ORM\Column(type="boolean", options={"default":false})
  158. */
  159. private ?bool $radiologyUpdated = false;
  160. public function __construct()
  161. {
  162. $this->recipientFirms = new ArrayCollection();
  163. $this->interpartyDisclosureStudies = new ArrayCollection();
  164. $this->interpartyDisclosureDiscs = new ArrayCollection();
  165. }
  166. public function __clone()
  167. {
  168. // Do not clone objects that don't have an identity - Doctrine recommendation.
  169. if ($this->id) {
  170. $this->id = null;
  171. // Mark it as a clone, so we can do some logic against it in the setters e.g. setPureCopy.
  172. $this->isClone = true;
  173. $this->setPureCopy(true);
  174. $this->setSavedAsDraft(false);
  175. $this->setStatus(self::STATUS_DRAFT);
  176. // Auto date fields
  177. $this->setCreatedBy(null);
  178. $this->setUpdated(null);
  179. // Revoked fields
  180. $this->setRevokedBy(null);
  181. $this->setRevokeDate(null);
  182. // Versioning
  183. $this->setParent(null);
  184. $this->setChild(null);
  185. // Other dates
  186. $this->setExpiryDate(null);
  187. $this->setRecordsModifiedDate(null);
  188. $this->setReactivateDate(null);
  189. // Other flags
  190. $this->setRadiologyUpdated(false);
  191. $this->setRecordsUpdated(false);
  192. // Clone firms
  193. $recipientFirms = $this->getRecipientFirms();
  194. $this->recipientFirms = new ArrayCollection();
  195. foreach ($recipientFirms as $recipientFirm) {
  196. $this->addRecipientFirm(clone $recipientFirm);
  197. }
  198. // Clone studies
  199. $interpartyDisclosureStudies = $this->getInterpartyDisclosureStudies();
  200. $this->interpartyDisclosureStudies = new ArrayCollection();
  201. foreach ($interpartyDisclosureStudies as $study) {
  202. $this->addInterpartyDisclosureStudy(clone $study);
  203. }
  204. // Clone discs
  205. $interpartyDisclosureDiscs = $this->getInterpartyDisclosureDiscs();
  206. $this->interpartyDisclosureDiscs = new ArrayCollection();
  207. foreach ($interpartyDisclosureDiscs as $disc) {
  208. $this->addInterpartyDisclosureDisc(clone $disc);
  209. }
  210. }
  211. }
  212. /**
  213. * @return int|null
  214. */
  215. public function getId(): ?int
  216. {
  217. return $this->id;
  218. }
  219. /**
  220. * @return string
  221. */
  222. public function getStatus(): string
  223. {
  224. return $this->status;
  225. }
  226. /**
  227. * @return array
  228. */
  229. public static function getStatusOptions(): array
  230. {
  231. return array_flip(self::getConstantsWithLabelsAsChoices('STATUS'));
  232. }
  233. /**
  234. * @return string
  235. */
  236. public function getStatusLabel(): string
  237. {
  238. $options = self::getStatusOptions();
  239. return $options[$this->getStatus()] ?? '';
  240. }
  241. /**
  242. * @param string $status
  243. *
  244. * @return self
  245. */
  246. public function setStatus(string $status): self
  247. {
  248. // if status is changing to expired, clear the recordsUpdates and radiologyUpdated fields
  249. if ($status === self::STATUS_EXPIRED) {
  250. $this->setRecordsUpdated(false);
  251. $this->setRadiologyUpdated(false);
  252. }
  253. $this->status = $status;
  254. return $this;
  255. }
  256. /**
  257. * @return bool
  258. */
  259. public function isStatusDraft(): bool
  260. {
  261. return $this->getStatus() === self::STATUS_DRAFT;
  262. }
  263. /**
  264. * @return bool
  265. */
  266. public function isStatusPending(): bool
  267. {
  268. return $this->getStatus() === self::STATUS_PENDING;
  269. }
  270. /**
  271. * @return bool
  272. */
  273. public function isStatusProcessing(): bool
  274. {
  275. return $this->getStatus() === self::STATUS_PROCESSING;
  276. }
  277. /**
  278. * @return bool
  279. */
  280. public function isStatusFailed(): bool
  281. {
  282. return $this->getStatus() === self::STATUS_FAILED;
  283. }
  284. /**
  285. * @return bool
  286. */
  287. public function isStatusActive(): bool
  288. {
  289. return $this->getStatus() === self::STATUS_ACTIVE;
  290. }
  291. /**
  292. * @return bool
  293. */
  294. public function isStatusRevoked(): bool
  295. {
  296. return $this->getStatus() === self::STATUS_REVOKED;
  297. }
  298. /**
  299. * @return bool
  300. */
  301. public function isStatusUpdated(): bool
  302. {
  303. return $this->getStatus() === self::STATUS_UPDATED;
  304. }
  305. /**
  306. * @return bool
  307. */
  308. public function isStatusExpired(): bool
  309. {
  310. return $this->getStatus() === self::STATUS_EXPIRED;
  311. }
  312. /**
  313. * @return \DateTime|null
  314. */
  315. public function getExpiryDate(): ?\DateTime
  316. {
  317. return $this->expiryDate;
  318. }
  319. /**
  320. * @param \DateTime|null $expiryDate
  321. *
  322. * @return self
  323. */
  324. public function setExpiryDate(?\DateTime $expiryDate): self
  325. {
  326. $this->expiryDate = $expiryDate;
  327. return $this;
  328. }
  329. /**
  330. * NOTE: Extension is based only on the parent expiry date.
  331. * Extends the expiry date if the remaining days is less than our extension period.
  332. *
  333. * @return self
  334. */
  335. public function extendExpiryDateIfDue(): self
  336. {
  337. if ($this->getParent() === null) {
  338. throw new \InvalidArgumentException('A parent disclosure is expected to extend the expiry date.');
  339. }
  340. $parentExpiryDate = clone $this->getParent()->getExpiryDate();
  341. // If the parent disclosure was set to expire in less than the extension days, extend it further by the extension days.
  342. if ($this->getParent()->getExpiryDate() < new \DateTime(sprintf('+%1$s days', self::DEFAULT_EXPIRY_EXTENSION_DAYS))) {
  343. $this->setExpiryDate($parentExpiryDate->modify(sprintf('+%1$s days', self::DEFAULT_EXPIRY_EXTENSION_DAYS)));
  344. } else {
  345. // Else, give it the original expiry date of the parent.
  346. $this->setExpiryDate($parentExpiryDate);
  347. }
  348. return $this;
  349. }
  350. /**
  351. * Calculates and sets the expiry date.
  352. *
  353. * Important: should be called before any status change of the disclosure.
  354. *
  355. * @return self
  356. */
  357. public function calculateExpiryDate(): self
  358. {
  359. if ($this->getParent() === null || $this->isStatusExpired() === true || $this->isParentExpired()) {
  360. // This is the original disclosure, or the disclosure is expired and we're not updating, give it a normal expiry date.
  361. $this->setExpiryDate(new \DateTime(sprintf('+%1$s days', self::DEFAULT_EXPIRY_DAYS)));
  362. } else {
  363. // else, we're simply updating an existing disclosure and need to extend with the expiry date
  364. // using some additional logic.
  365. $this->extendExpiryDateIfDue();
  366. }
  367. return $this;
  368. }
  369. /**
  370. * @return \DateTimeInterface|null
  371. */
  372. public function getCreated(): ?\DateTimeInterface
  373. {
  374. return $this->created;
  375. }
  376. /**
  377. * @param \DateTimeInterface $created
  378. *
  379. * @return self
  380. */
  381. public function setCreated(\DateTimeInterface $created): self
  382. {
  383. $this->created = $created;
  384. return $this;
  385. }
  386. /**
  387. * @return \DateTimeInterface|null
  388. */
  389. public function getUpdated(): ?\DateTimeInterface
  390. {
  391. return $this->updated;
  392. }
  393. /**
  394. * @param \DateTimeInterface|null $updated
  395. *
  396. * @return self
  397. */
  398. public function setUpdated(?\DateTimeInterface $updated): self
  399. {
  400. $this->updated = $updated;
  401. return $this;
  402. }
  403. /**
  404. * @return Project|null
  405. */
  406. public function getProject(): ?Project
  407. {
  408. return $this->project;
  409. }
  410. /**
  411. * @param Project|null $project
  412. *
  413. * @return self
  414. */
  415. public function setProject(?Project $project): self
  416. {
  417. $this->project = $project;
  418. return $this;
  419. }
  420. /**
  421. * @return DoctrineCollection<int, InterpartyDisclosureRecipientFirm>
  422. */
  423. public function getRecipientFirms(): DoctrineCollection
  424. {
  425. return $this->recipientFirms;
  426. }
  427. /**
  428. * Returns the recipient firms reference number - limited to the first recipient firm.
  429. *
  430. * @return string
  431. */
  432. public function getRecipientFirmReference(): string
  433. {
  434. return $this->getRecipientFirms()->count() > 0 ? $this->getRecipientFirms()->first()->getMatterNumber() : '';
  435. }
  436. /**
  437. * Retrieve a string of Recipient Firm name/s.
  438. * If there are multiple Firms then prefixes each name with a bracketed number.
  439. *
  440. * @return string
  441. */
  442. public function getRecipientFirmsString(): string
  443. {
  444. $firmsArray = $this->getRecipientFirms()->map(function (InterpartyDisclosureRecipientFirm $recipientFirm) {
  445. return $recipientFirm->getName();
  446. })->toArray();
  447. if (count($firmsArray) === 1) {
  448. return $firmsArray[0];
  449. }
  450. $firmsStr = implode(' ', array_map(function (string $currentItem, $currentKey) {
  451. return sprintf('(%1$s) %2$s', $currentKey + 1, $currentItem);
  452. }, $firmsArray, array_keys($firmsArray)));
  453. return $firmsStr;
  454. }
  455. /**
  456. * Retrieve a string of Recipient Firm name/s postfixed with bracketed office field if not empty.
  457. * If there are multiple Firms then prefixes each name with a bracketed number.
  458. *
  459. * @return string
  460. */
  461. public function getRecipientFirmsWithOfficeString(): string
  462. {
  463. $firmsArray = $this->getRecipientFirms()->map(function (InterpartyDisclosureRecipientFirm $recipientFirm) {
  464. $office = is_null($recipientFirm->getOffice()) ? '' : ' (' . $recipientFirm->getOffice() . ')';
  465. return $recipientFirm->getName() . $office;
  466. })->toArray();
  467. if (count($firmsArray) === 1) {
  468. return $firmsArray[0];
  469. }
  470. $firmsStr = implode(' ', array_map(function (string $currentItem, $currentKey) {
  471. return sprintf('(%1$s) %2$s', $currentKey + 1, $currentItem);
  472. }, $firmsArray, array_keys($firmsArray)));
  473. return $firmsStr;
  474. }
  475. /**
  476. * Retrieves all recipient contacts belonging to a disclosure
  477. *
  478. * @return array
  479. */
  480. public function getAllInterpartyDisclosureContacts(): array
  481. {
  482. if ($this->getRecipientFirms()->count() === 0) {
  483. return [];
  484. }
  485. $allContacts = [];
  486. foreach ($this->getRecipientFirms() as $firm) {
  487. foreach ($firm->getRecipientContacts() as $contact) {
  488. $allContacts[] = $contact;
  489. }
  490. }
  491. return $allContacts;
  492. }
  493. /**
  494. * @param InterpartyDisclosureRecipientFirm $recipientFirm
  495. *
  496. * @return self
  497. */
  498. public function addRecipientFirm(InterpartyDisclosureRecipientFirm $recipientFirm): self
  499. {
  500. if (!$this->recipientFirms->contains($recipientFirm)) {
  501. $this->recipientFirms[] = $recipientFirm;
  502. $recipientFirm->setInterpartyDisclosure($this);
  503. }
  504. return $this;
  505. }
  506. /**
  507. * @param InterpartyDisclosureRecipientFirm $recipientFirm
  508. *
  509. * @return self
  510. */
  511. public function removeRecipientFirm(InterpartyDisclosureRecipientFirm $recipientFirm): self
  512. {
  513. if ($this->recipientFirms->removeElement($recipientFirm)) {
  514. // set the owning side to null (unless already changed)
  515. if ($recipientFirm->getInterpartyDisclosure() === $this) {
  516. $recipientFirm->setInterpartyDisclosure(null);
  517. }
  518. }
  519. return $this;
  520. }
  521. /**
  522. * @return \DateTimeInterface|null
  523. */
  524. public function getRevokeDate(): ?\DateTimeInterface
  525. {
  526. return $this->revokeDate;
  527. }
  528. /**
  529. * @param \DateTimeInterface|null $revokeDate
  530. *
  531. * @return self
  532. */
  533. public function setRevokeDate(?\DateTimeInterface $revokeDate): self
  534. {
  535. $this->revokeDate = $revokeDate;
  536. return $this;
  537. }
  538. /**
  539. * @return \DateTimeInterface|null
  540. */
  541. public function getReactivateDate(): ?\DateTimeInterface
  542. {
  543. return $this->reactivateDate;
  544. }
  545. /**
  546. * @param \DateTimeInterface|null $reactivateDate
  547. *
  548. * @return self
  549. */
  550. public function setReactivateDate(?\DateTimeInterface $reactivateDate): self
  551. {
  552. $this->reactivateDate = $reactivateDate;
  553. return $this;
  554. }
  555. /**
  556. * @return User|null
  557. */
  558. public function getRevokedBy(): ?User
  559. {
  560. return $this->revokedBy;
  561. }
  562. /**
  563. * @param User|null $revokedBy
  564. *
  565. * @return self
  566. */
  567. public function setRevokedBy(?User $revokedBy): self
  568. {
  569. $this->revokedBy = $revokedBy;
  570. return $this;
  571. }
  572. /**
  573. * @return DoctrineCollection<int, InterpartyDisclosureStudy>
  574. */
  575. public function getInterpartyDisclosureStudies(): DoctrineCollection
  576. {
  577. return $this->interpartyDisclosureStudies;
  578. }
  579. /**
  580. * @param InterpartyDisclosureStudy $interpartyDisclosureStudy
  581. *
  582. * @return self
  583. */
  584. public function addInterpartyDisclosureStudy(InterpartyDisclosureStudy $interpartyDisclosureStudy): self
  585. {
  586. if (!$this->interpartyDisclosureStudies->contains($interpartyDisclosureStudy)) {
  587. $this->interpartyDisclosureStudies[] = $interpartyDisclosureStudy;
  588. $interpartyDisclosureStudy->setInterpartyDisclosure($this);
  589. }
  590. return $this;
  591. }
  592. /**
  593. * @param InterpartyDisclosureStudy $interpartyDisclosureStudy
  594. *
  595. * @return self
  596. */
  597. public function removeInterpartyDisclosureStudy(InterpartyDisclosureStudy $interpartyDisclosureStudy): self
  598. {
  599. if ($this->interpartyDisclosureStudies->removeElement($interpartyDisclosureStudy)) {
  600. // set the owning side to null (unless already changed)
  601. if ($interpartyDisclosureStudy->getInterpartyDisclosure() === $this) {
  602. $interpartyDisclosureStudy->setInterpartyDisclosure(null);
  603. }
  604. }
  605. return $this;
  606. }
  607. /**
  608. * @return DoctrineCollection<int, Study>
  609. */
  610. public function getStudies(): DoctrineCollection
  611. {
  612. return $this->getInterpartyDisclosureStudies()->map(function (InterpartyDisclosureStudy $disclosureStudy) {
  613. return $disclosureStudy->getStudy();
  614. });
  615. }
  616. /**
  617. * @param Study $study
  618. *
  619. * @return self
  620. */
  621. public function addStudy(Study $study): self
  622. {
  623. $disclosureStudy = new InterpartyDisclosureStudy();
  624. $disclosureStudy->setStudy($study);
  625. $discs = $study->getDiscs();
  626. $disclosureDiscs = $discs->map(function ($disc) {
  627. return [
  628. 'name' => $disc->getName(),
  629. 'id' => $disc->getId(),
  630. ];
  631. });
  632. $disclosureStudy->setPayload(
  633. [
  634. 'date' => $study->getStudyDate(),
  635. 'description' => $study->getStudyDescription(),
  636. 'discs' => $disclosureDiscs,
  637. ]
  638. );
  639. $this->addInterpartyDisclosureStudy($disclosureStudy);
  640. return $this;
  641. }
  642. /**
  643. * @param Study $study
  644. *
  645. * @return self
  646. */
  647. public function removeStudy(Study $study): self
  648. {
  649. foreach ($this->getInterpartyDisclosureStudies() as $disclosureStudy) {
  650. if ($disclosureStudy->getStudy() === $study) {
  651. $this->removeInterpartyDisclosureStudy($disclosureStudy);
  652. }
  653. }
  654. return $this;
  655. }
  656. /**
  657. * @return DoctrineCollection<int, InterpartyDisclosureDisc>
  658. */
  659. public function getInterpartyDisclosureDiscs(): DoctrineCollection
  660. {
  661. return $this->interpartyDisclosureDiscs;
  662. }
  663. /**
  664. * @param InterpartyDisclosureDisc $interpartyDisclosureDisc
  665. *
  666. * @return self
  667. */
  668. public function addInterpartyDisclosureDisc(?InterpartyDisclosureDisc $interpartyDisclosureDisc): self
  669. {
  670. if (!$this->interpartyDisclosureDiscs->contains($interpartyDisclosureDisc)) {
  671. $this->interpartyDisclosureDiscs[] = $interpartyDisclosureDisc;
  672. $interpartyDisclosureDisc->setInterpartyDisclosure($this);
  673. }
  674. return $this;
  675. }
  676. /**
  677. * @param InterpartyDisclosureDisc $interpartyDisclosureDisc
  678. *
  679. * @return self
  680. */
  681. public function removeInterpartyDisclosureDisc(InterpartyDisclosureDisc $interpartyDisclosureDisc): self
  682. {
  683. if ($this->interpartyDisclosureDiscs->removeElement($interpartyDisclosureDisc)) {
  684. // set the owning side to null (unless already changed)
  685. if ($interpartyDisclosureDisc->getInterpartyDisclosure() === $this) {
  686. $interpartyDisclosureDisc->setInterpartyDisclosure(null);
  687. }
  688. }
  689. return $this;
  690. }
  691. /**
  692. * @return DoctrineCollection<int, Disc>
  693. */
  694. public function getDiscs(): DoctrineCollection
  695. {
  696. return $this->getInterpartyDisclosureDiscs()->map(function (InterpartyDisclosureDisc $disclosureDisc) {
  697. return $disclosureDisc->getDisc();
  698. });
  699. }
  700. /**
  701. * @param Disc $disc
  702. *
  703. * @return self
  704. */
  705. public function addDisc(Disc $disc): self
  706. {
  707. $disclosureDisc = new InterpartyDisclosureDisc();
  708. $disclosureDisc->setDisc($disc);
  709. $disclosureDisc->setPayload(
  710. [
  711. 'id' => $disc->getId(),
  712. 'name' => $disc->getName(),
  713. ]
  714. );
  715. $this->addInterpartyDisclosureDisc($disclosureDisc);
  716. return $this;
  717. }
  718. /**
  719. * @param Disc $disc
  720. *
  721. * @return self
  722. */
  723. public function removeDisc(Disc $disc): self
  724. {
  725. foreach ($this->getInterpartyDisclosureDiscs() as $disclosureDisc) {
  726. if ($disclosureDisc->getDisc() === $disc) {
  727. $this->removeInterpartyDisclosureDisc($disclosureDisc);
  728. }
  729. }
  730. return $this;
  731. }
  732. /**
  733. * @return array|null
  734. */
  735. public function getSelectedRecordsPayload(): ?array
  736. {
  737. return $this->selectedRecordsPayload;
  738. }
  739. /**
  740. * @param array|null $selectedRecordsPayload
  741. *
  742. * @return self
  743. */
  744. public function setSelectedRecordsPayload(?array $selectedRecordsPayload): self
  745. {
  746. $this->selectedRecordsPayload = $selectedRecordsPayload;
  747. if ($selectedRecordsPayload === null) {
  748. $this->setMedicalRecordsCount(0);
  749. } else {
  750. $this->setMedicalRecordsCount(count($selectedRecordsPayload));
  751. }
  752. return $this;
  753. }
  754. /**
  755. * @return User|null
  756. */
  757. public function getCreatedBy(): ?User
  758. {
  759. return $this->createdBy;
  760. }
  761. /**
  762. * @param User|null $createdBy
  763. *
  764. * @return self
  765. */
  766. public function setCreatedBy(?User $createdBy): self
  767. {
  768. $this->createdBy = $createdBy;
  769. return $this;
  770. }
  771. /**
  772. * Counts the number of third parties, i.e. recipient firm contacts that have
  773. * access to the interparty disclosure
  774. *
  775. * @return int|null
  776. */
  777. public function getContactPersonCount(): ?int
  778. {
  779. $count = 0;
  780. foreach ($this->getRecipientFirms() as $firm) {
  781. $count += $firm->getRecipientContacts()->count();
  782. }
  783. return $count;
  784. }
  785. /**
  786. * Counts the number of radiology studies that an interparty disclosure has associated
  787. * with it.
  788. *
  789. * @return int|null
  790. */
  791. public function getRadiologyStudyCount(): ?int
  792. {
  793. return $this->getInterpartyDisclosureStudies()->count();
  794. }
  795. /**
  796. * @return array
  797. */
  798. public static function getHasRadiologyStudiesOptions(): array
  799. {
  800. return array_flip(self::getConstantsWithLabelsAsChoices('HAS_RADIOLOGY_STUDIES'));
  801. }
  802. /**
  803. * Counts the number of failed radiology discs that an interparty disclosure
  804. * has associated with it.
  805. *
  806. * @return int|null
  807. */
  808. public function getRadiologyFailedDiscsCount(): ?int
  809. {
  810. return $this->getInterpartyDisclosureDiscs()->count();
  811. }
  812. /**
  813. * @return array
  814. */
  815. public static function getRadiologyFailedDiscsOptions(): array
  816. {
  817. return array_flip(self::getConstantsWithLabelsAsChoices('RADIOLOGY_FAILED_DISCS'));
  818. }
  819. /**
  820. * @return int
  821. */
  822. public function getRadiologyItemTotalCount(): int
  823. {
  824. return $this->getRadiologyStudyCount() + $this->getRadiologyFailedDiscsCount();
  825. }
  826. /**
  827. * @return string|null
  828. */
  829. public function getThirdPartyContacts(): ?string
  830. {
  831. $contactDetails = [];
  832. foreach ($this->getRecipientFirms() as $firm) {
  833. foreach ($firm->getRecipientContacts() as $contact) {
  834. $contactDetails[] = $contact;
  835. }
  836. }
  837. $contactNames = array_map(function ($contact) {
  838. return $contact->getFullName();
  839. }, $contactDetails);
  840. return implode(',', $contactNames);
  841. }
  842. /**
  843. * @return self|null
  844. */
  845. public function getParent(): ?self
  846. {
  847. return $this->parent;
  848. }
  849. /**
  850. * @param self|null $parent
  851. *
  852. * @return self
  853. */
  854. public function setParent(?self $parent): self
  855. {
  856. $this->parent = $parent;
  857. return $this;
  858. }
  859. /**
  860. * @return self|null
  861. */
  862. public function getChild(): ?self
  863. {
  864. return $this->child;
  865. }
  866. /**
  867. * @param self|null $child
  868. *
  869. * @return self
  870. */
  871. public function setChild(?self $child): self
  872. {
  873. // unset the owning side of the relation if necessary
  874. if ($child === null && $this->child !== null) {
  875. $this->child->setParent(null);
  876. }
  877. // set the owning side of the relation if necessary
  878. if ($child !== null && $child->getParent() !== $this) {
  879. $child->setParent($this);
  880. }
  881. $this->child = $child;
  882. return $this;
  883. }
  884. /**
  885. * @return InterpartyDisclosure[]
  886. */
  887. public function getPreviousVersions(): array
  888. {
  889. $currentElement = $this;
  890. $previousVersions = [];
  891. do {
  892. if ($currentElement->getParent() !== null) {
  893. $previousVersions[] = $currentElement->getParent();
  894. }
  895. $currentElement = $currentElement->getParent();
  896. } while ($currentElement !== null);
  897. return $previousVersions;
  898. }
  899. /**
  900. * Returns true if the current disclosure is a previous disclosure of the disclosure passed in as an argument.
  901. *
  902. * @param InterpartyDisclosure $latestDisclosure
  903. *
  904. * @return bool
  905. */
  906. public function isDisclosurePreviousVersionOf(InterpartyDisclosure $latestDisclosure): bool
  907. {
  908. return in_array($this, $latestDisclosure->getPreviousVersions());
  909. }
  910. /**
  911. * @return bool
  912. */
  913. public function isChildDraft(): bool
  914. {
  915. return $this->getParent() instanceof self && $this->isStatusDraft();
  916. }
  917. /**
  918. * @return bool
  919. */
  920. public function isParentExpired(): bool
  921. {
  922. return $this->getParent() !== null && $this->getParent()->getExpiryDate() <= new \DateTime();
  923. }
  924. /**
  925. * Returns true if the disclosure chain should be considered expired.
  926. *
  927. * @return bool
  928. */
  929. public function isDisclosureChainExpired(): bool
  930. {
  931. return $this->isStatusExpired() || $this->isParentExpired();
  932. }
  933. /**
  934. * @return bool
  935. */
  936. public function hasChildDraft(): bool
  937. {
  938. return $this->getChild() instanceof self && $this->getChild()->isStatusDraft();
  939. }
  940. /**
  941. * @return bool
  942. */
  943. public function canEdit(): bool
  944. {
  945. $allowedStatuses = [
  946. self::STATUS_ACTIVE,
  947. self::STATUS_DRAFT,
  948. ];
  949. return in_array($this->getStatus(), $allowedStatuses);
  950. }
  951. /**
  952. * Returns the ID of the entity that should be used for editing.
  953. *
  954. * @return int|null
  955. */
  956. public function getEditId(): ?int
  957. {
  958. if ($this->hasChildDraft()) {
  959. return $this->getChild()->getId();
  960. }
  961. return $this->getId();
  962. }
  963. /**
  964. * @return bool|null
  965. */
  966. public function getPureCopy(): ?bool
  967. {
  968. return $this->pureCopy;
  969. }
  970. /**
  971. * @param bool|null $pureCopy
  972. *
  973. * @return self
  974. */
  975. public function setPureCopy(?bool $pureCopy): self
  976. {
  977. if ($pureCopy === true && $this->isChildDraft() === false && $this->isClone === false) {
  978. throw new \InvalidArgumentException('Only child drafts can be marked as pure copies.');
  979. }
  980. $this->pureCopy = $pureCopy;
  981. return $this;
  982. }
  983. /**
  984. * @return int|null
  985. */
  986. public function getMedicalRecordsCount(): ?int
  987. {
  988. return $this->medicalRecordsCount;
  989. }
  990. /**
  991. * @param int|null $medicalRecordsCount
  992. *
  993. * @return self
  994. */
  995. public function setMedicalRecordsCount(?int $medicalRecordsCount): self
  996. {
  997. $this->medicalRecordsCount = $medicalRecordsCount;
  998. return $this;
  999. }
  1000. /**
  1001. * @return array
  1002. */
  1003. public static function getHasMedicalRecordsOptions(): array
  1004. {
  1005. return array_flip(self::getConstantsWithLabelsAsChoices('HAS_MEDICAL_RECORDS'));
  1006. }
  1007. /**
  1008. * Returns true if the disclosure is the current head of the chain.
  1009. * Child drafts are ignored.
  1010. *
  1011. * @return bool
  1012. */
  1013. public function isCurrentHeadOfChain(): bool
  1014. {
  1015. return $this->getChild() === null || $this->hasChildDraft() === true;
  1016. }
  1017. /**
  1018. * @return Project|null
  1019. */
  1020. public function getDisclosureTarget(): ?Project
  1021. {
  1022. return $this->disclosureTarget;
  1023. }
  1024. /**
  1025. * @param Project|null $disclosureTarget
  1026. *
  1027. * @return self
  1028. */
  1029. public function setDisclosureTarget(?Project $disclosureTarget): self
  1030. {
  1031. $this->disclosureTarget = $disclosureTarget;
  1032. return $this;
  1033. }
  1034. /**
  1035. * @return array|null
  1036. */
  1037. public function getRecordsSnapshot(): ?array
  1038. {
  1039. return $this->recordsSnapshot;
  1040. }
  1041. /**
  1042. * @param array|null $recordsSnapshot
  1043. *
  1044. * @return self
  1045. */
  1046. public function setRecordsSnapshot(?array $recordsSnapshot): self
  1047. {
  1048. $this->recordsSnapshot = $recordsSnapshot;
  1049. return $this;
  1050. }
  1051. /**
  1052. * @return \DateTimeInterface|null
  1053. *
  1054. */
  1055. public function getRecordsModifiedDate(): ?\DateTimeInterface
  1056. {
  1057. return $this->recordsModifiedDate;
  1058. }
  1059. /**
  1060. * @param \DateTimeInterface|null $recordsModifiedDate
  1061. *
  1062. * @return self
  1063. *
  1064. */
  1065. public function setRecordsModifiedDate(?\DateTimeInterface $recordsModifiedDate): self
  1066. {
  1067. $this->recordsModifiedDate = $recordsModifiedDate;
  1068. return $this;
  1069. }
  1070. /**
  1071. * Determines the contacts removed from the child that were in the parent.
  1072. *
  1073. * @return InterpartyDisclosureRecipientContact[]
  1074. */
  1075. public function getContactsRemoved(): array
  1076. {
  1077. // No parent? No contacts removed
  1078. if ($this->getParent() === null) {
  1079. return [];
  1080. }
  1081. // Start by mapping the previous set of contacts to their emails.
  1082. $prevContacts = array_map(function (InterpartyDisclosureRecipientContact $contact) {
  1083. return $contact->getEmail();
  1084. }, $this->getParent()->getAllInterpartyDisclosureContacts());
  1085. $currContacts = array_map(function (InterpartyDisclosureRecipientContact $contact) {
  1086. return $contact->getEmail();
  1087. }, $this->getAllInterpartyDisclosureContacts());
  1088. // Get the elements that are in prevContacts, but not in currContacts
  1089. $removedContacts = array_diff($prevContacts, $currContacts);
  1090. // Nothing, return empty array
  1091. if (empty($removedContacts)) {
  1092. return [];
  1093. }
  1094. // Turn them back into entities and return
  1095. return array_filter($this->getParent()->getAllInterpartyDisclosureContacts(), function (InterpartyDisclosureRecipientContact $contact) use ($removedContacts) {
  1096. return in_array($contact->getEmail(), $removedContacts) === true;
  1097. });
  1098. }
  1099. /**
  1100. * @return bool|null
  1101. */
  1102. public function getSavedAsDraft(): ?bool
  1103. {
  1104. return $this->savedAsDraft;
  1105. }
  1106. /**
  1107. * @param bool $savedAsDraft
  1108. *
  1109. * @return self
  1110. */
  1111. public function setSavedAsDraft(bool $savedAsDraft): self
  1112. {
  1113. $this->savedAsDraft = $savedAsDraft;
  1114. return $this;
  1115. }
  1116. /**
  1117. * Checks if passed-in disclosure is a child and that it has no child draft, then it is the most recent disclosure.
  1118. *
  1119. * @return bool
  1120. */
  1121. public function isDisclosurePreviousVersion(): bool
  1122. {
  1123. return $this->getChild() !== null && $this->hasChildDraft() === false;
  1124. }
  1125. /**
  1126. * @return bool|null
  1127. */
  1128. public function isRecordsUpdated(): ?bool
  1129. {
  1130. return $this->recordsUpdated;
  1131. }
  1132. /**
  1133. * @param bool $recordsUpdated
  1134. *
  1135. * @return self
  1136. */
  1137. public function setRecordsUpdated(bool $recordsUpdated): self
  1138. {
  1139. $this->recordsUpdated = $recordsUpdated;
  1140. return $this;
  1141. }
  1142. /**
  1143. * @return bool|null
  1144. */
  1145. public function isRadiologyUpdated(): ?bool
  1146. {
  1147. return $this->radiologyUpdated;
  1148. }
  1149. /**
  1150. * @param bool $recordsUpdated
  1151. *
  1152. * @return self
  1153. */
  1154. public function setRadiologyUpdated(bool $recordsUpdated): self
  1155. {
  1156. $this->radiologyUpdated = $recordsUpdated;
  1157. return $this;
  1158. }
  1159. }