feat(treatment): bind an appointment to a chosen session, and free it on cancel
Two holes in how a course's later appointments were made.
The link from the unbooked queue carried nothing — `/admin/appointments/new`
with no parameters — so the secretary retyped the patient and the service, and
which case the appointment joined was inferred from the service they happened to
pick. A patient with two open courses had no way to say which one they meant,
and picking the wrong service silently opened a third case. (The suggestion link
did pass slot_start and resource_uuid, but the create page never read either.)
POST /api/v1/my/appointment now takes an optional treatment_session_uuid.
SessionBookingLink validates it — same tenant, still unbooked, case open, same
patient — and reserves that session. Confirm-time attachment steps aside when
the appointment already holds a session. The booking form states in words which
session, which course and which patient it is about to book, read from a new
GET /api/v1/treatment-session/{uuid}.
Nothing ever detached a session from its appointment, so a cancelled booking
left the session `booked` forever, and since findNextUnbooked requires
"has no appointment", it could never return to the queue. Cancellation and
no-show now release it back to `planned`. A finished session is history and is
left alone.
The system still never books the next appointment by itself — it only suggests.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -50,6 +50,7 @@ class AppointmentController extends BaseController
|
||||
private readonly \App\Appointment\Service\ServiceBookingCalculator $serviceCalculator,
|
||||
private readonly \App\Appointment\Service\ServiceRescheduleService $rescheduleService,
|
||||
private readonly \App\Appointment\Availability\Service\ResourceOccupier $occupier,
|
||||
private readonly \App\Treatment\Service\SessionBookingLink $sessionLink,
|
||||
private readonly \Psr\Log\LoggerInterface $logger,
|
||||
) {}
|
||||
|
||||
@@ -1067,6 +1068,18 @@ class AppointmentController extends BaseController
|
||||
if ($appointment->getResource() !== null) {
|
||||
$this->occupier->releaseForAppointment((int) $appointment->getId());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* جلسهٔ درمان با لغو **و** غیبت آزاد میشود.
|
||||
*
|
||||
* غیبت در `CANCEL_STATUSES` نیست چون نوبت را لغو نمیکند، ولی برای دوره فرقی
|
||||
* ندارد: بیمار نیامده و آن جلسه باید دوباره قابل رزرو باشد. سابقهٔ غیبت روی
|
||||
* خودِ نوبت میماند، پس چیزی از دست نمیرود.
|
||||
*/
|
||||
if (in_array($newStatus, [...self::CANCEL_STATUSES, Appointment::STATUS_NO_SHOW], true)) {
|
||||
$this->sessionLink->release($appointment);
|
||||
}
|
||||
|
||||
if ($newStatus === Appointment::STATUS_COMPLETED) {
|
||||
|
||||
@@ -50,6 +50,7 @@ class MyAppointmentsController extends BaseController
|
||||
private readonly \App\Resource\Repository\ClinicResourceRepository $resourceRepo,
|
||||
private readonly \App\Resource\Service\ResourceBookingSlotService $resourceSlots,
|
||||
private readonly \App\Appointment\Availability\Service\ResourceOccupier $occupier,
|
||||
private readonly \App\Treatment\Service\SessionBookingLink $sessionLink,
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -355,6 +356,20 @@ class MyAppointmentsController extends BaseController
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* جلسهٔ درمانِ صریح.
|
||||
*
|
||||
* یک بیمار میتواند چند دورهٔ باز داشته باشد؛ بدون این، اتصال از روی سرویس
|
||||
* حدس زده میشود و سرویسِ اشتباه بیصدا یک پروندهٔ موازی میسازد. بعد از
|
||||
* `bookAtomically` میآید چون شناسهٔ نوبت لازم است.
|
||||
*/
|
||||
$sessionUuid = trim((string) ($data['treatment_session_uuid'] ?? ''));
|
||||
|
||||
if ($sessionUuid !== '') {
|
||||
$this->sessionLink->bind($appointment, $sessionUuid, ...$this->branches->pair($user));
|
||||
$this->appointmentRepo->save($appointment);
|
||||
}
|
||||
|
||||
return $this->success([
|
||||
'uuid' => $appointment->getUuid(),
|
||||
'slot_start' => $slotStart,
|
||||
|
||||
Reference in New Issue
Block a user