# تسک ۰۱ — شعبه و اتاق **فاز:** ۱ (هسته) · **وابستگی:** ۰۰ · **زمان:** ۶-۸ ساعت (بازتخمین — رجوع به بخش «تصحیح طرح») --- ## ⛔ تصحیح طرح (اجرای ۱۴۰۵/۰۵/۰۸) نسخهٔ اول این تسک یک جدول `branches` تازه می‌خواست و `doctor_addresses.branch_id` را به آن وصل می‌کرد. **این طرح غلط بود: «شعبه» از قبل وجود دارد و نامش `DoctorAddress` است.** | `branches` پیشنهادی | واقعیت `doctor_addresses` | |---|---| | `name` · `phone` · `address` | ✅ `name` · `telephone` · `address` | | `city_id` · `province_id` | ✅ **FK به entity `City`/`Province`** — بهتر از ارجاع خام به `categories` که طرح اول می‌خواست | | `latitude` · `longitude` | ✅ هر دو | | جفت tenant | ⚠️ `DoctorAddress::forDoctor(Doctor)` / `forClinic(int $clinicId)` + ستون `type` — همان اطلاعات، با شکل دیگر | | `timezone` | ❌ غایب | | `active` | ❌ غایب | | ساعت کاری | ❌ غایب — **واقعاً جدید** | | اتاق | ❌ غایب — **واقعاً جدید** | و این‌ها هم از قبل هستند: - `WeeklySchedule.setting[day].sessions[].location_id` → `doctor_addresses.id` - `GET /api/v1/appointment-booking-locations/{doctorUuid}` هر آدرس را «محل نوبت‌دهی» می‌نامد - `DoctorAddressRepository::findForContext($doctor, $clinicId)` چند آدرس per محیط می‌دهد - **۹ endpoint CRUD**: `clinic/{uuid}/addresses` (۴ عدد) و `clinic-pro/doctor-address*` (۵ عدد) - UI ادمین در `ClinicDetailPage`، `ClinicFormPage`، `DoctorDetailPage`، `SettingsPage` ساختن `branches` بالای این یعنی: دو جدول برای یک مکان فیزیکی، دو منبع حقیقت برای نام/آدرس/تلفن/مختصات، هر مصرف‌کننده باید تصمیم بگیرد کدام را بخواند، و branchی که `location_id` هرگز به آن اشاره نمی‌کند. نقض قاعدهٔ #۸ پروژه («اول بگرد، بعد توسعه بده، در آخر بساز»). **پس در این تسک هیچ جدول `branches` ساخته نمی‌شود و هیچ endpoint CRUD شعبه اضافه نمی‌شود.** --- ## هدف سه چیزِ واقعاً غایب را اضافه کن تا تسک‌های ۰۲ و ۰۳ جایی برای نشستن داشته باشند: ۱. `active` و `timezone` روی `DoctorAddress` — یک شعبهٔ بسته باید بتواند بسته شود، و بند ۹ مستند ذخیره‌سازی UTC با نمایش محلی می‌خواهد. ۲. **ساعت کاری هفتگی شعبه** — امروز ساعت کاری فقط روی برنامهٔ پزشک است. تسک ۰۳ برای کسر لایه‌ها به ساعت کاری شعبه نیاز دارد. ۳. **اتاق** — با ظرفیت هم‌زمان. تسک ۰۲ اتاق را به‌عنوان یک `resource_type` منعکس می‌کند. ## دامنه **هست:** دو ستون روی `DoctorAddress` · جدول و entity `BranchWorkingHours` · جدول و entity `Room` · سرویس اعتبارسنجی ساعت · endpoint ساعت کاری و CRUD اتاق · UI ادمین برای هر دو. **نیست:** جدول `branches` (رد شد) · CRUD شعبه (موجود) · استفاده از ساعت شعبه در محاسبهٔ اسلات (تسک ۰۳) · اتاق به‌عنوان منبع قابل رزرو (تسک ۰۲). ## Endpoint ها | متد | مسیر | توضیح | |---|---|---| | GET | `/api/v1/branch/{addressUuid}/working-hours` | ساعت کاری هفتگی یک شعبه | | PUT | `/api/v1/branch/{addressUuid}/working-hours` | جایگزینی کامل هفت روز | | GET | `/api/v1/branch/{addressUuid}/rooms` | اتاق‌های شعبه | | POST | `/api/v1/room` | ساخت اتاق | | PATCH | `/api/v1/room/{uuid}` | ویرایش | | DELETE | `/api/v1/room/{uuid}` | حذف (فقط بدون منبع فعال — گارد در تسک ۰۲ تکمیل می‌شود) | `{addressUuid}` همان uuid رکورد `doctor_addresses` است. «شعبه» و «آدرس» یک چیزند. ## معیار پذیرش - ✅ موفق: کلینیک `PUT /branch/{uuid}/working-hours` با هفت روز می‌فرستد → `200`؛ `GET` همان ساختار را با کلیدهای `0..6` (۰=شنبه، همان قرارداد `SlotCalculatorService`) برمی‌گرداند. - ✅ موفق: `POST /api/v1/room` با `address_uuid` و `capacity: 3` → `201`، و جفت tenant اتاق **از آدرس مشتق** می‌شود نه از بدنهٔ درخواست. - ✅ موفق: `active` پیش‌فرض `true` و `timezone` پیش‌فرض `Asia/Tehran` — هیچ آدرس موجودی رفتارش عوض نمی‌شود. - ❌ خطا: آدرس محیط دیگر → `404` (رفتار `TenantFilter`، نه ۴۰۳). - ❌ خطا: ساعت با `end_minute <= start_minute` → `422`. - ❌ خطا: دو بازهٔ هم‌پوشان در یک روز → `422`. - ❌ خطا: `capacity <= 0` → `422`. - ⚠️ مرزی: بازهٔ شبانه‌روزی `0..1440` → `200` (یک ردیف، نه دو). - ⚠️ مرزی: روز بدون هیچ بازه → معتبر، یعنی شعبه آن روز بسته است. - ⚠️ مرزی: شعبهٔ **بدون هیچ ساعت کاری** → «تعریف‌نشده»، نه «همیشه‌باز». تسک ۰۳ در این حالت به رفتار فعلی برمی‌گردد (برنامهٔ پزشک تنها مرجع). این تصمیم باید در `docs/api/branch.md` نوشته شود. - ⚠️ مرزی: `PUT` با آرایهٔ خالی → همهٔ ساعت‌های آن شعبه پاک می‌شوند (بستنِ کامل شعبه). ## خروجی - `src/Branch/` — `BranchWorkingHours`، `Room`، سرویس‌ها، کنترلرها - دو ستون روی `DoctorAddress` + migration - `assets/admin/pages/BranchWorkingHoursPage.tsx` + `BranchRoomsPage.tsx` - `docs/api/branch.md` - [checklist.md](checklist.md) کامل‌شده