# تسک ۰۱ — شعبه و اتاق

**فاز:** ۱ (هسته) · **وابستگی:** ۰۰ · **زمان:** ۶-۸ ساعت (بازتخمین — رجوع به بخش «تصحیح طرح»)

---

## ⛔ تصحیح طرح (اجرای ۱۴۰۵/۰۵/۰۸)

نسخهٔ اول این تسک یک جدول `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) کامل‌شده
