Refactor insurance share calculation logic in PatientService

- Consolidated the calculation of patient and insurance shares into a single method using BillingCalculator.
- Introduced new fields in PatientSession to store breakdown of insurance shares and patient share.
- Updated the API responses to include the new fields for consistency across payment, invoice, and claims dashboard.
- Added migration to backfill existing sessions with appropriate values for the new fields.
- Implemented tests to ensure the correctness of the new logic and verify that the breakdown sums to the gross total.
- Redesigned the claims dashboard to provide a more user-friendly overview of patient claims and their statuses.
This commit is contained in:
hamed
2026-07-18 22:56:46 +03:30
parent 466b649988
commit b3a5cda808
16 changed files with 842 additions and 110 deletions
+1 -6
View File
@@ -103,7 +103,6 @@
"price_rials": 500000,
"active": true,
"insurance_covered": false,
"insurance_price_rials": null,
"duration_minutes": 50,
"bookable": true,
"created_at": 1718000000,
@@ -137,7 +136,6 @@ refresh مستقیم هم کار کند، بنابراین فیلترکردن س
"price_rials": 850000,
"active": true,
"insurance_covered": true,
"insurance_price_rials": null,
"duration_minutes": 30,
"bookable": true,
"staff": { "uuid": "...", "full_name": "مریم امینی" },
@@ -213,7 +211,6 @@ refresh مستقیم هم کار کند، بنابراین فیلترکردن س
"price_rials": 500000,
"staff_uuid": "...",
"insurance_covered": true,
"insurance_price_rials": 200000,
"duration_minutes": 50,
"bookable": true
}
@@ -227,7 +224,6 @@ refresh مستقیم هم کار کند، بنابراین فیلترکردن س
| staff_uuids | UUID[] | ❌ — پرسنل مسئول (چند نفر). ترجیح داده می‌شود |
| staff_uuid | UUID | ❌ — legacy تک‌پرسنل (اگر `staff_uuids` نباشد استفاده می‌شود) |
| insurance_covered | boolean | ❌ (پیش‌فرض false) — **deprecated برای نوشتن.** پنل ادمین دیگر این فیلد را نمی‌فرستد؛ مقدارش به‌صورت خودکار از ردیف‌های پوشش بیمه همگام می‌شود (به [insurance.md](insurance.md#put-apiv1billingtenant-insurancesuuidservice-coverage) نگاه کن). endpoint هنوز آن را می‌پذیرد تا کلاینت‌های قدیمی نشکنند، ولی ذخیره‌ی پوشش بعداً آن را بازنویسی می‌کند |
| insurance_price_rials | integer\|null | ❌ — **deprecated.** سهم تقریبی بیمار؛ از فرم سرویس حذف شد. محاسبه‌ی دقیق سهم بیمار از `TenantServiceCoverage` انجام می‌شود |
| duration_minutes | integer\|null | ❌ — «زمان متوسط» انجام خدمت به دقیقه (`""`/`null` = بدون مقدار) |
| bookable | boolean | ❌ (پیش‌فرض false) — «نمایش در نوبت‌دهی». فقط سرویس‌های `bookable=true` در حالت نوبت‌دهی سرویسی قابل‌انتخاب‌اند |
| inventory_package_uuid | UUID\|null | ❌ — پکیج کالای مصرفی این خدمت ([inventory.md](inventory.md)). `null`/`""` یعنی قطع اتصال. پکیج باید متعلق به همان مطب/کلینیک باشد وگرنه `422 ERR_VALIDATION_001` با فیلد `inventory_package_uuid` |
@@ -235,7 +231,7 @@ refresh مستقیم هم کار کند، بنابراین فیلترکردن س
> `bookable` در `PATCH /api/v1/service-item/{uuid}` هم به همین شکل پذیرفته می‌شود.
**Response 201:** ServiceItem object (شامل `insurance_covered` و `insurance_price_rials`)
**Response 201:** ServiceItem object (شامل `insurance_covered`)
> **قیمت واحد:** هنگام ساخت سرویس، یک تعرفه برای **سال جاری** با همان `price_rials` به‌صورت خودکار ثبت می‌شود. قیمت سرویس = تعرفه‌ی سال جاری است و همه‌جا (صورتحساب، مراجعه، مطالبات) از همین قیمت استفاده می‌شود.
@@ -252,7 +248,6 @@ refresh مستقیم هم کار کند، بنابراین فیلترکردن س
"staff_uuid": null,
"active": false,
"insurance_covered": true,
"insurance_price_rials": 250000,
"duration_minutes": 30
}
```