feat(treatment): per-case operators, shown on the list and searchable

A treatment case said which doctor supervised it but never who actually did the
work, so the list could not answer the first question a manager asks about a
course: who performed it.

Two separate things now travel with the case. `performed_by` is history —
derived from the sessions' performedBy, so it only ever reports what happened.
`assigned_staff` is plan — a new treatment_case_staff table, editable from the
modal, saying who is meant to handle this patient's course. The card shows the
first and falls back to the second while nothing has been performed yet.

Search matches both. A manager typing an operator's name wants that person's
work, and work already done is part of it.

Assignment also narrows the operator queue: a case with assigned staff shows its
sessions only to those people, because a patient who started a multi-session
course with one operator should keep them. An unassigned case keeps the existing
protocol rule, and an empty list means "anyone the protocol allows" rather than
"nobody" — the same "no rows is not a restriction" convention used elsewhere.

Unlike areas, removing an operator erases nothing: a finished session carries its
real operator on itself and never consults this list.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
hamed
2026-08-07 14:55:01 +03:30
co-authored by Claude Opus 5
parent 12c0d57e1a
commit b78f7311cf
13 changed files with 416 additions and 6 deletions
+13 -2
View File
@@ -209,7 +209,7 @@ single-session again. Idempotent: deleting a service that has no protocol still
| Query | توضیح |
|---|---|
| `status` | `active` \| `completed` \| `abandoned` — نبودش یعنی همه |
| `q` | جستجو روی نام بیمار، موبایل، کد ملی، شمارهٔ پرونده و نام سرویس |
| `q` | جستجو روی نام بیمار، موبایل، کد ملی، شمارهٔ پرونده، نام سرویس و **نام پرسنل** |
| `from` | `YYYY-MM-DD` میلادی — پرونده‌هایی که از ابتدای این روز به بعد باز شده‌اند |
| `to` | `YYYY-MM-DD` میلادی — تا انتهای این روز |
@@ -232,10 +232,19 @@ single-session again. Idempotent: deleting a service that has no protocol still
"mobile": "09120001111",
"record_number": "۱۲"
},
"areas": [ { "uuid": "…", "name": "بیکینی", "category_uuid": "…" } ]
"areas": [ { "uuid": "…", "name": "بیکینی", "category_uuid": "…" } ],
"assigned_staff": [ { "uuid": "…", "name": "پرسنل۱" } ],
"performed_by": [ { "uuid": "…", "name": "پرسنل۱" } ]
}
```
`assigned_staff` برنامه است و `performed_by` سابقه — اولی از خودِ پرونده می‌آید و دومی
از `TreatmentSession.performedBy`. جستجوی `q` هر دو را می‌گیرد، چون «کارهای این نفر»
شامل کارِ انجام‌شده هم هست.
**پروندهٔ بدون اختصاص یعنی «هر کسی که پروتکل مجاز دانسته»، نه «هیچ‌کس».** پرونده‌ای که
اپراتور دارد، جلساتش فقط در صفِ همان افراد دیده می‌شود.
`areas[].uuid` شناسهٔ همان ردیفِ ناحیه است و `category_uuid` شناسهٔ دستهٔ کاتالوگ.
ویرایش با دومی کار می‌کند؛ `null` یعنی دسته حذف شده و ناحیه فقط در سابقه مانده.
@@ -253,6 +262,7 @@ single-session again. Idempotent: deleting a service that has no protocol still
| `status` | `active` \| `completed` \| `abandoned`. برگرداندن به `active` پروندهٔ بسته را باز می‌کند و `closed_at` را پاک می‌کند |
| `supervisor_doctor_uuid` | پزشک ناظر؛ `null` یعنی بدون ناظر |
| `area_uuids` | فهرست **دستهٔ کاتالوگ**، جایگزین کامل. حداقل یکی |
| `staff_uuids` | اپراتورهای این پرونده، جایگزین کامل. فهرست خالی مجاز است |
| `total_sessions` | بین `TreatmentProtocol::MIN_STEPS` و `MAX_STEPS`. کم‌کردن جلسات را از انتها حذف می‌کند |
مرزِ ثابت: **هیچ ویرایشی سابقهٔ انجام‌شده را بازنویسی نمی‌کند.**
@@ -263,6 +273,7 @@ single-session again. Idempotent: deleting a service that has no protocol still
| ERR_VALIDATION_001 | 422 | `area_uuids` | فهرست خالی یا نامعتبر |
| ERR_VALIDATION_001 | 422 | `total_sessions` | خارج از بازهٔ مجاز |
| ERR_NOT_FOUND_001 | 404 | `supervisor_doctor_uuid` / `area_uuids` | پزشک یا ناحیه یافت نشد |
| ERR_NOT_FOUND_001 | 404 | `staff_uuids` | پرسنل یافت نشد، غیرفعال است، یا مال محیط دیگری است |
| ERR_CONFLICT_001 | 409 | `area_uuids` | ناحیه در جلسه‌ای ثبت شده و حذف نمی‌شود |
| ERR_CONFLICT_001 | 409 | `total_sessions` | کمتر از جلساتی که نوبت دارند یا انجام شده‌اند |