feat: implement per-doctor insurance settings in multi-doctor clinics
- Updated InsuranceModal to include doctorUuid in the payload for insurance contracts. - Enhanced TenantInsuranceContracts to allow selection of doctors and pass doctorUuid in API requests. - Modified InsuranceController to handle doctorUuid for tenant insurance endpoints, ensuring contracts are stored per doctor. - Updated API documentation to reflect the new optional doctor_uuid parameter for tenant insurance endpoints. - Added tests to verify the functionality of per-doctor insurance contracts and ensure isolation of contracts between doctors.
This commit is contained in:
+13
-2
@@ -371,9 +371,13 @@ entity جاری از `#[CurrentUser]` resolve میشود: نقش `ROLE_DOCTOR
|
||||
|
||||
tenant از `#[CurrentUser]` با `App\Patient\Security\PatientRecordScopeResolver` resolve میشود — همان رزولور پروندهها و صورتحسابها، تا قرارداد بیمه و صورتحسابی که از آن ساخته میشود هرگز به دو محیط متفاوت نیفتند. محیط فعال (`UserActiveContext`) تعیینکننده است، نه صرفاً ترتیب نقشها؛ مالک کلینیکی که خودش پزشک هم هست، قراردادهای **کلینیک** خود را میبیند.
|
||||
|
||||
**تنظیمات per-doctor در کلینیک چندپزشکه:** درصد و شرایط هر بیمه میتواند برای هر پزشک متفاوت باشد. همهٔ اندپوینتهای زیر یک پارامتر اختیاری `doctor_uuid` میپذیرند (در `GET`/`DELETE` از query، در `POST`/`PATCH`/`PUT` از بدنه). با آن، قرارداد بهجای موجودیتِ tenantِ کاربر جاری، بهازای پزشک هدف (`entity_type='doctor'`) خوانده/نوشته میشود — دقیقاً مثل `insurance-pricing`. **بدون** آن، رفتار قبلی (tenant کاربر جاری) دستنخورده میماند (سازگاری عقبرو). دسترسی با `doctor_uuid` هم مثل `insurance-pricing` بررسی میشود: `ROLE_ADMIN`، خودِ پزشک، یا کاربرِ عضو/مالکِ کلینیکِ آن پزشک با مجوز `services.view` (برای نوشتن `services.update`)؛ در غیر این صورت `403 ERR_ACCESS_DENIED`، و پزشکِ ناموجود `404 ERR_NOT_FOUND_001`.
|
||||
|
||||
### GET `/api/v1/billing/tenant-insurances`
|
||||
لیست قراردادهای tenant جاری — **آخرین نسخهٔ هر بیمه، فعال یا غیرفعال** (برای toggle فعال/غیرفعال در UI مدیریت بیمه). `insurance_kind` = `kind` قرارداد در صورت تعیین، وگرنه نوع بیمه از کاتالوگ.
|
||||
|
||||
**Query:** `doctor_uuid` (اختیاری) — قراردادهای همان پزشک را برمیگرداند (نگاه کنید به «تنظیمات per-doctor» بالا).
|
||||
|
||||
**Permission:** `AUTH` (doctor/clinic)
|
||||
|
||||
```json
|
||||
@@ -413,6 +417,7 @@ tenant از `#[CurrentUser]` با `App\Patient\Security\PatientRecordScopeResolv
|
||||
| `kind` | string \| null | نوع بیمه قرارداد (`basic`/`supplementary`); خالی → پیشفرض نوع کاتالوگ |
|
||||
| `effective_from` | int \| null | تاریخ شروع قرارداد (Unix)؛ null → اکنون |
|
||||
| `effective_to` | int \| null | تاریخ پایان قرارداد (Unix)؛ null → نامحدود |
|
||||
| `doctor_uuid` | string (UUID) \| null | اختیاری — قرارداد را بهازای پزشک هدف ذخیره میکند (نگاه کنید به «تنظیمات per-doctor» بالا) |
|
||||
|
||||
پاسخ `201`: `{ success, data: { …contract } }`.
|
||||
خطاها: `404 ERR_NOT_FOUND_001` بیمه یافت نشد · `422 ERR_VALIDATION_001` insurance_id الزامی · `403 ERR_FORBIDDEN_001` پروفایل یافت نشد.
|
||||
@@ -420,13 +425,16 @@ tenant از `#[CurrentUser]` با `App\Patient\Security\PatientRecordScopeResolv
|
||||
### PATCH `/api/v1/billing/tenant-insurances/{uuid}`
|
||||
ویرایش فیلدهای قرارداد (همه اختیاری، فقط کلیدهای موجود اعمال میشوند). فقط قرارداد متعلق به tenant جاری.
|
||||
|
||||
**Body:** `coverage_percent` · `franchise_rials` · `annual_ceiling_rials` · `kind` · `effective_from` · `effective_to` · `is_active`.
|
||||
**Body:** `coverage_percent` · `franchise_rials` · `annual_ceiling_rials` · `kind` · `effective_from` · `effective_to` · `is_active` · `doctor_uuid` (اختیاری، برای هدفگیری پزشک — نگاه کنید به «تنظیمات per-doctor» بالا).
|
||||
|
||||
- `is_active` (bool): toggle فعال/غیرفعال. برخلاف `DELETE`، مقدار `effective_to`ِ تعیینشدهٔ کاربر را دستنخورده نگه میدارد (برای reactivate).
|
||||
- قرارداد باید به همان موجودیتِ resolveشده (پزشک هدف یا tenant کاربر) تعلق داشته باشد، وگرنه `404`.
|
||||
|
||||
### DELETE `/api/v1/billing/tenant-insurances/{uuid}`
|
||||
غیرفعالسازی نرم (soft) — `is_active=false` و `effective_to=now`. داده حذف نمیشود.
|
||||
|
||||
**Query:** `doctor_uuid` (اختیاری) — برای غیرفعالسازی قرارداد یک پزشک خاص.
|
||||
|
||||
```json
|
||||
{ "success": true, "data": { "message": "قرارداد بیمه غیرفعال شد" } }
|
||||
```
|
||||
@@ -442,6 +450,8 @@ override پوشش یک خدمت خاص تحت قرارداد یک بیمه. فی
|
||||
### GET `/api/v1/billing/tenant-insurances/{uuid}/service-coverage`
|
||||
لیست overrideهای پوشش خدمات یک قرارداد.
|
||||
|
||||
**Query:** `doctor_uuid` (اختیاری) — برای قراردادِ متعلق به پزشک هدف در کلینیک چندپزشکه.
|
||||
|
||||
**Permission:** `AUTH` (مالک قرارداد)
|
||||
|
||||
```json
|
||||
@@ -476,8 +486,9 @@ override پوشش یک خدمت خاص تحت قرارداد یک بیمه. فی
|
||||
| `coverage_percent` | float \| null | null = ارث از قرارداد |
|
||||
| `franchise_rials` | int \| null | null = ارث از قرارداد |
|
||||
| `ceiling_rials` | int \| null | null = ارث از قرارداد |
|
||||
| `doctor_uuid` | string (UUID) \| null | اختیاری — قراردادِ متعلق به پزشک هدف (نگاه کنید به «تنظیمات per-doctor» بالا) |
|
||||
|
||||
سرویس باید متعلق به همان مطب/کلینیکِ قرارداد باشد (`ServiceItem→section→entity_type/entity_id`).
|
||||
سرویس باید متعلق به همان مطب/کلینیکِ قرارداد باشد (`ServiceItem→section→entity_type/entity_id`). با `doctor_uuid`، موجودیت هدف پزشک است، پس سرویس هم باید متعلق به همان پزشک باشد.
|
||||
|
||||
**اثر جانبی — همگامسازی `ServiceItem.insurance_covered`:** پس از ذخیرهی ردیف پوشش، پرچم
|
||||
`insurance_covered` همان خدمت بازمحاسبه میشود: اگر زیر **هر** قرارداد بیمهای دستکم یک ردیف با
|
||||
|
||||
Reference in New Issue
Block a user