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:
@@ -78,6 +78,7 @@ Get all appointment slots (available and booked) for a doctor on a specific date
|
||||
| `doctor_uuid` | string (uuid) | ✅ | |
|
||||
| `date` | string `Y-m-d` | ✅ | |
|
||||
| `service_item_uuids[]` | string[] | ✅ | یک یا چند UUID سرویسِ bookable |
|
||||
| `durations[<service_uuid>]` | int | ❌ | override مدت (دقیقه) برای همان سرویس — فقط در این محاسبه استفاده میشود و مقدار پیشفرضِ سرویس در تنظیمات تغییر نمیکند. برای نوبتدهیِ منشی که مدت را برای یک نوبت تغییر میدهد. مقدار ≤ 0 یا غایب ⇒ مدت پیشفرض سرویس |
|
||||
|
||||
### Response `200`
|
||||
```json
|
||||
@@ -117,7 +118,7 @@ Get all appointment slots (available and booked) for a doctor on a specific date
|
||||
"booking_mode": "service",
|
||||
"buffer_minutes": 5,
|
||||
"services": [
|
||||
{ "uuid": "…", "name": "عصبکشی", "duration_minutes": 30, "price_rials": 5000000 }
|
||||
{ "uuid": "…", "name": "عصبکشی", "duration_minutes": 30, "price_rials": 5000000, "service_section": { "uuid": "…", "name": "دندان" } }
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -209,6 +210,7 @@ Book an appointment slot.
|
||||
| `slot_end` | integer | ⚠️ | Slot end (Unix timestamp). فقط وقتی `duration_from_services=true` باشد سرور آن را از `slot_start + Σ duration_minutes` بازمحاسبه میکند؛ در غیر این صورت مقدار کلاینت حفظ میشود |
|
||||
| `service_item_uuids` | string[] | ❌ | یک یا چند UUID سرویس که به نوبت **پیوست** میشوند (چند سرویس). اولین سرویس بهعنوان سرویسِ اصلی (`service_item`) ثبت و همه در `service_items` برمیگردند. UUID ناموجود ⇒ `422` |
|
||||
| `duration_from_services` | boolean | ❌ | `true` = حالت نوبتدهی سرویسی: مدت نوبت از مجموع `duration_minutes` سرویسها محاسبه و `slot_end` بازنویسی میشود؛ در این حالت سرویسِ غیرbookable یا بدون مدت ⇒ `422`. پیشفرض `false` (حالت اسلاتی: فقط پیوست، ساعت پایانِ دستی حفظ میشود) |
|
||||
| `service_durations` | object | ❌ | override مدت هر سرویس بهصورت `{ "<service_uuid>": <minutes> }` — فقط وقتی `duration_from_services=true`. برای نوبتدهیِ منشی که مدت را برای همان نوبت تغییر میدهد؛ در محاسبهٔ `slot_end` لحاظ میشود و **مقدار پیشفرضِ سرویس تغییر نمیکند**. مقدار ≤ 0 یا غایب ⇒ مدت پیشفرض |
|
||||
| `for_self` | boolean | ❌ | `true` (default) = patient is the logged-in payer; `false` = booking for someone else |
|
||||
| `patient_name` | string | ⚠️ | Required when `for_self=false`; otherwise filled from the payer's profile |
|
||||
| `patient_mobile` | string | ⚠️ | Required when `for_self=false`; otherwise the payer's mobile |
|
||||
@@ -633,6 +635,9 @@ Extra optional body fields: `service_section_uuid`, `service_item_uuid`, `staff_
|
||||
`is_reserve: true` → day-level reserve entry: `slot_end` may equal `slot_start`, the past-slot rule is skipped, and the entry never occupies a slot (several reserves may share a day). Response `201` now also returns `is_reserve`.
|
||||
`service_item_uuids[]` (غیرِ رزرو): یک یا چند سرویس که به نوبت **پیوست** میشوند (چند سرویس)؛ اولین سرویس = سرویسِ اصلی و همه در `service_items` برمیگردند. UUID ناموجود ⇒ `422`.
|
||||
`duration_from_services: true` (حالت نوبتدهی سرویسی): مدت نوبت از مجموع `duration_minutes` سرویسها محاسبه و `slot_end` بازنویسی میشود؛ در این حالت سرویسِ غیرbookable یا بدون مدت ⇒ `422`. بدون این پرچم (حالت اسلاتی)، ساعت پایانِ دستی حفظ میشود.
|
||||
`service_durations` (فقط با `duration_from_services=true`): override مدت هر سرویس `{ "<uuid>": <minutes> }` برای همان نوبت (منشی)؛ در `slot_end` لحاظ میشود و مقدار پیشفرضِ سرویس تغییر نمیکند.
|
||||
|
||||
> **بخشِ سرویس در `appointment-booking-services`:** هر آیتم `services[]` علاوه بر `uuid/name/duration_minutes/price_rials`، فیلد `service_section: { uuid, name }` هم دارد تا فرمِ نوبتدهیِ سرویسی سرویسها را «بخش → سرویس» گروهبندی کند. عقبرو-سازگار (افزودنِ فیلد).
|
||||
|
||||
### GET `/api/v1/my/appointments` (extended)
|
||||
New query param `reserve=1` → returns only reserve-list entries; without it only regular slot bookings are returned. Each row now also includes: `patient_uuid`, `is_reserve`, `deposit_required`, `deposit_amount_rials`, `note`, `service_section`, `service_item`, `staff` (each `{uuid, name|full_name}` or null).
|
||||
|
||||
Reference in New Issue
Block a user