- Introduced phase 4 documentation detailing treatment estimates, data models, state machines, API endpoints, and new dashboard metrics. - Added phase 5 documentation covering consumables, lab operations, periodontal charts, sterilization cycles, clinical images, and a checklist for professional review. - Created a preset content document outlining default dental service packages and protocols for clinics.
201 lines
10 KiB
Markdown
201 lines
10 KiB
Markdown
# فاز ۳ — داشبورد دندانپزشکی
|
||
|
||
> پیشنیاز: فاز ۱ و ۲.
|
||
> خروجی قابل تست: مدیر و پزشک و پذیرش، شاخصهای دندانپزشکی را در همان داشبورد فعلی خودشان میبینند.
|
||
|
||
---
|
||
|
||
## ۱. هدف
|
||
|
||
شاخصهای مخصوص حوزهٔ فعالیت به داشبورد نقشی اضافه شوند، بدون دستزدن به منطق داشبورد عمومی و بدون کند کردن داشبورد حوزههای دیگر.
|
||
|
||
---
|
||
|
||
## ۲. الگو
|
||
|
||
همان الگوی `TreatmentWorkflow` که در `docs/adr/0005` ثبت شده.
|
||
|
||
```
|
||
src/Dashboard/Metric/DomainMetricProvider.php اینترفیس، با تگ app.domain_metric_provider
|
||
src/Dashboard/Metric/DomainMetricRegistry.php انتخاب بر اساس کد حوزه
|
||
src/Dashboard/Metric/MetricRequest.php بازه، نقش، محیط، فیلترها
|
||
src/Dashboard/Metric/MetricSet.php خروجی استاندارد
|
||
src/Dental/Metric/DentalMetricProvider.php پیادهسازی دندانپزشکی
|
||
```
|
||
|
||
اینترفیس:
|
||
|
||
```php
|
||
interface DomainMetricProvider
|
||
{
|
||
public function supports(?string $practiceDomainCode): bool;
|
||
|
||
/** @return list<string> کلید شاخصهایی که این نقش میبیند */
|
||
public function keysFor(string $role): array;
|
||
|
||
public function collect(MetricRequest $request): MetricSet;
|
||
}
|
||
```
|
||
|
||
محیطی که حوزهاش `null` است یا provider ندارد، هیچ بخش تازهای نمیگیرد.
|
||
برخلاف `TreatmentWorkflowRegistry` اینجا پیادهسازی پیشفرض لازم نیست؛ نبودن provider یعنی بخش دندانی رندر نمیشود.
|
||
دلیل: شاخص خالی بدتر از نبودن بخش است.
|
||
|
||
هر متد `collect` باید تعداد کوئری ثابت داشته باشد، مستقل از تعداد شاخص.
|
||
یعنی شاخصهای هممنبع در یک کوئری جمع شوند.
|
||
|
||
---
|
||
|
||
## ۳. رجیستری شاخصها
|
||
|
||
هر شاخص یک کلید ثابت دارد.
|
||
فرانت هیچ فرمولی محاسبه نمیکند و فقط مصرفکنندهٔ عدد است.
|
||
دلیل: اگر تعریف «تولید» در دو جا نوشته شود، دیر یا زود دو عدد متفاوت نشان داده میشود.
|
||
|
||
### شاخصهای قابل محاسبه در فاز ۳
|
||
|
||
| کلید | تعریف | منبع |
|
||
|---|---|---|
|
||
| `production` | جمع مبلغ ناخالص ویزیتهای انجامشده در بازه | `patient_sessions` |
|
||
| `collection` | جمع پرداختهای ثبتشده در بازه | `payments` و `session_payments` |
|
||
| `collection_rate` | وصولی تقسیم بر تولید | مشتق |
|
||
| `avg_per_visit` | تولید تقسیم بر تعداد ویزیت | مشتق |
|
||
| `production_per_doctor` | تولید به تفکیک پزشک | `patient_sessions` |
|
||
| `service_mix` | سهم هر گروه کاتالوگ از تولید | `session_services` و `service_catalog_categories` |
|
||
| `chair_utilization` | دقایق رزروشدهٔ منابع نوع اتاق تقسیم بر دقایق ظرفیت | `resource_occupancy` و `resource_calendars` |
|
||
| `no_show_rate` | نوبتهای حاضرنشده تقسیم بر کل نوبتها | `appointments` |
|
||
| `cancellation_rate` | نوبتهای لغوشده تقسیم بر کل | `appointments` |
|
||
| `new_patients` | بیماران با اولین ویزیت در بازه | `patient_sessions` |
|
||
| `ar_outstanding` | جمع بدهی معوق بیماران | `patient_sessions` |
|
||
| `treatments_by_group` | تعداد خدمت انجامشده به تفکیک گروه دندانی | `session_services` |
|
||
| `teeth_treated` | تعداد دندانهای درمانشدهٔ یکتا در بازه | `session_services` |
|
||
|
||
### شاخصهایی که در این فاز وجود ندارند
|
||
|
||
| کلید | چرا |
|
||
|---|---|
|
||
| `case_acceptance_rate` | ورودیاش برآورد درمان است که فاز ۴ ساخته میشود |
|
||
| `unscheduled_treatment` | همان |
|
||
| `redo_rate` | نیازمند حالت درمان مجدد روی ردیف برآورد |
|
||
| `lab_cost_ratio` | فاز ۵ |
|
||
| `consumable_cost_ratio` | فاز ۵ |
|
||
| `recall_response_rate` | نیازمند سازوکار ریکال که پروژه هنوز ندارد |
|
||
|
||
این فهرست عمداً در سند مانده تا کسی فکر نکند فراموش شدهاند.
|
||
|
||
### دو تعریف که نباید قاطی شوند
|
||
|
||
**تولید** جمع مبلغ کاری است که انجام شده.
|
||
**وصولی** جمع پولی است که رسیده.
|
||
|
||
این دو از دو جدول متفاوت میآیند و هیچکدام نباید از دیگری استنتاج شود.
|
||
فاصلهٔ بینشان همان چیزی است که نرخ وصول را معنادار میکند.
|
||
|
||
---
|
||
|
||
## ۴. نماها
|
||
|
||
| نقش | شاخصهای صفحهٔ اصلی |
|
||
|---|---|
|
||
| مدیر کلینیک یا مطب | `collection`, `collection_rate`, `chair_utilization`, `new_patients`, `ar_outstanding`, `service_mix` |
|
||
| پزشک | تولید شخصی، `avg_per_visit` خودش، `treatments_by_group` خودش، `teeth_treated` |
|
||
| پذیرش | اشغال یونیت امروز، `no_show_rate`, `cancellation_rate`, صف نوبت امروز |
|
||
|
||
حداکثر شش کارت در صفحهٔ اصلی هر نقش.
|
||
بقیه پشت drill-down.
|
||
دلیل: داشبورد با بیست کارت خوانده نمیشود و کاربر بهجای تصمیم، اسکرول میکند.
|
||
|
||
فیلتر مشترک: بازهٔ تاریخ جلالی، پزشک، گروه خدمت، یونیت.
|
||
|
||
---
|
||
|
||
## ۵. API
|
||
|
||
اندپوینتهای نقشی موجود دست نمیخورند.
|
||
پاسخشان یک کلید تازه میگیرد:
|
||
|
||
```
|
||
GET /api/v1/dashboard/clinic
|
||
→ { ..., domain_metrics: { code: "dental", metrics: { ... } } | null }
|
||
```
|
||
|
||
اگر محیط حوزه ندارد یا provider ندارد، مقدار `null` است.
|
||
|
||
یک اندپوینت تازه برای بازه و روند:
|
||
|
||
```
|
||
GET /api/v1/dashboard/domain-metrics?from&to&doctorUuid?&resourceUuid?&groupUuid?
|
||
→ { code, metrics: { <key>: { value, previous_value, change_percent } } }
|
||
|
||
GET /api/v1/dashboard/domain-metrics/trend?metric=production&from&to&interval=day|week|month
|
||
→ { points: [ { date, value } ] }
|
||
```
|
||
|
||
`date` میلادی برمیگردد و تبدیل جلالی در فرانت انجام میشود.
|
||
دلیل: رشتهٔ جلالی در پاسخ، مرتبسازی و بازهگیری را در فرانت میشکند.
|
||
|
||
بازهٔ پیشفرض سی روز.
|
||
حداکثر بازهٔ مجاز یک سال، وگرنه خطای ۴۲۲.
|
||
دلیل: بدون سقف، یک درخواست میتواند کل جدول ویزیت را اسکن کند.
|
||
|
||
`docs/api/dashboard.md` همان جلسه بهروز میشود.
|
||
|
||
---
|
||
|
||
## ۶. پنل ادمین
|
||
|
||
فایل: `assets/admin/pages/DashboardPage.tsx`
|
||
|
||
- بخش «شاخصهای دندانپزشکی» بعد از کارتهای عمومی، فقط وقتی `domain_metrics` مقدار دارد.
|
||
- کارتها با `StatCard` موجود.
|
||
- روند با `Recharts` که پروژه از قبل دارد.
|
||
- drill-down با `DataTable` موجود.
|
||
- انتخاب بازه با `PersianDateInput` موجود.
|
||
- فیلتر پزشک و یونیت با `SearchableSelect`.
|
||
- `TanStack Query` با `staleTime` معقول، چون این اعداد ثانیهای عوض نمیشوند.
|
||
|
||
هیچ کامپوننت تازهای ساخته نمیشود.
|
||
|
||
---
|
||
|
||
## ۷. تسکها
|
||
|
||
| کد | تسک | فایلهای اصلی | معیار پذیرش |
|
||
|---|---|---|---|
|
||
| DM3-01 | اینترفیس و رجیستری `DomainMetricProvider` | `src/Dashboard/Metric/` | محیط بدون حوزه، `null` میگیرد |
|
||
| DM3-02 | `MetricRequest` و `MetricSet` | همان | بازهٔ بزرگتر از یک سال ۴۲۲ میدهد |
|
||
| DM3-03 | `DentalMetricProvider` بخش مالی | `src/Dental/Metric/` | تولید و وصولی با دادهی ساختگی درست است |
|
||
| DM3-04 | بخش اشغال یونیت | همان | منبع بدون تقویم، صفر میدهد نه خطا |
|
||
| DM3-05 | بخش نوبت، حاضرنشده و لغو | همان | مخرج صفر، `null` میدهد نه تقسیم بر صفر |
|
||
| DM3-06 | بخش ترکیب خدمات و دندانهای درمانشده | همان | خدمت بدون گروه در «سایر» میرود |
|
||
| DM3-07 | اتصال `domain_metrics` به چهار اندپوینت نقشی | `src/Dashboard/Controller/DashboardController.php` | داشبورد حوزههای دیگر کوئری اضافه نمیزند |
|
||
| DM3-08 | اندپوینت بازه و روند | همان | فیلترها ترکیبی کار میکنند |
|
||
| DM3-09 | بخش دندانی در صفحهٔ داشبورد | `assets/admin/pages/DashboardPage.tsx` | برای حوزهٔ زیبایی رندر نمیشود |
|
||
| DM3-10 | نمودار روند و drill-down | همان | خالی بودن داده، حالت خالی نشان میدهد نه خطا |
|
||
| DM3-11 | بهروزرسانی `docs/api/dashboard.md` | `docs/api/` | نمونهٔ پاسخ با کد یکی است |
|
||
|
||
---
|
||
|
||
## ۸. تستها
|
||
|
||
- محیط بدون حوزه: `domain_metrics` برابر `null` و هیچ کوئری اضافهای اجرا نمیشود.
|
||
- محیط زیبایی: همان.
|
||
- محیط دندانپزشکی بدون هیچ ویزیت: همهٔ شاخصها صفر یا `null`، بدون خطا.
|
||
- تقسیم بر صفر در هر نرخ: `null` برمیگردد و فرانت خط تیره نشان میدهد.
|
||
- پزشک فقط عدد خودش را میبیند، حتی با فیلتر پزشک دیگر.
|
||
- منشی به شاخصهای مالی دسترسی ندارد.
|
||
- بازهٔ بزرگتر از یک سال: ۴۲۲.
|
||
- تست کارایی: تعداد کوئری با افزایش تعداد شاخص ثابت میماند.
|
||
|
||
---
|
||
|
||
## ۹. ریسکها
|
||
|
||
**کندی داشبورد با رشد داده.**
|
||
تصمیم فاز ۳ محاسبهٔ زنده است.
|
||
مهار: سقف بازه، ایندکس روی ستونهای تاریخ و محیط، و یک تست کارایی که تعداد کوئری را قفل میکند.
|
||
اگر بعداً کند شد، جدول تجمیع پشت همین سرویس اضافه میشود بدون تغییر API.
|
||
|
||
**دو عدد متفاوت برای یک شاخص.**
|
||
مهار: هیچ فرمولی در فرانت نوشته نمیشود.
|