Files
clinicpro/docs/new_feture/dental-module/phase-2-tooth-chart.md
T
hamed 2f030edef1 Add dental module phase 4 and phase 5 documentation, including treatment estimates, clinical operations, and preset content
- 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.
2026-08-20 16:21:08 +03:30

300 lines
14 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.
# فاز ۲ — چارت دندان و هدف‌گیری دندان روی خدمت ویزیت
> پیش‌نیاز: فاز ۱.
> خروجی قابل تست: دندانپزشک وضعیت دندان‌های بیمار را می‌بیند، ثبت خدمت روی دندان انجام می‌دهد و چارت خودکار به‌روز می‌شود.
---
## ۱. هدف
سه چیز:
۱. هر بیمار در هر محیط یک چارت دندان داشته باشد.
۲. ثبت خدمت در ویزیت بتواند دندان و سطح را هدف بگیرد.
۳. چارت بعد از ثبت خدمت خودکار به‌روز شود، و وضعیت‌های قدیمی هم دستی قابل ثبت باشند.
---
## ۲. شماره‌گذاری دندان
استاندارد `FDI` دو رقمی، همان `ISO 3950`.
```
دائمی : 1118, 2128, 3138, 4148
شیری : 5155, 6165, 7175, 8185
```
ذخیره به‌صورت `smallint`، نه رشته.
دلیل: مقایسه و بازه و ایندکس روی عدد کار می‌کند و «۱۱» و «11» دو مقدار جدا نمی‌سازد.
اعتبارسنجی در یک نقطه:
```
src/Dental/Validator/ToothNumberValidator.php
```
استانداردهای `Universal` و `Palmer` در لایهٔ داده استفاده نمی‌شوند.
اگر بعداً لازم شد، فقط لایهٔ نمایش تبدیل می‌کند.
سطوح دندان:
```
M مزیال · D دیستال · O اکلوزال · B باکال · L لینگوال · P پالاتال · I اینسایزال
```
---
## ۳. مدل داده
همه در `src/Dental/Entity`.
### `ToothChart` — جدول `dental_tooth_charts`
| ستون | نوع | توضیح |
|---|---|---|
| `id`, `uuid` | | |
| `entity_type`, `entity_id` | | جفت محیط |
| `patient_record_id` | int | یکتا در هر محیط |
| `dentition_type` | string 20 | `permanent`, `primary`, `mixed` |
| `last_examined_at` | int, nullable | |
| `created_at`, `updated_at` | int | |
یکتایی: `(entity_type, entity_id, patient_record_id)`.
چارت با اولین نیاز ساخته می‌شود، نه با ساخت پرونده.
دلیل: پروندهٔ بیماری که هرگز درمان دندانی نمی‌گیرد نباید ردیف خالی بسازد.
### `ToothStatus` — جدول `dental_tooth_statuses`
| ستون | نوع | توضیح |
|---|---|---|
| `id`, `uuid` | | |
| `chart_id` | int | `ON DELETE CASCADE` |
| `tooth_number` | smallint | FDI |
| `condition` | string 30 | وضعیت کلی دندان |
| `surface_map` | json, nullable | وضعیت هر سطح |
| `note` | string 500, nullable | |
| `source` | string 20 | `manual` یا `visit` |
| `recorded_by_user_id` | int, nullable | |
| `updated_at` | int | |
یکتایی: `(chart_id, tooth_number)`.
مقادیر `condition`:
```
healthy | caries | filled | crown | bridge_pontic | root_canal
implant | missing | extracted | impacted | to_extract | unerupted
```
`surface_map` شکل ثابت دارد:
```json
{ "O": "filled", "M": "caries", "D": "healthy" }
```
**چرا وضعیت جدا از تاریخچه ذخیره می‌شود:**
رندر چارت باید با یک کوئری انجام شود.
اگر وضعیت هر بار از بازپخش تاریخچهٔ ویزیت‌ها ساخته شود، هر باز کردن تب یک محاسبهٔ سنگین است و وضعیت قبل از اولین مراجعه اصلاً قابل ثبت نیست.
هزینهٔ پذیرفته‌شده: این جدول باید بعد از هر ویزیت به‌روز شود و این کار فقط در یک کلاس انجام می‌شود، نه پراکنده در کنترلرها.
### `ToothStatusLog` — جدول `dental_tooth_status_logs`
هر تغییر وضعیت یک ردیف اضافه می‌کند. فقط افزودنی است.
| ستون | نوع |
|---|---|
| `id`, `uuid` | |
| `chart_id`, `tooth_number` | |
| `from_condition`, `to_condition` | string 30 |
| `surface_map_before`, `surface_map_after` | json, nullable |
| `session_service_id` | int, nullable |
| `changed_by_user_id` | int, nullable |
| `changed_at` | int |
دلیل وجودش: چارت سند پزشکی است.
«چه کسی دندان ۱۶ را کشیده‌شده علامت زد» باید قابل جواب دادن باشد.
---
## ۴. هدف‌گیری دندان روی خدمت ویزیت
### تغییر روی دامنهٔ موجود
روی `SessionService` سه ستون اختیاری اضافه می‌شود:
```
tooth_number smallint nullable
surfaces json nullable
target_code string 10 nullable کد فک یا ناحیه
```
**چرا اینجا و نه در جدول دندانی جدا:**
این‌ها ویژگی همان ردیف خدمتِ فاکتورشده‌اند.
جدا کردنشان یعنی برای هر ردیف فاکتور یک join اضافه، و امکان اینکه ردیف فاکتور بدون هدف بماند بدون اینکه کسی بفهمد.
برخلاف `ServiceItem` که تنظیمات است و مشترک همهٔ حوزه‌هاست، `SessionService` سند یک ویزیت است و این سه ستون بخشی از همان سند.
### اعتبارسنجی
در `src/Dental/Service/ToothTargetValidator.php`.
قاعده بر اساس `target_scope` پروفایل خدمت:
| `target_scope` | لازم | ممنوع |
|---|---|---|
| `none` و `mouth` | — | هر سه |
| `tooth` | `tooth_number` | `surfaces`, `target_code` |
| `tooth_surface` | `tooth_number` و حداقل یک سطح | `target_code` |
| `quadrant` | `target_code` از ۱ تا ۴ | `tooth_number`, `surfaces` |
| `arch` | `target_code` برابر `upper` یا `lower` | `tooth_number`, `surfaces` |
قاعدهٔ دوم: `tooth_scope` خدمت با شمارهٔ دندان بخواند.
خدمت `permanent_only` روی دندان ۵۱ خطا می‌دهد.
قاعدهٔ سوم: خدمتی که پروفایل دندانی ندارد، هیچ هدفی نمی‌پذیرد.
خطا با `ERR_VALIDATION_002` و نام فیلد.
### پروجکتور چارت
مسیر: `src/Dental/Service/ToothChartProjector.php`
بعد از ثبت یا ویرایش ردیف خدمت با هدف دندانی:
```
سرویس ترمیمی روی سطوح → همان سطوح در surface_map مقدار filled می‌گیرند
سرویس کشیدن دندان → condition برابر extracted
سرویس درمان ریشه → condition برابر root_canal
سرویس روکش → condition برابر crown
سرویس ایمپلنت → condition برابر implant
بقیه → وضعیت دست نمی‌خورد، فقط لاگ ثبت می‌شود
```
نگاشت خدمت به اثر، در همان `DentalPreset` تعریف می‌شود با کلید `chart_effect`.
دلیل: مدیر می‌تواند خدمت دلخواه بسازد و اثرش را انتخاب کند، بدون اینکه کد عوض شود.
حذف ردیف خدمت، وضعیت را به عقب برنمی‌گرداند.
دلیل: دندان کشیده‌شده با حذف یک ردیف فاکتور برنمی‌گردد.
به‌جایش یک لاگ با توضیح ثبت می‌شود و اصلاح دستی می‌ماند.
---
## ۵. API
`docs/api/dental.md` گسترش پیدا می‌کند.
```
GET /api/v1/dental/chart/{patientRecordUuid}
→ { chart: {...}, teeth: [ { tooth_number, condition, surfaces, note } ] }
PUT /api/v1/dental/chart/{patientRecordUuid}/tooth/{toothNumber}
→ ثبت یا اصلاح دستی وضعیت یک دندان
GET /api/v1/dental/chart/{patientRecordUuid}/tooth/{toothNumber}/history
→ لاگ تغییرات همان دندان
```
دسترسی:
| عملیات | clinic | doctor | secretary | staff |
|---|---|---|---|---|
| دیدن چارت | بله | بیماران خودش | خواندنی | نه |
| ویرایش دستی چارت | نه | بله | نه | نه |
| ثبت هدف دندانی در ویزیت | نه | بله | نه | نه |
خطاها:
| کد | HTTP | حالت |
|---|---|---|
| `ERR_VALIDATION_002` | 422 | شمارهٔ دندان نامعتبر یا هدف ناسازگار |
| `ERR_NOT_FOUND_001` | 404 | پرونده در این محیط نیست |
| `ERR_FORBIDDEN_001` | 403 | نقش مجاز نیست |
اندپوینت ثبت خدمت ویزیت هم کلیدهای تازه می‌گیرد و `docs/api/patient.md` همان جلسه به‌روز می‌شود.
---
## ۶. پنل ادمین
### تب تازه
فایل: `assets/admin/pages/PatientDetailPage.tsx`
- کلید تب: `dental`، برچسب «چارت دندان».
- فقط وقتی حوزهٔ محیط دندانپزشکی است رندر می‌شود.
- بین «پرونده پزشکی» و «ضمیمه» می‌نشیند.
### کامپوننت چارت
فایل: `assets/admin/components/dental/ToothChart.tsx`
این تنها جایی است که ساخت کامپوننت تازه موجه است، چون هیچ کامپوننت موجودی این کار را نمی‌کند.
قواعد:
- `SVG` دست‌نویس، بدون کتابخانهٔ بیرونی.
- هر دندان یک گروه قابل کلیک با شمارهٔ FDI.
- هر سطح یک مسیر جدا، تا کلیک روی سطح جدا از کلیک روی دندان باشد.
- رنگ‌ها فقط از توکن‌های `styles.css`. هیچ رنگ ثابتی در کد کامپوننت نیست.
- چیدمان `RTL` و سازگار با تم تیره.
- فک بالا در ردیف بالا، فک پایین در ردیف پایین، سمت راست بیمار در سمت راست تصویر. این قرارداد در بالای فایل به‌صورت کامنت نوشته شود چون خطای رایج همین است.
- حالت شیری و مختلط: دندان‌های شیری در همان گرید، کوچکتر.
- بدون تعامل هم باید خوانا باشد، چون در چاپ پرونده استفاده می‌شود.
### فرم ثبت خدمت در ویزیت
فایل: `assets/admin/pages/EditSessionPage.tsx`
- بعد از انتخاب خدمت، اگر پروفایل دندانی دارد، انتخابگر هدف نشان داده شود.
- انتخاب دندان از روی همان `ToothChart` انجام شود، نه از یک `select` با ۳۲ گزینه.
- انتخاب سطح فقط وقتی `target_scope` برابر `tooth_surface` است.
---
## ۷. تسک‌ها
| کد | تسک | فایل‌های اصلی | معیار پذیرش |
|---|---|---|---|
| DM2-01 | `ToothNumberValidator` و ثابت‌های FDI | `src/Dental/Validator/` | همهٔ شماره‌های معتبر و نامعتبر تست می‌شوند |
| DM2-02 | موجودیت `ToothChart` | `src/Dental/Entity/` | یکتایی پرونده در محیط |
| DM2-03 | موجودیت `ToothStatus` با `surface_map` | همان | شکل json اعتبارسنجی می‌شود |
| DM2-04 | موجودیت `ToothStatusLog` | همان | فقط افزودنی، بدون متد حذف |
| DM2-05 | سه ستون هدف روی `SessionService` با migration | `src/Patient/Entity/SessionService.php` | ردیف بدون هدف مثل قبل کار می‌کند |
| DM2-06 | `ToothTargetValidator` | `src/Dental/Service/` | هر پنج حالت `target_scope` تست می‌شود |
| DM2-07 | `ToothChartProjector` و نگاشت `chart_effect` | `src/Dental/Service/`, `src/Dental/Preset/` | ثبت کشیدن دندان، وضعیت را عوض می‌کند و لاگ می‌زند |
| DM2-08 | سه اندپوینت چارت | `src/Dental/Controller/DentalChartController.php` | موفق، بدون دسترسی، پروندهٔ محیط دیگر |
| DM2-09 | گسترش ثبت خدمت ویزیت برای هدف دندانی | `src/Patient/Controller/PatientController.php` | هدف ناسازگار ۴۲۲ می‌دهد |
| DM2-10 | کامپوننت `ToothChart` | `assets/admin/components/dental/` | تست: کلیک دندان، کلیک سطح، حالت فقط‌خواندنی |
| DM2-11 | تب چارت در پروندهٔ بیمار | `assets/admin/pages/PatientDetailPage.tsx` | برای حوزهٔ غیر دندانی رندر نمی‌شود |
| DM2-12 | انتخابگر هدف در فرم ثبت خدمت | `assets/admin/pages/EditSessionPage.tsx` | خدمت بدون پروفایل، انتخابگر نشان نمی‌دهد |
| DM2-13 | به‌روزرسانی `docs/api/dental.md` و `docs/api/patient.md` | `docs/api/` | مسیرها با کد یکی است |
---
## ۸. تست‌ها
- ثبت خدمت روی دندان شیری با خدمت `permanent_only`: خطای ۴۲۲.
- ثبت خدمت `tooth_surface` بدون سطح: خطای ۴۲۲.
- ثبت خدمت `arch` با شمارهٔ دندان: خطای ۴۲۲.
- ثبت کشیدن دندان: وضعیت `extracted` و یک ردیف لاگ.
- ویرایش دستی وضعیت: منبع `manual` ثبت می‌شود.
- خواندن چارت بیمار محیط دیگر: خطای ۴۰۴، نه ۴۰۳. دلیل: نباید وجود پرونده در محیط دیگر لو برود.
- منشی چارت را می‌بیند ولی نمی‌تواند ویرایش کند.
- چارت بیماری که هیچ درمانی نگرفته: ساخته می‌شود و همهٔ دندان‌ها `healthy` برمی‌گردند بدون اینکه ۳۲ ردیف در دیتابیس ساخته شود.
---
## ۹. ریسک‌ها
**واگرایی چارت از فاکتور.**
اگر کاربر خدمت را ثبت کند ولی هدف را خالی بگذارد، چارت به‌روز نمی‌شود و کسی نمی‌فهمد.
مهار: برای خدمتی که پروفایل دندانی دارد، هدف اجباری است و ردیف بدون هدف اصلاً ذخیره نمی‌شود.
**تعداد ردیف وضعیت.**
اگر برای هر بیمار ۳۲ ردیف ساخته شود، جدول سریع بزرگ می‌شود.
مهار: فقط دندان‌هایی که وضعیتشان از `healthy` فاصله گرفته ردیف می‌گیرند. بقیه در پاسخ API از پیش‌فرض ساخته می‌شوند.
**سمت چپ و راست جابه‌جا.**
خطای رایج در چارت دندان و در سند پزشکی خطرناک است.
مهار: قرارداد جهت در کامنت بالای کامپوننت، و یک تست که دندان ۱۱ را در جای درست ادعا می‌کند.