POST /api/v1/appointment now accepts resource_uuid. When the resource is a
doctor the doctor is inferred from it, and the booking clinic is derived from
the resource's branch — sending clinic_uuid separately was only ever a way to
make the two disagree. The doctor-only path is untouched, which the public site
depends on since it sends nothing else.
Two guards before the booking is built. The resource must belong to the same
environment as the booking: it arrives as a uuid from the request body, so
TenantFilter does not cover it and without the check a patient could attach
another clinic's device to this clinic's appointment. And a resource that does
not offer the requested service is refused up front rather than discovered when
the patient turns up. That second check runs over the items the calculator
already validated rather than re-reading uuids, which is also why the
tenant-lookup inventory stays where it was.
GET and PUT /api/v1/resource/{uuid}/services manage the offerings. The list
returns the effective duration and price along with which level produced each,
so the panel can label an empty cell "30 minutes — service default" instead of
leaving the user guessing whether it is unset or zero. PUT replaces wholesale,
like the skills endpoint: a row absent from the body is a row the user removed,
and an empty string clears an override back to inheritance rather than setting
zero.
findEligible now also orders by category coverage — a device registered for
"foot" sorts ahead for a foot service. Ordering, not filtering: a clinic that
categorised only some of its devices would otherwise lose the rest.
Thirteen tests across the two files. Suite 1304 green, phpstan at its 14-error
baseline.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
تسکهای موتور نوبتدهی چندمنبعی Clinic Pro
پیادهسازی تدریجی clinic-pro-mostanad-sade.md روی کد موجود. گزارش وضعیت فعلی و تحلیل شکاف: 00-current-state-report.md
⛔ سه قاعدهای که پیش از هر تسکی باید بخوانی
| سند | چه میگوید |
|---|---|
| _shared/red-lines.md | منطق اسلاتی به هیچ عنوان دستکاری نمیشود · نوبتدهی سرویسی در همین فاز کامل میشود |
| _shared/ui-conventions.md | هر صفحه یا بخش جدید عیناً با دیزاینسیستم موجود — هیچ طراحی جدید |
| _shared/definition-of-done.md | هیچ تسکی بدون تکمیل چکلیستش تمام نیست — ✅ 🔄 ⏳ ⚠️ |
در تناقض، این سه سند بر متن تسکها برندهاند.
لیست تسکها
فاز ۰ — تثبیت وضعیت فعلی (پیشنیاز بقیه)
| تسک | ماژول | پروژه | Endpoint | وابستگی | زمان |
|---|---|---|---|---|---|
| ۰۰ | تکمیل نوبتدهی سرویسی | clinicpro |
۴ | — | ۱۴-۱۸h |
| ۰۰ب | سازگارسازی سایت عمومی | nobat724_front |
— | ۰۰ | ۱۰-۱۴h |
فاز ۱ — هستهٔ چندمنبعی
| تسک | ماژول | Endpoint | وابستگی | زمان |
|---|---|---|---|---|
| ۰۱ | شعبه و اتاق | ۸ | ۰۰ | ۱۰-۱۲h |
| ۰۲ | منبع، نوع منبع، مهارت، استخر | ۱۴ | ۰۱ | ۱۴-۱۸h |
| ۰۳ | تقویم منبع، مرخصی، تعطیلات ملی | ۹ | ۰۱، ۰۲ | ۱۲-۱۴h |
| ۰۴ | کاتالوگ خدمات v2 (گروه آیتم، دو نوع زمان) | ۱۰ | ۰۱ | ۱۴-۱۶h |
| ۰۵ | بخشهای نوبت و سازندهٔ برنامه | ۳ | ۰۲، ۰۴ | ۱۶-۲۰h |
| ۰۶ | موتور جستجوی وقت چندمنبعی | ۲ | ۰۳، ۰۵ | ۲۰-۲۴h |
| ۰۷ | رزرو موقت و ثبت نهایی چندمنبعی | ۴ | ۰۶ | ۱۶-۲۰h |
| ۰۸ | لیست قیمت بازهدار و snapshot فاکتور | ۷ | ۰۴، ۰۷ | ۱۲-۱۴h |
فاز ۲ — قوانین
| تسک | ماژول | Endpoint | وابستگی | زمان |
|---|---|---|---|---|
| ۰۹ | موتور قوانین ششدستهای | ۶ | ۰۵، ۰۶، ۰۸ | ۲۰-۲۴h |
| ۱۰ | فرم ساخت قانون + محیط آزمایش | ۲ | ۰۹ | ۱۰-۱۲h |
فاز ۳ — کسبوکار
| تسک | ماژول | Endpoint | وابستگی | زمان |
|---|---|---|---|---|
| ۱۱ | پکیج و دفتر اعتبار جلسات | ۸ | ۰۸ | ۱۰-۱۲h |
| ۱۲ | دوره درمان | ۹ | ۰۷، ۱۱ | ۱۶-۲۰h |
| ۱۳ | سیاست لغو، عدم حضور، لیست انتظار | ۷ | ۰۷ | ۱۰-۱۲h |
فاز ۴ — بهینهسازی
| تسک | ماژول | Endpoint | وابستگی | زمان |
|---|---|---|---|---|
| ۱۴ | رویدادهای دامنه و گزارش بهرهوری | ۳ | ۰۷ | ۸-۱۰h |
مجموع endpoint جدید: ~۹۶ · مجموع زمان: ۲۱۴ تا ۲۵۶ ساعت
ساختار هر تسک
task-XX-name/
├── task.md ← شرح، دامنه، endpoint ها، معیار پذیرش، زمان
├── architecture.md ← فایلها، entity ها، سرویسها، لایهها، قواعد UI
├── database.md ← جداول، ستونها، ایندکسها، migration
├── implementation_notes.md ← نکات فنی، edge case، سازگاری عقبرو، تست
├── checklist.md ← ☑️ وضعیت هر مورد: ✅ 🔄 ⏳ ⚠️
└── user_flow.md ← (تسکهای پیچیده) جریان کاربری
checklist.md اجباری است و ساختار ثابتی دارد:
۰. خط سرخها ۱. بکاند ۲. دیتابیس ۳. UI ۴. تست ۵. مستندات ۶. بازبینی پایانی
چکلیست — قواعد
| نماد | معنی | اجازهٔ باقیماندن در پایان تسک |
|---|---|---|
| ✅ | انجامشده و تأییدشده | بله |
| 🔄 | در حال انجام | نه |
| ⏳ | انجامنشده | نه — مگر با دلیل مکتوب و تسک مقصد |
| ⚠️ | نیازمند بررسی یا تست | نه — باید تعیین تکلیف شود |
پیش از پایان هر تسک، همهٔ ردیفهای چکلیست بازبینی میشوند و وضعیت نهایی میگیرند. هر تسک بخش «۶. بازبینی پایانی» دارد که تستها، مستندات، UI و دو کلاینت دیگر را میسنجد.
ترتیب پیشنهادی اجرا
۰۰ ── ۰۰ب
│
├─ ۰۱ ─┬─ ۰۲ ── ۰۳ ─┐
│ └─ ۰۴ ── ۰۵ ─┴─ ۰۶ ── ۰۷ ─┬─ ۰۸ ─┬─ ۰۹ ── ۱۰
│ │ └─ ۱۱ ── ۱۲
│ ├─ ۱۳
│ └─ ۱۴
فاز ۰ اختیاری نیست. اگر حالت resource (تسک ۰۶) روی حالت service نیمهکاره ساخته
شود، هر باگ موجود سرویسی به موتور جدید ارث میرسد و تشخیص منبعش غیرممکن میشود.
فاز اول قابل عرضه: ۰۰ تا ۰۸ — بعد از آن یک کلینیک زیبایی با اتاق، دستگاه و اپراتور میتواند واقعاً نوبت بگیرد.
سه حالت نوبتدهی
| حالت | مقدار WeeklySchedule.meta.booking_mode |
وضعیت |
|---|---|---|
| اسلاتی | slot |
⛔ قفل — پیشفرض، تولیدی، دستنخورده |
| سرویسی | service |
🔄 موجود ولی نیمهکاره → تسک ۰۰ و ۰۰ب کاملش میکنند |
| چندمنبعی | resource |
⏳ جدید — تسک ۰۶ به بعد |
booking_mode پس از اولین ثبت قفل میشود (WeeklySchedule::getStoredBookingMode()).
تسک ۰۶ یک استثنای کنترلشده اضافه میکند: ارتقای یکطرفه از slot/service به
resource، مشروط بر نبودِ نوبت فعال آینده. بازگشت ممنوع.
ماتریس کامل «کدام endpoint در کدام حالت» در docs/architecture/booking-modes.md
(تسک ۰۰ میسازد، تسک ۰۶ حالت سوم را اضافه میکند).
اجبار خودکار خط سرخ
هر تسک باید این را سبز نگه دارد:
ddev exec php bin/phpunit --group=slot-mode-frozen
تسک ۰۰ این تست و سه fixture آن را میسازد. fixture ها بعد از آن read-only اند: اگر تستی قرمز شد، کد باید برگردد، نه fixture.
قواعد مشترک همهٔ تسکها
از CLAUDE.md و docs/architecture/tenancy.md. فهرست کامل در _shared/definition-of-done.md:
- entity جدید یا
TenantOwnedTraitمیگیرد یا با دلیل درGlobalTablesثبت میشود entity_type, entity_idستونهای اول هر ایندکس ترکیبیِ لیست- هر uuid از request با
TenantOwnershipCheckerسنجیده میشود - timestamp ها
intیونیکس؛ نمایش شمسی فقط در UI - کنترلر نازک ·
BaseController·success/paginated/error - API جدید فقط وقتی هیچ endpoint موجودی کافی نباشد — دلیلش نوشته شود
- تست موفق + خطا + مرزی؛ بدون اجرای موفق تست، تسک تمام نیست
- هر endpoint جدید یا تغییریافته →
docs/api/*در همان نشست - رشتههای UI فارسی از i18n؛ کد و کامیت و مستندات انگلیسی
- سازگاری عقبرو:
nobat724_frontوclinic-pro-tauriدر build خطا نمیدهند — بررسی دستی اجباری است (ردیف بازبینی پایانی هر تسک) - اول commit، بعد
graphify update .