feat(resource): every resource is supervised by a doctor
Supervision now lives on the resource itself instead of being asked for again at booking time, so one relation answers it everywhere. The column is deliberately separate from the existing doctor_id bridge. That bridge means "this resource IS this doctor" and isPerson() uses it to pin capacity at 1; a supervised three-seat device must not become a person resource. The FK is SET NULL rather than CASCADE because deleting a doctor should not take the clinic's laser with it. Required on create and non-clearable on update, enforced in the API where it can give a Persian message. Ownership is checked through Clinic::hasDoctor so a secretary cannot put their device under a doctor of another clinic; that returns 404, not 403, keeping foreign data invisible. The 13 existing resources are backfilled deterministically: a practice resource gets its own doctor, a clinic resource gets that clinic's first doctor. Both are editable from the resource form. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -178,6 +178,7 @@
|
||||
|---|---|---|---|
|
||||
| `address_uuid` | string | ✅ | محل نوبتدهی؛ جفت محیطِ منبع **از همین** مشتق میشود، نه از بدنه |
|
||||
| `type_uuid` | string | ✅ | |
|
||||
| `supervisor_doctor_uuid` | string | ✅ | پزشکِ ناظرِ منبع. باید پزشکِ همین محیط باشد وگرنه ۴۰۴ |
|
||||
| `name` | string | ✅ | حداکثر ۱۵۰ نویسه |
|
||||
| `capacity` | int | — | پیشفرض ۱، حداقل ۱؛ روی منبعِ شخص حداکثر ۱ |
|
||||
| `setup_minutes` | int | — | پیشفرض ۰، بازهٔ ۰..۴۸۰ |
|
||||
@@ -185,6 +186,36 @@
|
||||
| `attributes` | object | — | حداکثر ۲۰ کلید · کلید `[a-z_]{1,40}` · مقدار فقط اسکالر |
|
||||
| `active` | bool | — | پیشفرض `true` |
|
||||
|
||||
### پزشک ناظر (2026-08)
|
||||
|
||||
هر منبع **الزاماً** زیر نظر یک پزشک است؛ صفحهٔ نوبتها تب منابع را زیر همان پزشک
|
||||
میچیند، پس منبعِ بیناظر جایی برای دیدهشدن ندارد.
|
||||
|
||||
- `supervisor_doctor_uuid` در ساخت الزامی است → نبودش `422` با
|
||||
`field: supervisor_doctor_uuid` و پیام «انتخاب پزشک ناظر الزامی است».
|
||||
- در `PATCH` **اختیاری** است، ولی اگر بیاید نمیتواند خالی باشد (برداشتن ناظر ممنوع).
|
||||
- پزشکِ خارج از محیط → `404` («پزشک ناظر یافت نشد») — وجود دادهٔ محیط بیگانه لو نمیرود.
|
||||
- پاسخها فیلد `supervisor` را به شکل `{uuid, name}` برمیگردانند (یا `null` برای
|
||||
ردیفهایی که پزشکشان حذف شده — کلید خارجی `SET NULL` است).
|
||||
|
||||
**ناظر با پلِ منبع فرق دارد.** `subject_kind`/`doctor_id` یعنی «این منبع خودِ همان
|
||||
پزشک است» و `isPerson()` بر پایهاش ظرفیت را به ۱ قفل میکند. ناظرِ یک دستگاهِ
|
||||
سهظرفیتی نباید آن را به منبعِ انسانی تبدیل کند، پس ستون جداست.
|
||||
|
||||
خروجی واقعی `GET /api/v1/resources?active=1`:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "اتاق ۱",
|
||||
"supervisor": { "uuid": "631e81d8-0009-4e01-a40f-3029905a3f27", "name": "امیر کاظمی" },
|
||||
"subject_kind": null,
|
||||
"capacity": 1
|
||||
}
|
||||
```
|
||||
|
||||
**backfill:** ۱۳ منبعِ موجود در migration ناظر گرفتند — منبعِ مطب → همان پزشک، منبعِ
|
||||
کلینیک → پزشکِ اولِ همان کلینیک. قابل تغییر از فرم ویرایش منبع.
|
||||
|
||||
`attributes` عمداً آزاد است — کلید ناشناخته پذیرفته میشود — ولی مقدارش باید ساده
|
||||
باشد. دلیل: تسک ۰۵ قید `same_gender` و تسک ۰۹ شرطهای منبع را با مقایسهٔ ساده روی
|
||||
همین مقادیر میسنجند؛ آرایهٔ تودرتو یعنی مقایسهٔ دلخواه، همان چیزی که بند ۸ مستند
|
||||
|
||||
Reference in New Issue
Block a user