feat: section-based service picker + per-appointment duration override (service mode)
Service-booking mode now selects services by section like slot mode: appointment-booking-services returns service_section per item; ServiceSlotPicker groups by section (SearchableSelect), accumulates picks across sections into a removable 'section -> service' chip list. Secretaries can override a service's duration for a single appointment without changing the service default: appointment-service-slots accepts durations[uuid] and both create endpoints accept service_durations; the override drives total duration and slot_end. Online (patient) booking is unaffected — it never sends overrides. Backend + frontend tests and docs updated. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -167,6 +167,9 @@ class AppointmentController extends BaseController
|
||||
return $this->error(ErrorCodes::ERR_VALIDATION_001, 'انتخاب حداقل یک سرویس الزامی است', 422, 'service_item_uuids');
|
||||
}
|
||||
|
||||
// مدتِ override منشی (فقط برای همین محاسبه؛ پیشفرض سرویس تغییر نمیکند). durations[uuid]=minutes
|
||||
$overrides = (array) $request->query->all('durations');
|
||||
|
||||
$totalMinutes = 0;
|
||||
foreach ($uuids as $u) {
|
||||
$item = $this->itemRepo->findByUuid($u);
|
||||
@@ -176,10 +179,13 @@ class AppointmentController extends BaseController
|
||||
if (!$item->isBookable()) {
|
||||
return $this->error(ErrorCodes::ERR_VALIDATION_001, 'این سرویس برای نوبتدهی فعال نیست', 422, 'service_item_uuids');
|
||||
}
|
||||
if (($item->getDurationMinutes() ?? 0) <= 0) {
|
||||
$duration = isset($overrides[$u]) && (int) $overrides[$u] > 0
|
||||
? (int) $overrides[$u]
|
||||
: (int) ($item->getDurationMinutes() ?? 0);
|
||||
if ($duration <= 0) {
|
||||
return $this->error(ErrorCodes::ERR_VALIDATION_001, 'مدت سرویس تعریف نشده است', 422, 'service_item_uuids');
|
||||
}
|
||||
$totalMinutes += (int) $item->getDurationMinutes();
|
||||
$totalMinutes += $duration;
|
||||
}
|
||||
|
||||
$meta = $schedule ? $schedule->getMeta() : WeeklySchedule::DEFAULT_META;
|
||||
@@ -210,12 +216,16 @@ class AppointmentController extends BaseController
|
||||
$schedule = $this->scheduleRepo->findByDoctor($doctor);
|
||||
$meta = $schedule ? $schedule->getMeta() : WeeklySchedule::DEFAULT_META;
|
||||
|
||||
$services = array_map(fn(\App\ClinicService\Entity\ServiceItem $i) => [
|
||||
'uuid' => $i->getUuid(),
|
||||
'name' => $i->getName(),
|
||||
'duration_minutes' => $i->getDurationMinutes(),
|
||||
'price_rials' => $i->getPriceRials(),
|
||||
], $this->itemRepo->findBookableByEntity('doctor', $doctor->getId()));
|
||||
$services = array_map(function (\App\ClinicService\Entity\ServiceItem $i) {
|
||||
$section = $i->getSection();
|
||||
return [
|
||||
'uuid' => $i->getUuid(),
|
||||
'name' => $i->getName(),
|
||||
'duration_minutes' => $i->getDurationMinutes(),
|
||||
'price_rials' => $i->getPriceRials(),
|
||||
'service_section' => ['uuid' => $section->getUuid(), 'name' => $section->getName()],
|
||||
];
|
||||
}, $this->itemRepo->findBookableByEntity('doctor', $doctor->getId()));
|
||||
|
||||
return $this->success([
|
||||
'doctor_uuid' => $doctorUuid,
|
||||
|
||||
Reference in New Issue
Block a user