cases->findOpenFor($record, $service) ?? $this->opener->open( $record->getEntityType(), $record->getEntityId(), $record, $service, $protocol, ); $this->attachToNextOpenSession($case, $appointment); return $case; } public function onSessionFinished(TreatmentSession $session): void { $next = $this->scheduler->scheduleNextAfter($session); if ($next === null) { $this->closeIfEverySessionIsSettled($session->getTreatmentCase()); } $this->em->flush(); } /** * نوبت به اولین جلسهٔ بدونِ نوبت وصل می‌شود، نه لزوماً به جلسهٔ اول. * * مگر اینکه منشی هنگام ثبت، جلسه را صریح انتخاب کرده باشد؛ آن‌وقت نوبت از قبل * وصل است و حدسِ «اولین بدون نوبت» فقط جلسهٔ دیگری را اشتباهی رزرو می‌کند. */ protected function attachToNextOpenSession(TreatmentCase $case, Appointment $appointment): void { if ($this->sessions->findOneBy(['appointment' => $appointment]) !== null) { return; } $session = $this->sessions->findNextUnbooked($case); if ($session === null) { return; } $session->attachAppointment($appointment); // سررسیدِ جلسه‌ای که نوبت گرفته دیگر حدس نیست — همان ساعت نوبت است. $this->scheduler->seedFirstSession($session, $appointment->getSlotStart()); $this->em->flush(); } protected function closeIfEverySessionIsSettled(TreatmentCase $case): void { foreach ($case->getSessions() as $session) { if (!in_array($session->getStatus(), [TreatmentSession::STATUS_DONE, TreatmentSession::STATUS_CANCELLED], true)) { return; } } $case->close(TreatmentCase::STATUS_COMPLETED); } }