Adds SlotModeFrozenTest (#[Group('slot-mode-frozen')]) locking three things
against the multi-resource booking phase:
- GET /api/v1/appointment-slots response shape
- GET /api/v1/appointment-settings/month-availability/{uuid} response shape
- public method signatures of SlotCalculatorService
Fixtures are structural, not raw snapshots: a fixed past date is rejected by
isWithinBookingWindow so an empty snapshot would prove nothing. Instead a
deterministic schedule on a computed near-future date, with epoch/uuid values
normalized to placeholders. What stays locked is the contract itself: keys,
ordering, types and local times.
No production code touched.
Task: docs/new_feture/taskes/task-00-service-mode-completion/
Slot-mode contract: unchanged (--group=slot-mode-frozen green, 3 tests)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
9.5 KiB
9.5 KiB
چکلیست — تسک ۰۰ (تکمیل نوبتدهی سرویسی در clinicpro)
وضعیت کلی: 🔄 در حال انجام — قابلیت ۱ از ۱۰ (خط سرخ) تمام شد آخرین بازبینی: ۱۴۰۵/۰۵/۰۸
قواعد: _shared/definition-of-done.md · خط سرخها: _shared/red-lines.md · UI: _shared/ui-conventions.md
۰. خط سرخ — منطق اسلاتی
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۰.۱ | SlotModeFrozenTest + سه fixture ساخته شد پیش از هر تغییر کد |
✅ | tests/Appointment/SlotModeFrozenTest.php + tests/Appointment/fixtures/ — هیچ کد تولیدیای هنوز لمس نشده |
| ۰.۲ | time() |
✅ | انحراف عمدی از متن تسک. تاریخ ثابتِ گذشته را 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). یادداشت قبلی چکلیست غلط بود |
| ۰.۸ | 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 مهاجرت کرد |
⏳ | <td style={td}> حذف شد |
| ۳.۷ | مدت و بافر فارسی با واحد: «۳۵ دقیقه (+۱۰ دقیقه فاصله)» | ⏳ | |
| ۳.۸ | هیچ رنگ/شعاع/سایهٔ hard-code — همه از توکنهای styles.css |
⏳ | |
| ۳.۹ | دارکمود (data-theme="dark") بررسی شد |
⏳ | |
| ۳.۱۰ | حالت فشرده (data-density="compact") بررسی شد |
⏳ | |
| ۳.۱۱ | انتخاب چند سرویس با SearchableSelect؛ هیچ <select> بومی |
⏳ | |
| ۳.۱۲ | backTo/BackButton روی هر دو صفحه |
⏳ | |
| ۳.۱۳ | وضعیت لیست رزروها در URL با useUrlState |
⏳ | |
| ۳.۱۴ | تاریخ با PersianDateInput · مبلغ با formatRial |
⏳ | |
| ۳.۱۵ | RTL بررسی شد (ms/me نه ml/mr) |
⏳ | |
| ۳.۱۶ | موبایل بررسی شد — بدون اسکرول افقی | ⏳ | |
| ۳.۱۷ | همهٔ رشتهها فارسی و از i18n | ⏳ | |
| ۳.۱۸ | داده با TanStack Query و استخراج envelope درست | ⏳ |
۴. تست
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۴.۱ | SlotModeFrozenTest — سه سنجه |
⏳ | |
| ۴.۲ | ServiceBookingCalculatorTest — موفق/خطا/مرزی |
⏳ | |
| ۴.۳ | ServiceRescheduleTest — شامل «حذف سرویس → مدت خودکار» |
⏳ | |
| ۴.۴ | PatchServiceDurationTest — شامل «در حالت اسلاتی هیچکدام اجرا نمیشود» |
⏳ | |
| ۴.۵ | ConvertReserveTest — شامل active_slot_key و رقابت |
⏳ | |
| ۴.۶ | ServiceModeSectionDurationTest موجود سبز ماند |
⏳ | |
| ۴.۷ | BookingTenantTest موجود سبز ماند |
⏳ | |
| ۴.۸ | AppointmentEditPage.test.tsx — دو حالت |
⏳ |
۵. مستندات
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۵.۱ | docs/api/appointment.md — دو endpoint جدید + توسعهٔ PATCH |
⏳ | |
| ۵.۲ | ماتریس «کدام endpoint در کدام حالت» | ⏳ | |
| ۵.۳ | docs/architecture/booking-modes.md ساخته شد |
⏳ | تسک ۰۶ حالت سوم را اضافه میکند |
| ۵.۴ | دو کد خطای جدید مستند شد | ⏳ |
۶. بازبینی پایانی
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۶.۱ | همهٔ ردیفهای بالا وضعیت نهایی دارند (هیچ 🔄 و ⏳ بیدلیل) | ⏳ | |
| ۶.۲ | ddev exec php bin/phpunit کامل سبز |
⏳ | |
| ۶.۳ | ddev exec php bin/phpunit --group=slot-mode-frozen سبز |
⏳ | |
| ۶.۴ | phpstan analyse بدون خطای جدید |
⏳ | |
| ۶.۵ | npx tsc --noEmit بدون خطا |
⏳ | |
| ۶.۶ | yarn test سبز |
⏳ | |
| ۶.۷ | TenantSchemaCoverageTest + TenantLookupInventoryTest سبز |
⏳ | |
| ۶.۸ | docs/api/* بهروز شد |
⏳ | |
| ۶.۹ | چکلیست UI (بخش ۳) کامل شد | ⏳ | |
| ۶.۱۰ | nobat724_front و clinic-pro-tauri دستی بررسی شدند |
⏳ | service_item تکی همگام است؟ |
| ۶.۱۱ | commit شد، سپس graphify update . |
⏳ | |
| ۶.۱۲ | موارد بهتعویقافتاده با دلیل و تسک مقصد ثبت شدند | ⏳ |