feat: implement cancellation policy, no-show tracking, and waitlist management
- Add implementation notes for cancellation and waitlist features. - Create task documentation outlining goals, current status, and acceptance criteria for cancellation policy and resource utilization reporting. - Establish architecture for domain events and outbox pattern to ensure reliable event publishing. - Define database schema for domain events and necessary queries for resource utilization and plan accuracy reports. - Implement detailed implementation notes covering edge cases, testing strategies, and documentation requirements.
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
# تسک ۰۵ — بخشهای نوبت و سازندهٔ برنامه
|
||||
|
||||
**فاز:** ۱ (هسته) · **وابستگی:** ۰۲، ۰۴ · **زمان:** ۱۶-۲۰ ساعت
|
||||
|
||||
---
|
||||
|
||||
## هدف
|
||||
|
||||
مهمترین بخش مستند (بند ۷). یک نوبت یک تکه زمان پیوسته نیست:
|
||||
|
||||
| بخش | مدت | اتاق | اپراتور | دستگاه |
|
||||
|---|---|---|---|---|
|
||||
| مالیدن کرم بیحسی | ۵ | اشغال | اشغال | آزاد |
|
||||
| انتظار اثر کرم | ۳۰ | اشغال | **آزاد** | آزاد |
|
||||
| خود لیزر | ۲۰ | اشغال | اشغال | اشغال |
|
||||
| مراقبت بعد | ۵ | اشغال | اشغال | آزاد |
|
||||
|
||||
با مدل امروز اپراتور ۶۰ دقیقه قفل میشود در حالی که ۳۰ دقیقه کار میکند. نصف ظرفیت
|
||||
هدر میرود.
|
||||
|
||||
## وضعیت فعلی
|
||||
|
||||
```php
|
||||
// src/Appointment/Entity/Appointment.php
|
||||
private int $slotStart; // یک بازهٔ پیوسته
|
||||
private int $slotEnd;
|
||||
```
|
||||
|
||||
هیچ مفهومی از بخش، و هیچ نیازمندی منبعی وجود ندارد. تنها منبعی که تداخلش بررسی میشود
|
||||
پزشک است (`AppointmentRepository::isSlotTaken`).
|
||||
|
||||
## دامنه
|
||||
|
||||
**هست:**
|
||||
- `SegmentTemplate` — الگوی بخشهای یک سرویس (و بخشهای اضافهٔ هر `ServiceOption`)
|
||||
- `SegmentRequirement` — نیازمندی منبع هر بخش: نقش، تعداد، شرط مهارت، قید، نوع اشغال
|
||||
- `AppointmentPlanBuilder` — از (سرویس، آیتمها، بیمار، شعبه) یک **برنامهٔ نوبت** میسازد
|
||||
- ادغام بخشهای همنوع، محاسبهٔ مدت هر بخش، چیدمان ترتیبی
|
||||
- `GET /api/v1/appointment-plan/preview` برای دیدن برنامه پیش از جستجوی وقت
|
||||
|
||||
**نیست:** پیدا کردن منابع آزاد و زمان (تسک ۰۶)، ثبت اشغال (تسک ۰۷)،
|
||||
اعمال قوانین روی برنامه (تسک ۰۹ — نقطهٔ اتصالش اینجا آماده میشود).
|
||||
|
||||
## Endpoint ها
|
||||
|
||||
| متد | مسیر | توضیح |
|
||||
|---|---|---|
|
||||
| GET/PUT | `/api/v1/service-item/{uuid}/segments` | الگوی بخشهای سرویس |
|
||||
| PUT | `/api/v1/segment-template/{uuid}/requirements` | نیازمندیهای منبع یک بخش |
|
||||
| POST | `/api/v1/appointment-plan/preview` | ساخت و برگرداندن برنامهٔ نوبت (بدون ثبت) |
|
||||
|
||||
## خروجی `POST /appointment-plan/preview`
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"total_minutes": 60,
|
||||
"segments": [
|
||||
{ "sequence": 1, "name": "بیحسی موضعی", "offset_minutes": 0, "duration_minutes": 5,
|
||||
"patient_present": true,
|
||||
"requirements": [
|
||||
{ "role": "room", "count": 1, "occupancy": "exclusive", "candidates": 3 },
|
||||
{ "role": "operator", "count": 1, "occupancy": "exclusive", "candidates": 2 }
|
||||
] },
|
||||
{ "sequence": 2, "name": "انتظار", "offset_minutes": 5, "duration_minutes": 30,
|
||||
"patient_present": true, "mergeable": true,
|
||||
"requirements": [ { "role": "room", "count": 1, "occupancy": "exclusive", "candidates": 3 } ] }
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`candidates` تعداد منابع واجد شرایط است — اگر صفر باشد، برنامه ساخته نمیشود و خطای
|
||||
انسانی برمیگردد: «هیچ اپراتور خانمی با مهارت لیزر در این شعبه نیست» (مستند بند ۱۰).
|
||||
|
||||
## معیار پذیرش
|
||||
|
||||
- ✅ موفق: سرویس «لیزر» با چهار بخش بالا تعریف میشود؛ `preview` برنامهای با
|
||||
`total_minutes = 60` و چهار بخش با `offset_minutes` صحیح (۰، ۵، ۳۵، ۵۵) برمیگرداند.
|
||||
- ✅ موفق: انتخاب دو ناحیه (صورت + بیکینی) → بخش «آمادهسازی» **یک بار** میآید
|
||||
(`mergeable=true` همنوعها ادغام میشوند) ولی بخش «لیزر» مدتش با `DurationCalculator`
|
||||
تسک ۰۴ محاسبه شده است.
|
||||
- ✅ موفق: سرویسی که هیچ `SegmentTemplate` ندارد → برنامهای با **یک بخش** برابر کل مدت
|
||||
و نیازمندی پیشفرض (منبع `type=doctor`). این همان رفتار امروز است.
|
||||
- ❌ خطا: نیازمندی با مهارتی که هیچ منبعی در آن شعبه ندارد →
|
||||
`422` با `ERR_NO_ELIGIBLE_RESOURCE` و پیام فارسی شامل نقش و مهارت.
|
||||
- ❌ خطا: بخش با `duration_minutes <= 0` و بدون منبع مدت پویا → `422`.
|
||||
- ⚠️ مرزی: بخش با `requirements = []` (مثلاً «انتظار در خانه») → معتبر؛ زمان میگیرد،
|
||||
هیچ منبعی نمیگیرد.
|
||||
- ⚠️ مرزی: قید `same_gender_as_patient` وقتی جنسیت بیمار نامشخص است → نیازمندی نادیده
|
||||
گرفته **نمیشود**؛ `422` با پیام «برای این سرویس ثبت جنسیت بیمار الزامی است».
|
||||
- ⚠️ مرزی: `setup/cleanup` منبع در `preview` **نمایش داده نمیشود** ولی در
|
||||
`occupancy_offset` هر نیازمندی میآید تا تسک ۰۶ همان را استفاده کند.
|
||||
- ⚠️ مرزی: مجموع مدت بخشها بیشتر از ۸ ساعت → `422` (حفاظت از جستجوی وقت).
|
||||
|
||||
## خروجی
|
||||
|
||||
- `src/Appointment/Plan/` — entity ها، `AppointmentPlanBuilder`، DTO ها
|
||||
- `assets/admin/pages/ServiceSegmentsPage.tsx` + پیشنمایش برنامه
|
||||
- `docs/api/appointment-plan.md`
|
||||
- الگوهای آماده: `app:segment:seed-templates --preset=beauty|dental|physio` (مستند بند ۱۷)
|
||||
Reference in New Issue
Block a user