''"})} * ) * @ORM\Entity * @Serializer\DiscriminatorMap(typeProperty="type", mapping={ * "chill_calendar_calendar": Calendar::class * }) */ class Calendar implements TrackCreationInterface, TrackUpdateInterface, HasCentersInterface { use RemoteCalendarTrait; use TrackCreationTrait; use TrackUpdateTrait; public const SMS_CANCEL_PENDING = 'sms_cancel_pending'; public const SMS_PENDING = 'sms_pending'; public const SMS_SENT = 'sms_sent'; public const STATUS_CANCELED = 'canceled'; /** * @deprecated */ public const STATUS_MOVED = 'moved'; public const STATUS_VALID = 'valid'; /** * a list of invite which have been added during this session. * * @var array|Invite[] */ public array $newInvites = []; /** * a list of invite which have been removed during this session. * * @var array|Invite[] */ public array $oldInvites = []; public ?CalendarRange $previousCalendarRange = null; public ?User $previousMainUser = null; /** * @ORM\ManyToOne(targetEntity="Chill\PersonBundle\Entity\AccompanyingPeriod", inversedBy="calendars") */ private ?AccompanyingPeriod $accompanyingPeriod = null; /** * @ORM\ManyToOne(targetEntity="Chill\ActivityBundle\Entity\Activity") */ private ?Activity $activity = null; /** * @ORM\OneToOne(targetEntity="CalendarRange", inversedBy="calendar") * @Serializer\Groups({"calendar:read", "read"}) */ private ?CalendarRange $calendarRange = null; /** * @ORM\ManyToOne(targetEntity="CancelReason") */ private ?CancelReason $cancelReason = null; /** * @ORM\Embedded(class=CommentEmbeddable::class, columnPrefix="comment_") * @Serializer\Groups({"calendar:read", "read", "docgen:read"}) */ private CommentEmbeddable $comment; /** * @ORM\Column(type="integer", nullable=false, options={"default": 0}) */ private int $dateTimeVersion = 0; /** * @var Collection * @ORM\OneToMany(targetEntity=CalendarDoc::class, mappedBy="calendar", orphanRemoval=true) */ private Collection $documents; /** * @ORM\Column(type="datetime_immutable", nullable=false) * @Serializer\Groups({"calendar:read", "read", "calendar:light", "docgen:read"}) * @Assert\NotNull(message="calendar.An end date is required") */ private ?DateTimeImmutable $endDate = null; /** * @ORM\Id * @ORM\GeneratedValue * @ORM\Column(type="integer") * @Serializer\Groups({"calendar:read", "read", "calendar:light", "docgen:read"}) */ private ?int $id = null; /** * @ORM\OneToMany( * targetEntity=Invite::class, * mappedBy="calendar", * orphanRemoval=true, * cascade={"persist", "remove", "merge", "detach"} * ) * @ORM\JoinTable(name="chill_calendar.calendar_to_invites") * @Serializer\Groups({"read", "docgen:read"}) */ private Collection $invites; /** * @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\Location") * @Serializer\Groups({"read", "docgen:read"}) * @Assert\NotNull(message="calendar.A location is required") */ private ?Location $location = null; /** * @ORM\ManyToOne(targetEntity="Chill\MainBundle\Entity\User") * @Serializer\Groups({"calendar:read", "read", "calendar:light", "docgen:read"}) * @Serializer\Context(normalizationContext={"read"}, groups={"calendar:light"}) * @Assert\NotNull(message="calendar.A main user is mandatory") */ private ?User $mainUser = null; /** * @ORM\ManyToOne(targetEntity=Person::class) * @ORM\JoinColumn(nullable=true) */ private ?Person $person = null; /** * @ORM\ManyToMany(targetEntity="Chill\PersonBundle\Entity\Person", inversedBy="calendars") * @ORM\JoinTable(name="chill_calendar.calendar_to_persons") * @Serializer\Groups({"calendar:read", "read", "calendar:light", "docgen:read"}) * @Serializer\Context(normalizationContext={"read"}, groups={"calendar:light"}) * @Assert\Count(min=1, minMessage="calendar.At least {{ limit }} person is required.") */ private Collection $persons; /** * @ORM\Embedded(class=PrivateCommentEmbeddable::class, columnPrefix="privateComment_") * @Serializer\Groups({"calendar:read"}) */ private PrivateCommentEmbeddable $privateComment; /** * @ORM\ManyToMany(targetEntity="Chill\ThirdPartyBundle\Entity\ThirdParty") * @ORM\JoinTable(name="chill_calendar.calendar_to_thirdparties") * @Serializer\Groups({"calendar:read", "read", "calendar:light", "docgen:read"}) * @Serializer\Context(normalizationContext={"read"}, groups={"calendar:light"}) */ private Collection $professionals; /** * @ORM\Column(type="boolean", nullable=true) * @Serializer\Groups({"docgen:read"}) */ private ?bool $sendSMS = false; /** * @ORM\Column(type="text", nullable=false, options={"default": Calendar::SMS_PENDING}) */ private string $smsStatus = self::SMS_PENDING; /** * @ORM\Column(type="datetime_immutable", nullable=false) * @Serializer\Groups({"calendar:read", "read", "calendar:light", "docgen:read"}) * @Serializer\Context(normalizationContext={"read"}, groups={"calendar:light"}) * @Assert\NotNull(message="calendar.A start date is required") */ private ?DateTimeImmutable $startDate = null; /** * @ORM\Column(type="string", length=255, nullable=false, options={"default": "valid"}) * @Serializer\Groups({"calendar:read", "read", "calendar:light"}) * @Serializer\Context(normalizationContext={"read"}, groups={"calendar:light"}) */ private string $status = self::STATUS_VALID; /** * @ORM\Column(type="boolean", nullable=true) * @Serializer\Groups({"docgen:read"}) */ private ?bool $urgent = false; public function __construct() { $this->comment = new CommentEmbeddable(); $this->documents = new ArrayCollection(); $this->privateComment = new PrivateCommentEmbeddable(); $this->persons = new ArrayCollection(); $this->professionals = new ArrayCollection(); $this->invites = new ArrayCollection(); } /** * @internal use @{CalendarDoc::__construct} instead */ public function addDocument(CalendarDoc $calendarDoc): self { if ($this->documents->contains($calendarDoc)) { $this->documents[] = $calendarDoc; } return $this; } /** * @internal Use {@link (Calendar::addUser)} instead */ public function addInvite(Invite $invite): self { if ($invite->getCalendar() instanceof Calendar && $invite->getCalendar() !== $this) { throw new LogicException('Not allowed to move an invitation to another Calendar'); } $this->invites[] = $invite; $this->newInvites[] = $invite; $invite->setCalendar($this); return $this; } public function addPerson(Person $person): self { $this->persons[] = $person; return $this; } public function addProfessional(ThirdParty $professional): self { $this->professionals[] = $professional; return $this; } public function addUser(User $user): self { if (!$this->getUsers()->contains($user) && $this->getMainUser() !== $user) { $this->addInvite((new Invite())->setUser($user)); } return $this; } public function getAccompanyingPeriod(): ?AccompanyingPeriod { return $this->accompanyingPeriod; } public function getActivity(): ?Activity { return $this->activity; } public function getCalendarRange(): ?CalendarRange { return $this->calendarRange; } public function getCancelReason(): ?CancelReason { return $this->cancelReason; } public function getCenters(): ?iterable { switch ($this->getContext()) { case 'person': return [$this->getPerson()->getCenter()]; case 'accompanying_period': return $this->getAccompanyingPeriod()->getCenters(); default: throw new LogicException('context not supported: ' . $this->getContext()); } } public function getComment(): CommentEmbeddable { return $this->comment; } /** * @return 'person'|'accompanying_period'|null */ public function getContext(): ?string { if ($this->getAccompanyingPeriod() !== null) { return 'accompanying_period'; } if ($this->getPerson() !== null) { return 'person'; } return null; } /** * Each time the date and time is update, this version is incremented. */ public function getDateTimeVersion(): int { return $this->dateTimeVersion; } public function getDocuments(): Collection { return $this->documents; } /** * @Serializer\Groups({"docgen:read"}) */ public function getDuration(): ?DateInterval { if ($this->getStartDate() === null || $this->getEndDate() === null) { return null; } return $this->getStartDate()->diff($this->getEndDate()); } public function getEndDate(): ?DateTimeImmutable { return $this->endDate; } public function getId(): ?int { return $this->id; } public function getInviteForUser(User $user): ?Invite { $criteria = Criteria::create(); $criteria->where(Criteria::expr()->eq('user', $user)); $matchings = $this->invites ->matching($criteria); if (1 === $matchings->count()) { return $matchings->first(); } return null; } /** * @return Collection|Invite[] */ public function getInvites(): Collection { return $this->invites; } public function getLocation(): ?Location { return $this->location; } public function getMainUser(): ?User { return $this->mainUser; } public function getPerson(): ?Person { return $this->person; } /** * @return Collection|Person[] */ public function getPersons(): Collection { return $this->persons; } public function getPersonsAssociated(): array { if (null !== $this->accompanyingPeriod) { $personsAssociated = []; foreach ($this->accompanyingPeriod->getParticipations() as $participation) { if ($this->persons->contains($participation->getPerson())) { $personsAssociated[] = $participation->getPerson(); } } return $personsAssociated; } return []; } public function getPersonsNotAssociated(): array { if (null !== $this->accompanyingPeriod) { $personsNotAssociated = []; foreach ($this->persons as $person) { if (!in_array($person, $this->getPersonsAssociated(), true)) { $personsNotAssociated[] = $person; } } return $personsNotAssociated; } return []; } public function getPrivateComment(): PrivateCommentEmbeddable { return $this->privateComment; } /** * @return Collection|ThirdParty[] */ public function getProfessionals(): Collection { return $this->professionals; } public function getSendSMS(): ?bool { return $this->sendSMS; } public function getSmsStatus(): string { return $this->smsStatus; } public function getStartDate(): ?DateTimeImmutable { return $this->startDate; } public function getStatus(): ?string { return $this->status; } public function getThirdParties(): Collection { return $this->getProfessionals(); } public function getUrgent(): ?bool { return $this->urgent; } /** * @return Collection|User[] * @Serializer\Groups({"calendar:read", "read"}) */ public function getUsers(): Collection { return $this->getInvites()->map(static function (Invite $i) { return $i->getUser(); }); } public function hasCalendarRange(): bool { return null !== $this->calendarRange; } public function hasLocation(): bool { return null !== $this->getLocation(); } /** * return true if the user is invited. */ public function isInvited(User $user): bool { if ($this->getMainUser() === $user) { return false; } return $this->getUsers()->contains($user); } public static function loadValidatorMetadata(ClassMetadata $metadata): void { $metadata->addPropertyConstraint('startDate', new NotBlank()); $metadata->addPropertyConstraint('startDate', new Range([ 'min' => '2 years ago', 'max' => '+ 2 years', ])); $metadata->addPropertyConstraint('endDate', new NotBlank()); $metadata->addPropertyConstraint('endDate', new Range([ 'min' => '2 years ago', 'max' => '+ 2 years', ])); } /** * @internal use @{CalendarDoc::setCalendar} with null instead */ public function removeDocument(CalendarDoc $calendarDoc): self { if ($calendarDoc->getCalendar() !== $this) { throw new LogicException('cannot remove document of another calendar'); } return $this; } /** * @internal Use {@link (Calendar::removeUser)} instead */ public function removeInvite(Invite $invite): self { if ($this->invites->removeElement($invite)) { $invite->setCalendar(null); $this->oldInvites[] = $invite; } return $this; } public function removePerson(Person $person): self { $this->persons->removeElement($person); return $this; } public function removeProfessional(ThirdParty $professional): self { $this->professionals->removeElement($professional); return $this; } public function removeUser(User $user): self { if (!$this->getUsers()->contains($user)) { return $this; } $invite = $this->invites ->filter(static function (Invite $invite) use ($user) { return $invite->getUser() === $user; }) ->first(); $this->removeInvite($invite); return $this; } public function setAccompanyingPeriod(?AccompanyingPeriod $accompanyingPeriod): self { $this->accompanyingPeriod = $accompanyingPeriod; return $this; } public function setActivity(?Activity $activity): self { $this->activity = $activity; return $this; } public function setCalendarRange(?CalendarRange $calendarRange): self { if ($this->calendarRange !== $calendarRange) { $this->previousCalendarRange = $this->calendarRange; if (null !== $this->previousCalendarRange) { $this->previousCalendarRange->setCalendar(null); } } $this->calendarRange = $calendarRange; if ($this->calendarRange instanceof CalendarRange) { $this->calendarRange->setCalendar($this); } return $this; } public function setCancelReason(?CancelReason $cancelReason): self { $this->cancelReason = $cancelReason; return $this; } public function setComment(CommentEmbeddable $comment): self { $this->comment = $comment; return $this; } public function setEndDate(DateTimeImmutable $endDate): self { if (null === $this->endDate || $this->endDate->getTimestamp() !== $endDate->getTimestamp()) { $this->increaseaDatetimeVersion(); } $this->endDate = $endDate; return $this; } public function setLocation(?Location $location): Calendar { $this->location = $location; return $this; } public function setMainUser(?User $mainUser): self { if ($this->mainUser !== $mainUser) { $this->previousMainUser = $this->mainUser; } $this->mainUser = $mainUser; $this->removeUser($mainUser); return $this; } public function setPerson(?Person $person): Calendar { $this->person = $person; return $this; } public function setPrivateComment(PrivateCommentEmbeddable $privateComment): self { $this->privateComment = $privateComment; return $this; } public function setSendSMS(?bool $sendSMS): self { $this->sendSMS = $sendSMS; return $this; } public function setSmsStatus(string $smsStatus): self { $this->smsStatus = $smsStatus; return $this; } public function setStartDate(DateTimeImmutable $startDate): self { if (null === $this->startDate || $this->startDate->getTimestamp() !== $startDate->getTimestamp()) { $this->increaseaDatetimeVersion(); } $this->startDate = $startDate; return $this; } public function setStatus(string $status): self { $this->status = $status; if (self::STATUS_CANCELED === $status && $this->getSmsStatus() === self::SMS_SENT) { $this->setSmsStatus(self::SMS_CANCEL_PENDING); } return $this; } public function setUrgent(bool $urgent): self { $this->urgent = $urgent; return $this; } private function increaseaDatetimeVersion(): void { ++$this->dateTimeVersion; } }