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:
hamed
2026-07-30 11:43:58 +03:30
parent 1d338503c8
commit 021d0eb6b2
62 changed files with 8098 additions and 0 deletions
@@ -0,0 +1,95 @@
# دیتابیس — تسک ۰۵
## `segment_templates`
| ستون | نوع | توضیح |
|---|---|---|
| `id` | INT PK AI | |
| `uuid` | VARCHAR(36) UNIQUE | از request می‌آید |
| `entity_type` / `entity_id` | VARCHAR(10) / INT NOT NULL | |
| `owner_type` | VARCHAR(10) NOT NULL | `service` \| `option` |
| `service_item_id` | INT NULL | FK → `service_items.id` ON DELETE CASCADE |
| `service_option_id` | INT NULL | FK → `service_options.id` ON DELETE CASCADE |
| `name` | VARCHAR(150) NOT NULL | |
| `segment_type` | VARCHAR(40) NOT NULL | کلید ادغام |
| `sequence` | SMALLINT NOT NULL | |
| `fixed_minutes` | SMALLINT NULL | |
| `duration_share` | SMALLINT NULL | درصد ۰..۱۰۰ |
| `patient_present` | TINYINT(1) NOT NULL DEFAULT 1 | |
| `mergeable` | TINYINT(1) NOT NULL DEFAULT 0 | |
| `active` | TINYINT(1) NOT NULL DEFAULT 1 | |
| `created_at`/`updated_at` | INT NOT NULL | |
```sql
KEY idx_seg_tpl_tenant (entity_type, entity_id, active)
KEY idx_seg_tpl_service (service_item_id, sequence)
KEY idx_seg_tpl_option (service_option_id, sequence)
```
قیدهای اپلیکیشنی (در سازنده/سرویس، نه `CHECK`):
- دقیقاً یکی از `service_item_id` / `service_option_id` غیر-NULL و با `owner_type` سازگار
- دقیقاً یکی از `fixed_minutes` / `duration_share` غیر-NULL
- جمع `duration_share` بخش‌های یک سرویس = ۱۰۰ (اگر حداقل یکی سهمی باشد)
## `segment_requirements`
| ستون | نوع | توضیح |
|---|---|---|
| `id` | INT PK AI | |
| `uuid` | VARCHAR(36) UNIQUE | |
| `segment_template_id` | INT NOT NULL | FK ON DELETE CASCADE |
| `resource_type_id` | INT NOT NULL | FK → `resource_types.id` ON DELETE RESTRICT |
| `count` | SMALLINT NOT NULL DEFAULT 1 | |
| `resource_pool_id` | INT NULL | FK → `resource_pools.id` ON DELETE SET NULL |
| `specific_resource_id` | INT NULL | FK → `clinic_resources.id` ON DELETE SET NULL |
| `required_skills` | JSON NULL | آرایهٔ `skill_id` |
| `constraints` | JSON NULL | فهرست بسته — جدول architecture |
| `occupancy` | VARCHAR(10) NOT NULL DEFAULT 'exclusive' | `exclusive`\|`shared`\|`passive` |
| `sort_order` | SMALLINT NOT NULL DEFAULT 0 | |
```sql
KEY idx_seg_req_segment (segment_template_id, sort_order)
KEY idx_seg_req_pool (resource_pool_id)
```
فرزند aggregate با ریشهٔ `SegmentTemplate` — uuid از request فقط در
`PUT /segment-template/{uuid}/requirements` می‌آید که خودش از ریشه لنگر می‌خورد،
پس ستون tenant لازم ندارد.
> `required_skills` عمداً JSON است نه جدول واسط: همیشه کامل خوانده و کامل جایگزین می‌شود،
> و هیچ کوئری‌ای از سمت مهارت به نیازمندی نمی‌رود. جدول واسط اینجا فقط سه JOIN اضافه
> به مسیر داغ تسک ۰۶ می‌آورد.
## هیچ جدول جدیدی برای «برنامهٔ ساخته‌شده» نیست
`AppointmentPlan` یک DTO درون‌حافظه‌ای است، نه entity. ذخیره‌اش وقتی معنی پیدا می‌کند که
نوبت ثبت شود — که کار تسک ۰۷ است (`appointment_segments`).
دلیل: برنامه یک تابع خالص از (سرویس، آیتم‌ها، بیمار، شعبه، قوانین فعال) است. ذخیره‌کردنش
پیش از ثبت یعنی نگه‌داشتن حالت موقتی که باید منقضی شود — همان مسئله‌ای که رزرو موقت
تسک ۰۷ حل می‌کند و دو مکانیزم موازی لازم نیست.
## Migration
```bash
ddev exec php bin/console doctrine:migrations:diff --no-interaction
ddev exec php bin/console doctrine:migrations:migrate --no-interaction
ddev exec php bin/console app:segment:seed-templates --preset=beauty --tenant=clinic:12 --force
```
`app:segment:seed-templates` سه پریست دارد (مستند بند ۱۷: «الگوی آماده برای هر نوع کلینیک»):
| پریست | بخش‌ها |
|---|---|
| `beauty` | آماده‌سازی ۵ · انتظار ۳۰ (فقط اتاق) · درمان (سهمی ۱۰۰) · مراقبت ۵ |
| `dental` | آماده‌سازی ۵ · درمان (سهمی ۱۰۰) · تمیزکاری یونیت ۱۰ (بدون حضور بیمار) |
| `physio` | درمان (سهمی ۱۰۰) |
dry-run پیش‌فرض. پریست‌ها روی سرویس‌های موجود اعمال نمی‌شوند مگر با `--service=<uuid>`.
## طبقه‌بندی tenant
| جدول | وضعیت |
|---|---|
| `segment_templates` | جفت tenant |
| `segment_requirements` | `AGGREGATE_CHILDREN` → ریشه `SegmentTemplate` |