feat: add initial PRD for ClinicPro Phase 2 outlining staff management, subscription tiers, and patient records

This commit is contained in:
hamed
2026-06-14 20:41:37 +03:30
parent ab5dca4fb6
commit 9c935c341f
2 changed files with 492 additions and 0 deletions
+435
View File
@@ -0,0 +1,435 @@
# PRD — ClinicPro Phase 2
**تاریخ:** ۲۴ خرداد ۱۴۰۵
**نسخه:** 1.0
**وضعیت:** Draft
---
## خلاصه اجرایی
این سند نیازمندی‌های فاز ۲ پلتفرم **ClinicPro** را پوشش می‌دهد. هدف: تبدیل پلتفرم از یک سیستم نوبت‌دهی ساده به یک سیستم مدیریت کلینیک کامل با پنل‌های اشتراکی، پرونده بیمار، مدیریت پرسنل و پنل پیامکی.
---
## وضعیت فعلی پروژه
| ماژول | وضعیت |
|--------|--------|
| Auth (OTP + JWT) | ✅ کامل |
| Doctor profile | ✅ کامل |
| Clinic management | ✅ کامل |
| Appointment booking | ✅ کامل |
| Secretary entity (DoctorSecretary) | ✅ موجود — نیاز به UI و permissions بیشتر |
| Payment gateway (Mellat, SEP) | ✅ کامل — برای نوبت + subscription |
| SMS (SmsLog, SmsTemplate) | ✅ زیرساخت موجود — نیاز به پنل کلینیک |
| Wallet / Settlement | ✅ کامل |
| Site config (SiteConfig key-value) | ✅ موجود |
| **Staff (پرسنل)** | ❌ موجود نیست |
| **Panel subscription tiers** | ❌ موجود نیست |
| **Patient records (پرونده بیمار)** | ❌ موجود نیست |
| **Clinic services (خدمات)** | ❌ موجود نیست |
| **SMS panel per clinic** | ❌ UI نیاز دارد |
---
## اپیک‌ها و قابلیت‌ها
---
### اپیک ۱ — پرسنل (Staff)
#### توضیح
مدیر کلینیک یا دکتر مستقل می‌تواند پرسنلی که نیازی به لاگین ندارند را ثبت کند. این پرسنل در سرویس‌دهی، سشن‌ها و گزارشات استفاده می‌شوند.
#### نیازمندی‌های کارکردی
- **F1.1** — ایجاد / ویرایش / غیرفعال‌سازی پرسنل (حذف نرم — soft delete)
- **F1.2** — فیلدهای پرسنل:
- نام و نام خانوادگی (اجباری)
- شماره تماس
- عنوان شغل (مثال: پرستار، منشی، تکنسین)
- آدرس
- کد ملی
- فعال / غیرفعال
- **F1.3** — پرسنل به کلینیک یا مطب دکتر وابسته است (FK به clinic_id یا doctor_id)
- **F1.4** — لیست پرسنل فقط در scope کلینیک/مطب فعال نمایش داده می‌شود
- **F1.5** — در ایجاد سرویس، از لیست پرسنل انتخاب می‌شود
#### موجودیت‌های جدید مورد نیاز
```
ClinicStaff:
- id, uuid
- clinic_id (FK → clinics) OR doctor_id (FK → doctors) [یکی از دو]
- full_name, phone, job_title, address, national_code
- active (bool, default: true)
- created_at, updated_at (Unix timestamp)
```
#### API endpoints
| Method | Path | Permission |
|--------|------|-----------|
| `GET` | `/api/v1/staff` | clinic/doctor |
| `POST` | `/api/v1/staff` | clinic/doctor |
| `PATCH` | `/api/v1/staff/{uuid}` | clinic/doctor |
| `PATCH` | `/api/v1/staff/{uuid}/toggle` | clinic/doctor |
#### Frontend
- صفحه: `StaffPage.tsx` — لیست با DataTable + Modal ایجاد/ویرایش
- در Sidebar زیر بخش «تنظیمات»
---
### اپیک ۲ — پنل اشتراکی (Subscription Tiers)
#### توضیح
سه سطح پنل وجود دارد. برای هر پنل، ادمین می‌تواند چند **دوره زمانی** با قیمت‌های مختلف تعریف کند (مثلاً ۱ ماهه، ۶ ماهه، ۱۲ ماهه). کلینیک/دکتر دوره و پنل مورد نظر را انتخاب و پرداخت می‌کند.
#### پنل‌ها
| سطح | نام | قابلیت‌ها |
|-----|-----|----------|
| 0 | Free | نوبت‌دهی + ۱ منشی — رایگان، بدون زمان انقضا |
| 1 | Basic | ۲ منشی + پرونده بیمار + مدیریت سرویس‌ها |
| 2 | Professional | ۵ منشی + پرونده بیمار + مدیریت سرویس‌ها |
#### دوره‌های اشتراک (مثال)
| دوره | مدت (ماه) | قیمت Basic | قیمت Professional |
|------|-----------|-----------|------------------|
| ۱ ماهه | 1 | ادمین تعیین می‌کند | ادمین تعیین می‌کند |
| ۳ ماهه | 3 | ادمین تعیین می‌کند | ادمین تعیین می‌کند |
| ۶ ماهه | 6 | ادمین تعیین می‌کند | ادمین تعیین می‌کند |
| ۱۲ ماهه | 12 | ادمین تعیین می‌کند | ادمین تعیین می‌کند |
> ادمین می‌تواند دوره‌های دلخواه اضافه، ویرایش یا غیرفعال کند.
#### نیازمندی‌های کارکردی
- **F2.1** — ادمین برای هر ترکیب (پنل × دوره) یک قیمت تعریف می‌کند
- **F2.2** — ادمین می‌تواند دوره‌های جدید اضافه کند (مثلاً ۲ ماهه) یا دوره‌ای را غیرفعال کند
- **F2.3** — کلینیک/دکتر پنل + دوره را انتخاب و از طریق gateway موجود (Mellat/SEP) پرداخت می‌کند
- **F2.4** — پس از پرداخت موفق، اشتراک فعال می‌شود: `starts_at = now`, `expires_at = now + duration_months * 30 * 86400`
- **F2.5** — تمدید: اگر اشتراک فعال باشد، `starts_at` از تاریخ انقضای قبلی محاسبه می‌شود (نه از now)
- **F2.6** — gate: `hasFeature('patient_records')` — بررسی اشتراک فعال و expired نبودن
- **F2.7** — گزارش فروش پنل‌ها در داشبورد ادمین (تعداد، درآمد، بر اساس دوره و سطح)
- **F2.8** — نمایش تاریخ انقضا و هشدار ۷ روز قبل به کلینیک/دکتر
#### موجودیت‌های جدید مورد نیاز
```
SubscriptionPlan:
- id, uuid
- name: 'free' | 'basic' | 'professional'
- level: int (0/1/2)
- max_secretaries: int (1/2/5)
- features: json { patient_records: bool, services: bool, sms_panel: bool }
- active: bool
- created_at, updated_at
SubscriptionPeriod:
- id, uuid
- plan_id (FK → subscription_plans)
- label: string (مثال: "۶ ماهه", "سالانه")
- duration_months: int (1, 3, 6, 12, ...)
- price_rials: int (قیمت این دوره برای این پنل)
- active: bool
- sort_order: int (ترتیب نمایش)
- created_at, updated_at
ClinicSubscription:
- id, uuid
- entity_type: 'doctor' | 'clinic'
- entity_id: int
- plan_id (FK → subscription_plans)
- period_id (FK → subscription_periods)
- payment_id (FK → payments, nullable)
- starts_at: int (Unix timestamp)
- expires_at: int (Unix timestamp — null = بی‌نهایت برای Free)
- created_at
```
#### وابستگی به Payment موجود
- `Payment.type = 'subscription'` ✅ (از قبل موجود)
- Callback در `PaymentController::subscriptionCallback()`: ایجاد `ClinicSubscription` پس از تأیید
#### API endpoints
| Method | Path | Permission |
|--------|------|-----------|
| `GET` | `/api/v1/subscription/plans` | public — لیست پنل‌ها با دوره‌ها و قیمت‌ها |
| `GET` | `/api/v1/subscription/my` | clinic/doctor — اشتراک فعال + تاریخ انقضا |
| `POST` | `/api/v1/subscription-payment` | clinic/doctor — `{ period_uuid }` |
| `GET` | `/api/v1/admin/subscription/plans` | admin — مدیریت پنل‌ها |
| `POST` | `/api/v1/admin/subscription/plan` | admin |
| `PATCH` | `/api/v1/admin/subscription/plan/{uuid}` | admin |
| `POST` | `/api/v1/admin/subscription/period` | admin — افزودن دوره جدید |
| `PATCH` | `/api/v1/admin/subscription/period/{uuid}` | admin — ویرایش قیمت/مدت |
| `DELETE` | `/api/v1/admin/subscription/period/{uuid}` | admin — غیرفعال‌سازی |
| `GET` | `/api/v1/admin/subscription/report` | admin — گزارش فروش |
#### Frontend
- صفحه کلینیک/دکتر: `SubscriptionPage.tsx`
- نمایش پنل فعال + تاریخ انقضا (شمسی)
- جدول مقایسه پنل‌ها با انتخاب دوره (مثل toggle ماهانه/سالانه)
- دکمه خرید → redirect به gateway
- صفحه ادمین: `AdminSubscriptionPage.tsx`
- تب «پنل‌ها»: ویرایش قیمت دوره‌ها (DataTable با inline edit)
- تب «گزارش»: فروش بر اساس بازه زمانی، سطح، دوره
---
### اپیک ۳ — منشی (Secretary) — تکمیل
#### توضیح
جدول `doctor_secretaries` و entity `DoctorSecretary` موجود است. نیاز به:
- محدودیت تعداد منشی بر اساس پنل
- صفحه فرانت‌اند کامل برای مدیریت منشیان و تنظیم دسترسی‌ها
#### نیازمندی‌های کارکردی
- **F3.1** — بررسی سقف تعداد منشی قبل از ایجاد (free→1, basic→2, pro→5)
- **F3.2** — UI کامل: لیست منشیان + اضافه کردن + ویرایش دسترسی + غیرفعال‌سازی
- **F3.3** — پنل دسترسی: checkbox matrix برای هر منشی (permissions JSON موجود)
- نوبت‌ها: view, create, cancel, update_status
- آدرس‌ها: view, create, update, delete
- اطلاعات کلینیک: view, update
- بیمه‌ها: view, create, update, delete
- **F3.4** — منشی هرگز حذف نمی‌شود (soft delete با `active: false`)
#### وضعیت فعلی
```php
// DoctorSecretary.DEFAULT_PERMISSIONS — موجود
// API موجود: GET /api/v1/secretaries/{doctorUuid}, POST /api/v1/secretary, PATCH, DELETE
// صفحه SecretariesPage.tsx موجود — نیاز به بررسی کامل بودن
```
#### تغییرات مورد نیاز
- Backend: اضافه کردن بررسی سقف پنل در `SecretaryController::create()`
- Frontend: ویرایشگر گرافیکی permissions (checkbox matrix در Modal)
---
### اپیک ۴ — سرویس‌های کلینیک (Clinic Services)
#### توضیح
دسترسی فقط در پنل Basic و بالاتر. سرویس‌ها در دو سطح تعریف می‌شوند: بخش و زیر بخش.
#### ساختار سرویس
```
ServiceSection (بخش — مثال: تزریقات، پانسمان)
└── ServiceItem (زیر بخش — مثال: سرم)
- section_id (FK)
- staff_id (FK → clinic_staff) — پرسنل انجام‌دهنده
- price (ریال)
- active
```
#### نیازمندی‌های کارکردی
- **F4.1** — CRUD بخش‌ها (ServiceSection)
- **F4.2** — CRUD زیر بخش‌ها (ServiceItem) با انتخاب بخش و پرسنل
- **F4.3** — قیمت هر زیر بخش قابل ویرایش
- **F4.4** — gate: `hasFeature('services')` — بدون Basic+ → 403
#### موجودیت‌های جدید مورد نیاز
```
ServiceSection:
- id, uuid
- entity_type: 'doctor' | 'clinic', entity_id
- name
- active, created_at, updated_at
ServiceItem:
- id, uuid
- section_id (FK → service_sections)
- staff_id (FK → clinic_staff, nullable)
- name, price_rials
- active, created_at, updated_at
```
#### API endpoints
| Method | Path | Permission |
|--------|------|-----------|
| `GET` | `/api/v1/service-sections` | clinic/doctor |
| `POST` | `/api/v1/service-section` | clinic/doctor (basic+) |
| `PATCH` | `/api/v1/service-section/{uuid}` | clinic/doctor |
| `DELETE` | `/api/v1/service-section/{uuid}` | clinic/doctor |
| `GET` | `/api/v1/service-items/{sectionUuid}` | clinic/doctor |
| `POST` | `/api/v1/service-item` | clinic/doctor (basic+) |
| `PATCH` | `/api/v1/service-item/{uuid}` | clinic/doctor |
| `DELETE` | `/api/v1/service-item/{uuid}` | clinic/doctor |
#### Frontend
- صفحه: `ServicesPage.tsx` با دو سطح accordion: بخش‌ها و زیر بخش‌ها
---
### اپیک ۵ — پنل پیامکی (SMS Panel)
#### توضیح
زیرساخت SMS (SmsLog, SmsTemplate, SmsProvider) موجود است. باید پنل شارژی برای هر کلینیک/دکتر اضافه شود.
#### نیازمندی‌های کارکردی
- **F5.1** — کیف پول پیامک جداگانه از کیف پول مالی (یا همان wallet با type مجزا)
- **F5.2** — شارژ کردن پنل پیامک از طریق gateway موجود
- **F5.3** — ادمین تعرفه هر پیامک را در SiteConfig تعیین می‌کند (`sms_price_rials`)
- **F5.4** — کسر خودکار هزینه از پنل پس از هر ارسال موفق
- **F5.5** — تنظیمات ارسال پیامک (در صفحه تنظیمات کلینیک):
- یادآوری نوبت: فعال/غیرفعال + چند ساعت قبل
- پیام بعد از ویزیت: فعال/غیرفعال + متن قابل تنظیم
- **F5.6** — گزارش لاگ پیامک‌های هر کلینیک/دکتر
- **F5.7** — گزارش ادمین: درآمد پیامکی، مصرف هر کلینیک
#### موجودیت‌های جدید/تغییر یافته
```
SmsWallet (یا افزودن فیلد به Doctor/Clinic):
- entity_type, entity_id
- balance_rials (موجودی)
- created_at, updated_at
SmsSettings:
- entity_type, entity_id
- reminder_enabled (bool)
- reminder_hours_before (int)
- post_visit_enabled (bool)
- post_visit_text (text)
- updated_at
```
#### API endpoints
| Method | Path | Permission |
|--------|------|-----------|
| `GET` | `/api/v1/sms/wallet/balance` | clinic/doctor |
| `POST` | `/api/v1/sms/wallet/charge` | clinic/doctor |
| `GET` | `/api/v1/sms/wallet/logs` | clinic/doctor |
| `GET` | `/api/v1/sms/settings` | clinic/doctor |
| `PATCH` | `/api/v1/sms/settings` | clinic/doctor |
| `GET` | `/api/v1/admin/sms/wallet-report` | admin |
#### Frontend
- تب «پیامک» در صفحه تنظیمات کلینیک/دکتر
- نمایش موجودی + دکمه شارژ + لاگ
---
### اپیک ۶ — پرونده بیمار (Patient Records)
#### توضیح
دسترسی در پنل Basic+. پرونده بیمار به صورت خودکار پس از تأیید نوبت ایجاد می‌شود یا دستی توسط منشی/دکتر.
#### ساختار پرونده
```
PatientRecord (پرونده بیمار):
- entity_type, entity_id (کلینیک/مطب که پرونده به آن تعلق دارد)
- user_id (FK → users — بیمار)
- created_by (منشی/دکتر که ایجاد کرده)
- created_at
PatientSession (مراجعه/ویزیت):
- record_id (FK → patient_records)
- appointment_id (FK → appointments, nullable — اگر از نوبت)
- insurance_base_id (FK → categories, bundle='insurance_type')
- insurance_supplementary_id (FK → categories)
- visit_price_rials (مبلغ کل ویزیت)
- base_insurance_discount_percent (کسر بیمه پایه)
- supplementary_discount_percent (کسر بیمه مکمل)
- final_price_rials (مبلغ نهایی پس از کسورات)
- payment_method: 'cash' | 'card' | 'insurance' | 'pending'
- notes (text)
- created_at, updated_at
SessionService (سرویس انجام‌شده در یک مراجعه):
- session_id (FK → patient_sessions)
- service_item_id (FK → service_items)
- staff_id (FK → clinic_staff — پرسنل انجام‌دهنده)
- price_rials (قیمت در زمان ارائه)
- created_at
```
#### نیازمندی‌های کارکردی
- **F6.1** — ایجاد خودکار PatientRecord پس از تأیید appointment (اگر وجود ندارد)
- **F6.2** — ایجاد دستی توسط منشی یا دکتر
- **F6.3** — ثبت سشن جدید با:
- انتخاب بیمه پایه و تکمیلی (از categories)
- تعیین مبلغ ویزیت (از پیش تعیین‌شده یا قابل ویرایش)
- محاسبه خودکار مبلغ نهایی
- ثبت سرویس‌های انجام شده (از ServiceItems)
- تعیین پرسنل انجام‌دهنده
- **F6.4** — gate: `hasFeature('patient_records')` — بدون Basic+ → 403
- **F6.5** — تاریخچه مراجعات یک بیمار قابل نمایش
#### API endpoints
| Method | Path | Permission |
|--------|------|-----------|
| `GET` | `/api/v1/patients` | clinic/doctor (basic+) |
| `POST` | `/api/v1/patient` | clinic/doctor (basic+) |
| `GET` | `/api/v1/patient/{uuid}` | clinic/doctor |
| `GET` | `/api/v1/patient/{uuid}/sessions` | clinic/doctor |
| `POST` | `/api/v1/patient/{uuid}/session` | clinic/doctor |
| `PATCH` | `/api/v1/session/{uuid}` | clinic/doctor |
#### Frontend
- صفحه `PatientsPage.tsx` — لیست بیماران با جستجو
- صفحه `PatientDetailPage.tsx` — پرونده کامل + تاریخچه مراجعات
- Modal ثبت سشن جدید
---
### اپیک ۷ — داشبورد هوشمند (Smart Dashboard)
#### توضیح
هر داشبورد (ادمین، کلینیک، مطب، منشی) باید چارت‌های مناسب با فیلترهای role-based داشته باشد.
#### نیازمندی‌های کارکردی
- **F7.1** — داشبورد ادمین:
- چارت: درآمد پلتفرم (ماهانه، هفتگی)
- چارت: نوبت‌های ثبت‌شده در بازه زمانی
- آمار: تعداد کلینیک‌ها، دکترها، کاربران فعال
- گزارش پنل‌های فروخته‌شده
- **F7.2** — داشبورد کلینیک/مطب:
- نوبت‌های امروز (موجود)
- درآمد ماه جاری
- تعداد بیماران منحصربه‌فرد
- موجودی پیامک
- **F7.3** — فیلتر بازه زمانی در همه چارت‌ها (این هفته / این ماه / ۳ ماه / سفارشی)
- **F7.4** — فیلتر بر اساس نقش (منشی فقط نوبت‌ها را می‌بیند)
---
## اولویت‌بندی پیاده‌سازی
| اولویت | اپیک | دلیل |
|--------|------|-------|
| 1 | اپیک ۱ (Staff) | پیش‌نیاز اپیک ۴ و ۶ |
| 2 | اپیک ۲ (Subscription) | پیش‌نیاز gate checks |
| 3 | اپیک ۳ (Secretary - تکمیل) | وابسته به پنل |
| 4 | اپیک ۴ (Services) | پیش‌نیاز اپیک ۶ |
| 5 | اپیک ۵ (SMS Panel) | مستقل از بقیه |
| 6 | اپیک ۶ (Patient Records) | بیشترین وابستگی |
| 7 | اپیک ۷ (Dashboard) | آخر — نیاز به داده واقعی |
---
## وابستگی‌های فنی
### وضعیت زیرساخت موجود و قابل استفاده
| زیرساخت | فایل | نحوه استفاده در فاز ۲ |
|---------|------|----------------------|
| Payment gateway | `src/Payment/` | پرداخت پنل اشتراکی و شارژ SMS |
| Wallet | `src/Settlement/Entity/WalletTransaction.php` | بررسی مدل برای SMS wallet |
| SiteConfig | `src/Config/Entity/SiteConfig.php` | ذخیره قیمت پنل‌ها + تعرفه SMS |
| Categories (insurance) | `bundle='insurance_type'` | انتخاب بیمه در سشن |
| DoctorSecretary | `src/Secretary/Entity/DoctorSecretary.php` | افزودن بررسی سقف پنل |
| SmsLog/SmsTemplate | `src/Sms/Entity/` | زیرساخت ارسال موجود |
### نکات معماری
- همه entity‌های جدید از الگوی Unix timestamp استفاده می‌کنند
- همه controller‌های جدید از `BaseController` ارث می‌برند
- gate check برای features با یک helper در `BaseController` یا `SubscriptionService` پیاده‌سازی شود
- `entity_type` + `entity_id` pattern (polymorphic) برای Staff، Services، SmsSettings، Patient Records
---
## معیارهای پذیرش (Definition of Done)
برای هر اپیک:
- [ ] Entity + Migration ایجاد شده
- [ ] Controller + Repository + Service نوشته شده
- [ ] API endpoints تست‌پذیر در Swagger (`/api/doc`)
- [ ] Frontend page کامل با CRUD
- [ ] مستندات `docs/api/` به‌روزرسانی شده
- [ ] بررسی gate/permission برای plan-gated features