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,61 @@
# تسک ۰۱ — شعبه (Branch) و اتاق (Room)
**فاز:** ۱ (هسته) · **وابستگی:** — · **زمان:** ۱۰-۱۲ ساعت
---
## هدف
سطح سوم مکان را به مدل اضافه کن: امروز `(entity_type, entity_id)` می‌گوید داده مال کدام
محیط است، ولی نمی‌گوید در کدام **ساختمان** و کدام **اتاق**. مستند بند ۴ سه سطح می‌خواهد
و «منابع همیشه مال شعبه‌اند چون فیزیکی‌اند» — بدون شعبه، تسک ۰۲ جایی برای نشستن ندارد.
## وضعیت فعلی
- محل مراجعه امروز `DoctorAddress` است و `location_id` هر شیفت در
`WeeklySchedule.setting[day].sessions[].location_id` به `doctor_addresses.id` اشاره می‌کند
(`SlotCalculatorService::buildSessionSlots()` آن را در هر اسلات کپی می‌کند).
- `Clinic` هیچ فیلد شعبه‌ای ندارد؛ یک آدرس متنی تخت دارد.
- ساعت کاری شعبه وجود ندارد — ساعت کاری فقط روی برنامهٔ پزشک است.
## دامنه
**هست:** entity های `Branch` و `Room`، ساعت کاری هفتگی شعبه، CRUD پنل ادمین،
پل زدن `DoctorAddress.branch_id` برای اینکه شیفت‌های موجود بدون تغییر به شعبه نگاشت شوند.
**نیست:** استفاده از شعبه در محاسبهٔ اسلات (تسک ۰۳)، اتاق به‌عنوان منبع قابل رزرو (تسک ۰۲).
## Endpoint ها
| متد | مسیر | توضیح |
|---|---|---|
| GET | `/api/v1/branches` | لیست شعب محیط جاری (paginated) |
| POST | `/api/v1/branch` | ساخت شعبه |
| GET | `/api/v1/branch/{uuid}` | جزئیات + ساعت کاری |
| PATCH | `/api/v1/branch/{uuid}` | ویرایش |
| DELETE | `/api/v1/branch/{uuid}` | حذف (فقط بدون منبع/اتاق فعال) |
| PUT | `/api/v1/branch/{uuid}/working-hours` | ثبت ساعت کاری هفتگی |
| GET | `/api/v1/branch/{uuid}/rooms` | اتاق‌های شعبه |
| POST/PATCH/DELETE | `/api/v1/room[/{uuid}]` | CRUD اتاق |
## معیار پذیرش
- ✅ موفق: کلینیک با توکن مالک `POST /api/v1/branch` می‌زند → `201` و شعبه با
`entity_type=clinic, entity_id=<id>` ثبت می‌شود. `GET /api/v1/branches` همان را برمی‌گرداند.
- ✅ موفق: `PUT /branch/{uuid}/working-hours` با هفت روز → `200`؛ `GET /branch/{uuid}` همان
ساختار را با کلیدهای `0..6` (۰=شنبه) برمی‌گرداند.
- ❌ خطا: کلینیک B با uuid شعبهٔ کلینیک A → `404` با `ERR_NOT_FOUND_001` (نه ۴۰۳ — طبق
رفتار `TenantFilter`).
- ❌ خطا: `DELETE` شعبه‌ای که اتاق فعال دارد → `422` با پیام فارسی «شعبه دارای اتاق فعال است».
- ⚠️ مرزی: پزشک مستقل (محیط `doctor`) هم می‌تواند شعبه بسازد — «مطب» یک شعبه است.
اولین شعبه از روی `DoctorAddress` موجود ساخته می‌شود، نه دستی.
- ⚠️ مرزی: ساعت کاری با `end_time <= start_time``422`.
- ⚠️ مرزی: شعبه بدون ساعت کاری معتبر است (وراثت: تسک ۰۳ آن را «همیشه باز» تفسیر نمی‌کند،
«تعریف‌نشده» تفسیر می‌کند).
## خروجی
- `src/Branch/` کامل با تست
- `assets/admin/pages/BranchesPage.tsx` + `BranchFormPage.tsx` + `RoomsPage.tsx`
- `docs/api/branch.md`
- migration + دستور `app:branch:backfill` برای ساخت شعبهٔ اولیه از آدرس‌های موجود