# نکات پیاده‌سازی — تسک ۰۲

## ۱. ظرفیت: یک ردیف با ظرفیت ۳، نه سه ردیف

مستند بند ۶ صریح است و دلیلش در تسک ۰۶ روشن می‌شود: با سه ردیف، موتور جستجو باید سه
تقویم را ادغام کند و «کدام تخت» بشود یک تصمیم که هیچ‌کس نمی‌خواهد بگیرد. با ظرفیت ۳،
شرط اشغال یک شمارش ساده است:

```
SELECT COUNT(*) FROM resource_occupancy
WHERE resource_id = ? AND [بازه‌ها تداخل دارند] AND status IN (…)
→ اگر < capacity، جا هست
```

این را همین حالا در `docs/api/resource.md` بنویس تا کسی وسوسه نشود «اتاق ۱ تخت ۲» بسازد.

## ۲. `ClinicResource` نه `Resource`

نام کلاس `Resource` در PHP مشکل ندارد ولی در این کدبیس با `App\Shared\…` و مفهوم
«منبع API» قاطی می‌شود و در جستجوی کد نویز شدیدی می‌سازد. نام جدول `clinic_resources`
هم به همان دلیل. متغیرها و متدها می‌توانند `$resource` باشند.

## ۳. پل‌ها و `active`

`ClinicStaff.active = false` باید `ClinicResource.active` را هم false کند، وگرنه پرسنل
غیرفعال همچنان در جستجوی وقت ظاهر می‌شود. این را در `StaffService` (تسک موجود) با یک
فراخوانی به `ResourceLinker::syncActive()` انجام بده — نه با Doctrine lifecycle callback،
چون callback در `getArrayResult()` اجرا نمی‌شود و رفتار نامتقارن می‌سازد.

عکسش برقرار نیست: غیرفعال کردن منبع، پرسنل را غیرفعال نمی‌کند (پرسنل ممکن است فقط
اداری باشد).

## ۴. `setup_minutes` / `cleanup_minutes` چه هستند و چه نیستند

- **جزو نوبت بیمار نیستند** — بیمار ساعت ۱۰:۰۰ می‌آید و ۱۰:۳۰ می‌رود
- **منبع را اشغال می‌کنند** — یونیت از ۹:۵۵ تا ۱۰:۴۰ در دسترس نیست

پس در تسک ۰۷ بازهٔ ثبت‌شده در `resource_occupancy` گسترده‌تر از بازهٔ نوبت است. الان فقط
ستون را بساز و در `docs/api/resource.md` این تفاوت را بنویس؛ محاسبه‌اش کار تسک ۰۶ است.

اشتباه رایج: این را با `WeeklySchedule.meta.buffer_minutes` موجود یکی گرفتن. آن یکی
فاصلهٔ سراسری بین دو نوبتِ **پزشک** است؛ این یکی per منبع است. تا وقتی حالت `resource`
نیامده، هر دو کنار هم زندگی می‌کنند و `buffer_minutes` دست‌نخورده می‌ماند.

## ۵. مهارت را با `jobTitle` قاطی نکن

`ClinicStaff.jobTitle` متن آزاد و برای نمایش است. مهارت یک موجودیت با هویت است که در
شرط نیازمندی (تسک ۰۵) و شرط قانون (تسک ۰۹) استفاده می‌شود. هیچ‌جا `jobTitle` را برای
تصمیم‌گیری parse نکن.

## ۶. edge case ها

| حالت | رفتار درست |
|---|---|
| منبع بدون هیچ پل (دستگاه) | معتبر — حالت عادی تجهیزات |
| دو پل هم‌زمان (`doctor_id` و `staff_id`) | `422` در سازنده |
| حذف `resource_type` که منبع دارد | `422` |
| حذف `resource_type` با `is_system=1` | `422` همیشه |
| غیرفعال کردن منبعی که نوبت آیندهٔ فعال دارد | مجاز، ولی پاسخ شامل `warnings[]` با تعداد نوبت‌ها |
| استخر با صفر عضو | معتبر (در حال ساخت)، ولی تسک ۰۶ آن را «هیچ منبعی» می‌بیند |
| `capacity` روی منبع `type=doctor` بزرگ‌تر از ۱ | `422` — پزشک هم‌زمان دو بیمار ندارد |
| `level` خارج از ۱..۵ | `422` |
| مهارت محیط A روی منبع محیط B | `404` (پیش از هر بررسی: `TenantOwnershipChecker`) |

## ۷. کارایی

پرس‌وجوی داغ تسک ۰۶: «منابع فعالِ شعبهٔ X از نوع Y که مهارت Z را دارند».

```php
// ClinicResourceRepository::findEligible()
// یک کوئری با JOIN به resource_skills، بدون N+1
$qb->select('r')
   ->from(ClinicResource::class, 'r')
   ->join('r.skills', 'rs')
   ->where('r.branch = :branch')
   ->andWhere('r.type = :type')
   ->andWhere('r.active = true')
   ->andWhere('rs.skill IN (:skills)')
   ->groupBy('r.id')
   ->having('COUNT(DISTINCT rs.skill) = :skillCount');   // همهٔ مهارت‌ها، نه یکی
```

`HAVING COUNT(DISTINCT …)` عمدی است: نیازمندی «مهارت الف و ب» یعنی هر دو، نه یکی.

## ۸. تست

```
tests/Resource/ResourceCrudTest.php
  - ساخت منبع + خواندن → tenant و branch درست
  - منبع با شعبهٔ محیط دیگر → 404
  - capacity=0 → 422 · capacity=2 روی type=doctor → 422
  - دو پل هم‌زمان → 422
tests/Resource/SkillAssignmentTest.php
  - PUT skills جایگزینی کامل (حذف نداده‌ها)
  - level خارج بازه → 422
  - حذف مهارتِ در استفاده → 422
tests/Resource/ResourcePoolTest.php
  - عضو از شعبهٔ دیگر → 422
  - عضو از نوع دیگر → 422
tests/Resource/ResourceEligibilityTest.php
  - findEligible با دو مهارت: منبعی که فقط یکی را دارد برنمی‌گردد
tests/Resource/BackfillResourceTest.php
  - idempotent: دو بار اجرا = یک بار
tests/Shared/TenantSchemaCoverageTest.php
tests/Shared/TenantLookupInventoryTest.php   ← شمارنده به‌روز شود
```

## ۹. مستندات

`docs/api/resource.md` بساز. در `docs/api/staff.md` یک بخش «رابطه با منبع» اضافه کن.
`docs/architecture/tenancy.md` جدول طبقه‌بندی را به‌روز کن.
