Files
clinicpro/.claude/prompt/insurance-pricing-and-selects-cleanup.md
T

99 lines
7.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# یکپارچه‌سازی قراردادهای بیمه، select یکدست، و تعداد خدمت در مراجعه
## پروژه
`clinicpro` (admin frontend + کمی backend برای `quantity`)
## زمینه
چهار مورد به‌هم‌مرتبط در پنل ادمین:
1. صفحه‌ی `/admin/insurance-pricing` دو بخش دارد که هم‌پوشانی دارند → باید یکی شود.
2. select listها در کل پنل یک‌شکل نیستند (بعضی `<select className="input">` بومی، بعضی `SearchableSelect`).
3. در «ثبت مراجعه جدید» هر خدمت فقط یک‌بار اضافه می‌شود؛ تعداد (مثلاً سرُم ۲ عدد) ممکن نیست.
4. select بیمه‌ی پایه/تکمیلی در فرم مراجعه باید فقط بیمه‌هایی را نشان دهد که tenant در `insurance-pricing` قیمت‌گذاری/قرارداد کرده، و درصد تخفیف خودکار اعمال شود.
## وضعیت فعلی
### صفحه‌ی insurance-pricing (دو بخش هم‌پوشان)
`assets/admin/pages/InsurancePricingPage.tsx`:
```tsx
<TenantInsuranceContracts /> {/* قراردادها: بیمه + درصد پوشش + فرانشیز + سقف */}
<InsurancePricingSection /> {/* قیمت‌گذاری: قیمت ویزیت آزاد + سهم هر بیمه‌ی پایه */}
```
- `TenantInsuranceContracts` (`assets/admin/components/TenantInsuranceContracts.tsx`): per (tenant, insurance) قرارداد با `coverage_percent`, `franchise_rials`, `annual_ceiling_rials`؛ افزودن/ویرایش/حذف. هم basic هم supplementary. API: `/api/v1/billing/tenant-insurances` (GET/POST/PATCH/DELETE).
- `InsurancePricingSection` (`assets/admin/components/InsurancePricingSection.tsx`): «قیمت ویزیت آزاد» + سهم بیمار به ازای هر بیمه‌ی **پایه**. API: `GET/PUT /api/v1/insurance-pricing` (جدول `entity_insurance_pricing`).
این دو منطق موازی‌اند و کاربر را گیج می‌کنند. قرارداد با `coverage_percent` کافی است؛ بخش «قیمت‌گذاری» زیرش بی‌معنی است.
### select listها
نمونه‌ی بومی پراکنده (`NewSessionPage.tsx`, `MyPatientsPage.tsx`, `SmsPage.tsx`, `ClaimsPage.tsx`, `SettingsPage.tsx`, …):
```tsx
<select className="input" value={...} onChange={...}>
<option value="">...</option>
{opts.map(o => <option key={o.value} value={o.value}>{o.label}</option>)}
</select>
```
و در کنارش کامپوننت `SearchableSelect` (`assets/admin/components/ui/SearchableSelect.tsx`) برای بخش خدمات استفاده می‌شود. ظاهر این دو فرق دارد.
### تعداد خدمت
`src/Patient/Entity/SessionService.php` — فیلد `quantity` ندارد:
```php
private ServiceItem $serviceItem;
private ?ClinicStaff $staff = null;
private int $priceRials; // = serviceItem price، بدون ضرب در تعداد
```
`PatientService::createSession` services را تک‌واحدی جمع می‌زند:
```php
$servicesTotal = array_sum(array_column($serviceItems, 'price_rials'));
```
فرانت (`NewSessionPage.tsx`) هم هر خدمت را یک‌بار اضافه می‌کند (chip بدون تعداد).
### select بیمه در فرم مراجعه
`NewSessionPage.tsx` گزینه‌های بیمه را از `GET /api/v1/insurance-pricing` می‌گیرد (`pricing.insurances`)، ولی آن endpoint **همه‌ی بیمه‌های فعال** را برمی‌گرداند (نه فقط قراردادهای tenant). درصد هم از `patient_share_rials` همان endpoint محاسبه می‌شود — که بعد از حذف بخش قیمت‌گذاری دیگر منبع درستی نیست.
## وظایف
### ۱. ادغام صفحه‌ی insurance-pricing
- `InsurancePricingSection` و endpoint/جدول `entity_insurance_pricing` را کنار بگذار (از `InsurancePricingPage` حذف کن). قرارداد (`TenantInsuranceContracts`) تنها منبع باشد.
- `TenantInsuranceContracts` باید واضح هم basic هم supplementary را پشتیبانی کند (الان هم می‌کند؛ متن/گروه‌بندی را شفاف کن: دو دسته «بیمه پایه» و «بیمه تکمیلی»).
- اگر «قیمت ویزیت آزاد» جایی لازم است، آن را به‌صورت یک فیلد ساده در همان صفحه/پروفایل نگه‌دار یا حذف کن (تصمیم را در پیاده‌سازی مستند کن). مسیر `/admin/insurance-pricing` و آیتم سایدبار باقی بماند.
- `InsurancePricingSection.tsx` حذف یا خالی شود؛ importها پاک‌سازی.
### ۲. یک کامپوننت select یکدست
- یک کامپوننت `Select` در `assets/admin/components/ui/` بساز (یا `SearchableSelect` را به‌عنوان استاندارد واحد بپذیر) با ظاهر یکسان: همان استایل، RTL، chevron، حالت focus/disabled، placeholder.
- همه‌ی `<select className="input">`های پنل را با این کامپوننت جایگزین کن (صفحات بالا). رفتار/داده تغییر نکند، فقط ظاهر یکدست شود.
- نمونه‌ی مرجع ظاهر: select خدمات (`SearchableSelect`) در فرم مراجعه.
### ۳. تعداد (quantity) برای خدمات
**Backend:**
- به `SessionService` فیلد `quantity` (int, پیش‌فرض 1) اضافه کن + getter/setter + در `toArray()`. migration.
- `PatientService::createSession`: body هر service می‌تواند `quantity` داشته باشد؛ `line_total = price × quantity`؛ `services_total_rials` و `final_price_rials` با احتساب تعداد محاسبه شوند.
- `docs/api/patient.md` (یا فایل session مربوط) به‌روز شود.
**Frontend (`NewSessionPage.tsx`):**
- chip خدمت دارای ورودی/استپر تعداد باشد (۱،۲،…)؛ نمایش `price × qty`.
- body ارسالی: `services: [{ service_item_uuid, quantity }]`.
- خلاصه‌ی قیمت با تعداد محاسبه شود.
### ۴. select بیمه فقط از قراردادها + درصد خودکار
- در `NewSessionPage.tsx`، گزینه‌های «بیمه پایه» و «بیمه تکمیلی» از **قراردادهای tenant** بیایند (`GET /api/v1/billing/tenant-insurances`)، نه از `insurance-pricing`.
- هر گزینه `coverage_percent` خودش را دارد؛ با انتخاب بیمه، درصد تخفیف **خودکار** از `coverage_percent` قرارداد ست شود (نه از `patient_share_rials`).
- فقط بیمه‌های فعال قرارداد نمایش داده شوند؛ تفکیک basic/supplementary بر اساس `insurance_kind`.
## نکات مهم
- backend فقط برای `quantity` تغییر می‌کند؛ بقیه frontend است.
- بعد از تغییر Entity: `doctrine:migrations:diff` + `migrate`.
- پاسخ‌ها/الگوها: `BaseController`، `formatRial`/`formatNumber`، RTL، Vazirmatn، CSS variables موجود.
- چون `insurance-pricing` (entity_insurance_pricing) حذف می‌شود، هر مصرف‌کننده‌ی دیگرش را پیدا و پاک‌سازی کن (جستجوی `insurance-pricing` و `EntityInsurancePricing` در `src/` و `assets/`). اگر `BillingCalculator`/`InvoiceService` به آن وابسته است، به `coverage_percent` قرارداد سوییچ کن.
- tsc + `yarn dev` بدون خطا؛ هر بخش جدا commit.