# Appointment Plan API — بخش‌های نوبت و سازندهٔ برنامه > **Base:** `/api/v1` · **Auth:** JWT > وابسته به [resource.md](resource.md) (منبع و مهارت) و [clinic-services.md](clinic-services.md) (مدت آیتم‌ها). --- ## چرا نوبت یک تکه نیست بند ۷ مستند. یک جلسهٔ لیزر: | بخش | مدت | اتاق | اپراتور | دستگاه | |---|---|---|---|---| | مالیدن کرم بی‌حسی | ۵ | اشغال | اشغال | آزاد | | انتظار اثر کرم | ۳۰ | اشغال | **آزاد** | آزاد | | خود لیزر | ۲۰ | اشغال | اشغال | اشغال | | مراقبت بعد | ۵ | اشغال | اشغال | آزاد | با مدل تک‌بازه‌ای، اپراتور ۶۰ دقیقه قفل می‌شود در حالی که ۳۰ دقیقه کار می‌کند — **نصف ظرفیت هدر می‌رود**. ⚠️ این بخش هیچ زمان مطلقی و هیچ منبع مشخصی تعیین نمی‌کند. فقط **شکل** نوبت را می‌سازد؛ پیدا کردن وقت و منبع آزاد کارِ تسک بعدی است. --- ## `GET/PUT /api/v1/service-item/{uuid}/segments` `PUT` جایگزینی کامل است. هر بخش: | فیلد | نوع | توضیح | |---|---|---| | `sequence` | int | ترتیب اجرا | | `name` | string | ✅ الزامی | | `duration_source` | `fixed` \| `items` | پیش‌فرض `fixed` | | `duration_minutes` | int | برای `fixed` باید مثبت باشد | | `patient_present` | bool | پیش‌فرض `true`؛ «انتظار در خانه» خلافش است | | `mergeable` | bool | با چند آیتم **یک بار** می‌آید | | `requirements` | array | نیازمندی منبع | **`duration_source: "items"`** یعنی مدت این بخش از آیتم‌های انتخاب‌شده می‌آید و با فرمول تسک ۰۴ حساب می‌شود. «خود لیزر» با دو ناحیه طولانی‌تر می‌شود، ولی «انتظار اثر کرم» نه — به همین دلیل دو منبع مدت لازم است و یک عدد ثابت کافی نیست. هر نیازمندی: | فیلد | توضیح | |---|---| | `type_uuid` | ✅ نوع منبع (اتاق، اپراتور، دستگاه) | | `skill_uuid` | مهارت لازم | | `count` | پیش‌فرض ۱ | | `occupancy` | `exclusive` (قفل کامل) یا `shared` (ظرفیت می‌شمارد) | | `constraints` | فعلاً فقط `same_gender_as_patient` | **۴۲۲:** نام خالی · بخش `fixed` با مدت صفر · مجموع بیش از ۴۸۰ دقیقه · `occupancy` یا `constraint` ناشناخته. --- ## `POST /api/v1/appointment-plan/preview` ```json { "service_uuid": "…لیزر", "branch_uuid": "…شعبه", "item_uuids": ["…صورت", "…بیکینی"], "patient_gender": "female" } ``` **۲۰۰:** ```json { "success": true, "data": { "total_minutes": 60, "segments": [ { "sequence": 1, "name": "بی‌حسی موضعی", "offset_minutes": 0, "duration_minutes": 5, "patient_present": true, "mergeable": false, "requirements": [ { "role": "room", "role_name": "اتاق", "skill_name": null, "count": 1, "occupancy": "exclusive", "constraints": [], "candidates": 3, "occupancy_offset": { "setup_minutes": 0, "cleanup_minutes": 0 } } ] } ] } } ``` `candidates` تعداد منابع واجد شرایط است. `occupancy_offset` در نمای کاربر نمایش داده **نمی‌شود**؛ برای موتور جستجوی وقت است و محافظه‌کارانه از بیشترین مقدارِ کاندیدها گرفته می‌شود — کم گرفتنش یعنی نوبت بعدی روی زمان تمیزکاری بیفتد. ### خطاها | کد | کِی | |---|---| | `ERR_NO_ELIGIBLE_RESOURCE` (۴۲۲) | هیچ منبعی شرایط یک بخش را ندارد | | `ERR_VALIDATION_002` (۴۲۲) | قید جنسیت هست ولی جنسیت بیمار نامشخص است | | `ERR_VALIDATION_001` (۴۲۲) | مدت بخش تعیین نشده · مجموع بیش از سقف | | ۴۰۴ | سرویس یا شعبهٔ محیط دیگر | پیام `ERR_NO_ELIGIBLE_RESOURCE` انسانی است و نقش، مهارت و شعبه را می‌گوید: «هیچ اپراتور خانمی با مهارت «لیزر آلکساندرایت» در شعبهٔ «مرکزی» موجود نیست» (بند ۱۰). --- ## سه قرارداد **۱. سرویس بدون الگوی بخش، همان رفتار امروز را می‌گیرد.** یک بخش پیوسته به اندازهٔ کل مدت که منبعِ `type=doctor` را می‌گیرد. بدون این، هر سرویس موجود بی‌برنامه می‌شد. **۲. بخش بدون هیچ نیازمندی معتبر است.** «انتظار در خانه» زمان می‌گیرد ولی هیچ منبعی اشغال نمی‌کند. **۳. قید جنسیت وقتی جنسیت بیمار نامشخص است نادیده گرفته نمی‌شود.** ۴۲۲ می‌دهد، چون رد کردن بی‌صدا یعنی بیمار به منبعی می‌رسد که قرار نبود. `mergeable` هم‌نام‌ها یک بار می‌آیند: «آماده‌سازی» با دو ناحیه یک بار انجام می‌شود. تکرار شدنِ کارِ اصلی با `duration_source: "items"` بیان می‌شود، نه با تکرار بخش. --- ## طبقه‌بندی محیط | جدول | وضعیت | |---|---| | `segment_templates` | جفت محیط (از بخشِ سرویس مشتق می‌شود) | | `segment_requirements` | `AGGREGATE_CHILDREN` — ریشه `SegmentTemplate` | ## تست‌ها ```bash ddev exec php bin/phpunit tests/Appointment/AppointmentPlanTest.php # ۱۱ تست ```