99 lines
7.4 KiB
Markdown
99 lines
7.4 KiB
Markdown
# یکپارچهسازی قراردادهای بیمه، 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.
|