# چکلیست — تسک ۰۰ (تکمیل نوبتدهی سرویسی در clinicpro) **وضعیت کلی:** 🔄 در حال انجام — قابلیت ۱ از ۱۰ (خط سرخ) تمام شد **آخرین بازبینی:** ۱۴۰۵/۰۵/۰۸ قواعد: [_shared/definition-of-done.md](../_shared/definition-of-done.md) · خط سرخها: [_shared/red-lines.md](../_shared/red-lines.md) · UI: [_shared/ui-conventions.md](../_shared/ui-conventions.md) --- ## ۰. خط سرخ — منطق اسلاتی | # | مورد | وضعیت | یادداشت | |---|---|---|---| | ۰.۱ | `SlotModeFrozenTest` + سه fixture ساخته شد **پیش از** هر تغییر کد | ✅ | `tests/Appointment/SlotModeFrozenTest.php` + `tests/Appointment/fixtures/` — هیچ کد تولیدیای هنوز لمس نشده | | ۰.۲ | ~~fixture ها با تاریخ ثابتاند، نه `time()`~~ → fixture **ساختاری** است | ✅ | **انحراف عمدی از متن تسک.** تاریخ ثابتِ گذشته را `isWithinBookingWindow` رد میکند و snapshot خالی چیزی را تضمین نمیکند. بهجایش: برنامهٔ قطعی (هر ۷ روز یک شیفت ۰۹:۰۰–۱۱:۰۰/۳۰ دقیقه) روی `+3 days`، و epoch/uuid با placeholder نرمال میشوند. آنچه قفل میشود: کلیدها، ترتیب، نوعها، ساعتهای محلی | | ۰.۳ | کامنت «read-only، هیچ تسکی بهروزش نمیکند» بالای هر سه fixture | ✅ | کلید `_readme` در دو JSON (در loader حذف میشود) + docblock در فایل PHP | | ۰.۴ | هیچ متد موجود `SlotCalculatorService` ویرایش نشد | ⏳ | قابلیت ۴ — فقط پارامتر اختیاری `excludeAppointmentId` روی `getServiceStartTimes`؛ fixture امضا **یک بار** بهروز میشود (header خودش این را مجاز کرده) | | ۰.۵ | `GET /appointment-slots` بیتبهبیت دستنخورده | ⏳ | در پایان تسک تأیید میشود | | ۰.۶ | `GET /month-availability/{doctorUuid}` دستنخورده | ⏳ | در پایان تسک تأیید میشود | | ۰.۷ | `active_slot_key` و `refreshActiveSlotKey()` دستنخورده | ✅ | `setIsReserve()` **وجود ندارد**؛ toggle رزرو از قبل با `rescheduleTo($start,$end,$isReserve)` انجام میشود که خودش `refreshActiveSlotKey()` را صدا میزند ([Appointment.php:316](../../../src/Appointment/Entity/Appointment.php)). یادداشت قبلی چکلیست غلط بود | | ۰.۸ | `isSlotTaken` امضا و معنا دستنخورده | ⏳ | در پایان تسک تأیید میشود | | ۰.۹ | `--group=slot-mode-frozen` سبز | ✅ | `OK (3 tests, 8 assertions)` — نیازمند `#[Group]` attribute بود، نه `@group` (PHPUnit 12 annotation را حذف کرده) | | ۰.۱۰ | baseline: کل `tests/Appointment` پیش از تغییرات سبز | ✅ | `OK (166 tests, 382 assertions)` | ## ۱. بکاند | # | مورد | وضعیت | یادداشت | |---|---|---|---| | ۱.۱ | `ServiceBookingCalculator` ساخته شد | ⏳ | | | ۱.۲ | `serviceSlots()` موجود از آن استفاده میکند، **خروجیاش عوض نشده** | ⏳ | | | ۱.۳ | جمع سادهٔ `+=` **حفظ شد** (اصلاحش تسک ۰۴ است) | ⏳ | | | ۱.۴ | `ServiceRescheduleService` + `POST /appointment/{uuid}/service-reschedule` | ⏳ | | | ۱.۵ | `PATCH /appointment/{uuid}` توسعه یافت — منطق جدید داخل `isServiceMode()` | ⏳ | | | ۱.۶ | `PATCH` مقدار `service_item_uuids[]` میپذیرد | ⏳ | | | ۱.۷ | `ReserveConversionService` + `POST /appointment/{uuid}/convert-reserve` | ⏳ | | | ۱.۸ | نوبت رزرو در حالت سرویسی سرویسها را ذخیره میکند | ⏳ | | | ۱.۹ | `excludeAppointmentId` روی `getServiceStartTimes` و `findBusyIntervals` | ⏳ | همان الگوی `isSlotTaken` | | ۱.۱۰ | `Appointment::replaceServiceItems()` + `currentServiceUuids()` | ⏳ | | | ۱.۱۱ | `replaceServiceItems` مقدار `serviceItem` تکی را همگام میکند | ⏳ | چهار مصرفکننده رویش خواندهاند | | ۱.۱۲ | `setIsReserve()` صدا زدن `refreshActiveSlotKey()` | ⏳ | | | ۱.۱۳ | اعتبارسنجی زمان با **عضویت در `getServiceStartTimes`**، نه فقط `isSlotTaken` | ⏳ | | | ۱.۱۴ | `TenantOwnershipChecker` روی همهٔ uuid های سرویس، **پیش از** هر بررسی دیگر | ⏳ | | | ۱.۱۵ | `allowInactive` فقط برای سرویسهای موجود نوبت، نه uuid های تازه | ⏳ | | | ۱.۱۶ | کنترلر نازک ماند — منطق در سرویس | ⏳ | | ## ۲. دیتابیس و مهاجرت | # | مورد | وضعیت | یادداشت | |---|---|---|---| | ۲.۱ | `service_total_minutes` و `service_buffer_minutes` (تهیپذیر) | ⏳ | | | ۲.۲ | هیچ ستون موجودی حذف/تغییر نوع/تغییر معنا نداد | ⏳ | | | ۲.۳ | دو کد خطای جدید در `ErrorCodes.php` با پیام فارسی | ⏳ | شمارهٔ واقعی از خود فایل | | ۲.۴ | `app:appointment:backfill-service-duration` — dry-run پیشفرض، idempotent | ⏳ | | | ۲.۵ | backfill مقدار را از خود نوبت میگیرد، نه بازمحاسبه از سرویسها | ⏳ | | | ۲.۶ | migration اجرا شد و `TenantSchemaCoverageTest` سبز | ⏳ | | ## ۳. UI — پنل ادمین | # | مورد | وضعیت | یادداشت | |---|---|---|---| | ۳.۱ | `AppointmentEditPage`: حالت سرویسی `ServiceSlotPicker` نشان میدهد | ⏳ | | | ۳.۲ | `AppointmentEditPage`: حالت اسلاتی **دقیقاً** رفتار امروز | ⏳ | سه فیلد ساعت | | ۳.۳ | ورودی دستی ساعت در حالت سرویسی **پنهان**، نه disabled | ⏳ | | | ۳.۴ | `ServiceSlotPicker` موجود بازاستفاده شد؛ نسخهٔ موازی ساخته نشد | ⏳ | فقط prop `excludeAppointmentUuid` | | ۳.۵ | `ReserveAppointmentsPage`: سرویسها + دکمهٔ تبدیل | ⏳ | | | ۳.۶ | `ReserveAppointmentsPage` از جدول خام به `DataTable` مهاجرت کرد | ⏳ | `