# «شعبه» جدول تازه‌ای نیست — `doctor_addresses` است

**این سند بر همهٔ تسک‌هایی که `branches` یا `branch_id` می‌گویند حاکم است.**
نسخهٔ اول تسک ۰۱ یک جدول `branches` طراحی کرده بود؛ در اجرا معلوم شد آن موجودیت از قبل
وجود دارد. تسک ۰۱ اصلاح شد و جدول ساخته **نشد**.

هر جا در تسک‌های ۰۲، ۰۴، ۰۷، ۰۸، ۰۹، ۱۰، ۱۳ نوشته شده `branch_id INT NOT NULL FK →
branches(id)`، بخوانید:

```sql
address_id INT NOT NULL   -- FK → doctor_addresses(id)
```

و هر جا `Branch $branch` نوشته شده، بخوانید `DoctorAddress $address`.

---

## چرا

`App\Doctor\Entity\DoctorAddress` تمام چیزی است که یک شعبه لازم دارد:

| نیاز شعبه | در `DoctorAddress` |
|---|---|
| نام | `name` |
| آدرس | `address` |
| تلفن | `telephone` |
| مختصات | `latitude` / `longitude` |
| شهر و استان | FK به entity `City` / `Province` |
| مالک (محیط) | `forDoctor(Doctor)` یا `forClinic(int $clinicId)` + ستون `type` |
| فعال/غیرفعال | `active` — **تسک ۰۱ اضافه کرد** |
| منطقهٔ زمانی | `timezone` — **تسک ۰۱ اضافه کرد** |

و از قبل در کل سیستم به همین معنا مصرف می‌شود:

- `WeeklySchedule.setting[day].sessions[].location_id` → `doctor_addresses.id`
- `SlotCalculatorService::buildSessionSlots()` آن را در هر اسلات کپی می‌کند
- `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`

ساختن جدول موازی یعنی دو منبع حقیقت برای نام/آدرس/تلفن/مختصات یک مکان فیزیکی، و شعبه‌ای
که `location_id` هرگز به آن اشاره نمی‌کند — یعنی decorative. قاعدهٔ #۸ پروژه:
«API/جدول جدید فقط وقتی هیچ موجودی — حتی با توسعه — کافی نباشد.»

---

## پیامد برای تسک‌های بعدی

| تسک | چه چیزی عوض می‌شود |
|---|---|
| ۰۲ منابع | `clinic_resources.address_id` → `doctor_addresses(id)`. `ResourcePool` هم همین. «منابع مال شعبه‌اند» = مال یک آدرس‌اند |
| ۰۳ تقویم منبع | ساعت کاری شعبه از `branch_working_hours` که به `doctor_addresses.id` کلید می‌خورد |
| ۰۴ کاتالوگ | `service_branch_overrides.address_id` |
| ۰۷ رزرو | `appointments.address_id` **از قبل وجود دارد** (`Appointment::$addressId`) — ستون جدید لازم نیست |
| ۰۸ قیمت | `price_lists.address_id` |
| ۰۹/۱۰ قوانین | `policies.address_id` (اختصاصی‌بودن per شعبه) |
| ۱۳ لغو/انتظار | `waitlist_entries.address_id` |

⚠️ نکتهٔ تسک ۰۷: `Appointment` از قبل `address_id` دارد (ستون `addressId`, تهی‌پذیر) و
`SlotCalculatorService::resolveSlotLocationId()` پرش می‌کند. پس آنجا هم ستون تازه لازم نیست.

---

## جفت tenant اتاق و منابع

`DoctorAddress` ستون‌های `entity_type`/`entity_id` ندارد؛ مالکیتش با `type` +
`doctor_id`/`clinic_id` بیان می‌شود. موجودیت‌های جدیدی که به آدرس کلید می‌خورند و
`TenantOwnedTrait` دارند، جفتشان را در **سازنده از آدرس مشتق** می‌کنند:

```php
// App\Branch\Entity\Room::__construct()
$this->assignTenantPair(
    $address->getType() === DoctorAddress::TYPE_CLINIC ? 'clinic' : 'doctor',
    $address->getType() === DoctorAddress::TYPE_CLINIC
        ? (int) $address->getClinicId()
        : (int) $address->getDoctor()->getId(),
);
```

همان قاعدهٔ `docs/architecture/tenancy.md`: جفت در سازنده از ریشه مشتق می‌شود، نه از
ورودی درخواست — پس هیچ نقطهٔ ساختی نمی‌تواند فراموشش کند و write-once می‌ماند.

---

## به‌روزرسانی پس از تسک ۰۲

منابع ساخته شدند و `clinic_resources.address_id` و `resource_pools.address_id` هر دو
به `doctor_addresses(id)` می‌خورند — همان‌طور که جدول بالا پیش‌بینی کرده بود.

یک تصحیح **اضافه** روی همان جدول: کلید یکتای منبعِ پزشک
`(doctor_id, address_id)` است نه `(doctor_id)`. یک `WeeklySchedule` per جفت
(پزشک، کلینیک) است ولی هر شیفتِ درونش `location_id` خودش را دارد، پس یک پزشک از قبل
در چند آدرسِ یک محیط کار می‌کند. همین برای `(staff_id, address_id)` هم صادق است.
`rooms` استثناست: اتاق ذاتاً در یک آدرس است، پس `UNIQUE(room_id)` کافی است.

تسک‌های ۰۴/۰۷/۰۸/۰۹/۱۰/۱۳ که هنوز `branch_id` می‌گویند، همین الگو را دنبال کنند.
