- Add task for completing service mode in clinicpro with detailed objectives and acceptance criteria. - Create architecture documentation for task 00b, outlining involved components and necessary changes. - Develop checklist for task 00b to ensure all requirements are met. - Document implementation notes for task 00b, emphasizing API contract checks and design system adherence. - Update task documentation for task 00b, specifying goals and current issues with service mode.
9.5 KiB
تسک ۰۰ — تکمیل نوبتدهی سرویسی در clinicpro
فاز: ۰ (تثبیت وضعیت فعلی) · وابستگی: — · زمان: ۱۴-۱۸ ساعت پیشنیاز همهٔ تسکهای ۰۱ به بعد
⛔ خط سرخ
منطق اسلاتی (booking_mode = 'slot') در این تسک به هیچ عنوان دستکاری نمیشود.
فهرست کامل قفلشدهها: _shared/red-lines.md.
این تسک fixture و تست --group=slot-mode-frozen را میسازد — همان تستی که همهٔ
تسکهای بعدی باید سبز نگهش دارند.
هدف
حالت booking_mode = 'service' در مسیر رزرو کار میکند، ولی در بقیهٔ چرخهٔ عمر نوبت
غایب است. این تسک آن را کامل میکند تا موتور چندمنبعی (تسک ۰۶) روی پایهٔ سالم ساخته شود.
وضعیت فعلی — چه کار میکند و چه نمیکند
✅ کار میکند
| مسیر | فایل |
|---|---|
| انتخاب سرویس و اسلات در رزرو عمومی | GET /api/v1/appointment-booking-services/{doctorUuid} · GET /api/v1/appointment-service-slots |
| محاسبهٔ زمانهای شروع بر اساس مدت سرویس | SlotCalculatorService::getServiceStartTimes() |
| ثبت نوبت با چند سرویس | POST /api/v1/appointment + appointment_service_items |
| توگل روش نوبتدهی در تنظیمات | assets/admin/components/schedule/ScheduleSection.tsx |
| ساخت نوبت از پنل | assets/admin/pages/AppointmentCreatePage.tsx + components/appointments/ServiceSlotPicker.tsx + hooks/useDoctorBookingServices.ts |
| ساخت سریع از drawer | assets/admin/components/NewAppointmentDrawer.tsx |
| رزرو از سایت | nobat724_front/components/appointment/* |
❌ کار نمیکند — شکافهای این تسک
۱. ویرایش و جابهجایی نوبت، حالت سرویسی را نمیشناسد.
PATCH /api/v1/appointment/{uuid} (AppointmentController.php:1077):
$hasStart = array_key_exists('slot_start', $data);
$hasEnd = array_key_exists('slot_end', $data);
// … فقط این دو بررسی میشوند:
if ($newEnd <= $newStart) { /* 422 */ }
if ($this->appointmentRepo->isSlotTaken($doctor, $newStart, $newEnd, $id)) { /* 409 */ }
سه مشکل:
- مدت دلخواه پذیرفته میشود؛ هیچ بررسیای که
slot_end - slot_startبا مجموع مدت سرویسهای نوبت بخواند وجود ندارد buffer_minutesنادیده گرفته میشود — نوبت جدید میتواند چسبیده به نوبت بعدی بنشیند- فقط
service_item_uuidتکی بهروز میشود؛service_items(ManyToMany) دستنخورده میماند → نوبت با سرویسهای قبلی و مدت جدید ناسازگار میشود
۲. AppointmentEditPage.tsx ورودی دستی ساعت دارد.
سه فیلد date/start/end آزاد + یک SearchableSelect تکی برای سرویس
(AppointmentEditPage.tsx:74-76).
هیچ ServiceSlotPicker ای نیست، هیچ چند-سرویسی نیست.
نتیجه: منشی نوبت سرویسیِ ۴۵ دقیقهای را ویرایش میکند، ۲۰ دقیقه میگذارد، سیستم قبول میکند، و بیمار بعدی روی نوبت اول مینشیند.
۳. نوبت رزرو (is_reserve) در حالت سرویسی معنا ندارد.
NewAppointmentDrawer.tsx:72 صریح: $serviceMode = bookingMode === 'service' && !isReserve.
پس نوبت رزرو همیشه اسلاتی رفتار میکند و ReserveAppointmentsPage.tsx فقط
service_item?.name تکی نشان میدهد. تبدیل رزرو به نوبت واقعی هم مسیر سرویسی ندارد.
۴. patient_facing بودن مدت جایی نمایش داده نمیشود.
پاسخ appointment-service-slots مدت کل را میدهد ولی نوبت ثبتشده هیچجا نگه نمیدارد
که این مدت از کدام سرویسها و چه بافری آمده. لیست نوبتها فقط slot_start/slot_end دارد.
دامنه
هست:
ServiceBookingCalculator— یک سرویس واحد که «مدت مجاز یک ترکیب سرویس» را حساب میکند (استخراج منطق تکرارشدهٔserviceSlots()از کنترلر)- اعتبارسنجی حالت سرویسی در
PATCH /appointment/{uuid} - endpoint جابهجایی سرویسآگاه:
POST /api/v1/appointment/{uuid}/service-reschedule ServiceSlotPickerدرAppointmentEditPage- حالت سرویسی برای نوبت رزرو + تبدیل رزرو به نوبت
- ستونهای
service_total_minutesوservice_buffer_minutesرویappointments - fixture و تست
--group=slot-mode-frozen
نیست: بخشهای نوبت، چند منبع، قوانین (تسک ۰۵ به بعد). nobat724_front (تسک ۰۰ب).
Endpoint ها
| متد | مسیر | توضیح |
|---|---|---|
| POST | /api/v1/appointment/{uuid}/service-reschedule |
جابهجایی سرویسآگاه: سرویسها + زمان شروع؛ مدت را خودش حساب میکند |
| PATCH | /api/v1/appointment/{uuid} |
توسعه — در حالت سرویسی مدت را اعتبارسنجی میکند و service_item_uuids[] میپذیرد |
| POST | /api/v1/appointment/{uuid}/convert-reserve |
تبدیل نوبت رزرو به نوبت زماندار (هر دو حالت) |
| GET | /api/v1/appointment-service-slots |
توسعه — پارامتر exclude_appointment_uuid برای جابهجایی |
هیچ endpoint اسلاتیای تغییر نمیکند. GET /appointment-slots دستنخورده.
معیار پذیرش
- ✅ موفق: نوبت سرویسیِ «لیزر صورت (۲۰) + بیکینی (۱۵)» با مدت ۳۵ دقیقه.
POST /appointment/{uuid}/service-rescheduleبا زمان جدید و همان سرویسها →200وslot_end - slot_start = 35 * 60دقیقاً. - ✅ موفق: همان endpoint با حذف بیکینی → مدت خودکار ۲۰ دقیقه میشود، بدون اینکه کلاینت عددی بفرستد.
- ✅ موفق:
GET /appointment-service-slots?…&exclude_appointment_uuid={uuid}بازهٔ خودِ نوبت را اشغال حساب نمیکند، پس زمان فعلیاش در فهرست میآید. - ✅ موفق:
AppointmentEditPageبرای نوبت سرویسی،ServiceSlotPickerنشان میدهد و ورودی دستی ساعت را پنهان میکند؛ برای نوبت اسلاتی، دقیقاً رفتار امروز. - ✅ موفق: نوبت رزرو در حالت سرویسی سرویسهایش را ذخیره میکند و
POST /convert-reserveبا زمان انتخابی، نوبت زماندار با مدت درست میسازد. - ✅ موفق (خط سرخ):
ddev exec php bin/phpunit --group=slot-mode-frozenسبز است و fixture قرارداد اسلاتی بیتبهبیت تغییر نکرده. - ❌ خطا:
PATCHباslot_end - slot_startناسازگار با مدت سرویسها →422ERR_SERVICE_DURATION_MISMATCHبا پیام فارسی شامل مدت درست. - ❌ خطا:
service-rescheduleروی نوبت اسلاتی →422ERR_WRONG_BOOKING_MODE. - ❌ خطا:
service-rescheduleبا زمان شروعی که درgetServiceStartTimesنیست →422با پیام «این زمان برای مدت انتخابی در دسترس نیست». - ❌ خطا: سرویس محیط دیگر در
service_item_uuids[]→404(بدون لو دادن وجودش). - ⚠️ مرزی: نوبتی که سرویسهایش غیرفعال (
bookable=false) شدهاند → جابهجایی مجاز است باwarnings[]؛ افزودن سرویس غیرفعال ممنوع. - ⚠️ مرزی:
buffer_minutesتغییر کرد بعد از ثبت نوبت → نوبت موجود سالم میماند؛ فقط جابهجایی جدید بافر جدید را میگیرد. - ⚠️ مرزی: جابهجایی به روزی که برنامهٔ هفتگی آن محیط عوض شده → همان اعتبارسنجی
getServiceStartTimes، پس خودکار پوشش داده میشود. - ⚠️ مرزی: نوبت سرویسی بدون هیچ سرویس (داده قدیمی) → مدت موجود حفظ میشود و
warnings[]میگوید سرویس ثبت نشده. رد نمیشود. - ⚠️ مرزی:
PATCHبدونslot_startروی نوبت سرویسی (فقط تغییر یادداشت) → بدون اعتبارسنجی مدت، مثل امروز.
خروجی
src/Appointment/Service/ServiceBookingCalculator.php+ توسعهٔ کنترلرassets/admin/pages/AppointmentEditPage.tsxتوسعهیافتهassets/admin/pages/ReserveAppointmentsPage.tsxتوسعهیافته- migration دو ستون تهیپذیر
tests/Appointment/SlotModeFrozenTest.php+ fixturedocs/api/appointment.mdبهروزرسانی- checklist.md کاملشده