Update PRD for ClinicPro Phase 2: Enhance project requirements, add new modules, and refine existing functionalities. Include detailed specifications for Staff, Subscription tiers, Clinic services, Patient records, SMS panel, and more. Revise API documentation and frontend requirements for better clarity and implementation guidance.

This commit is contained in:
hamed
2026-06-14 20:53:49 +03:30
parent a1d3442b9b
commit 6ddc66ad74
+392 -321
View File
@@ -1,449 +1,520 @@
# PRD — ClinicPro Phase 2 # PRD — ClinicPro Phase 2
**تاریخ:** ۲۴ خرداد ۱۴۰۵ **تاریخ:** ۲۴ خرداد ۱۴۰۵
**نسخه:** 1.0 **نسخه:** 1.2
**وضعیت:** Draft **وضعیت:** Draft
--- ---
## خلاصه اجرایی ## خلاصه اجرایی
این سند نیازمندی‌های فاز ۲ پلتفرم **ClinicPro** را پوشش می‌دهد. هدف: تبدیل پلتفرم از یک سیستم نوبت‌دهی ساده به یک سیستم مدیریت کلینیک کامل با پنل‌های اشتراکی، پرونده بیمار، مدیریت پرسنل و پنل پیامکی. فاز ۲ پلتفرم **ClinicPro** را از یک سیستم نوبت‌دهی ساده به یک سیستم مدیریت کلینیک کامل تبدیل می‌کند. شامل پنل‌های اشتراکی، مدیریت پرسنل، پرونده بیمار، سرویس‌های درمانی و پنل پیامکی اختصاصی برای هر کلینیک/مطب.
--- ---
## وضعیت فعلی پروژه ## وضعیت فعلی پروژه
| ماژول | وضعیت | | ماژول | Backend | API Doc |
|--------|--------| |--------|---------|---------|
| Auth (OTP + JWT) | ✅ کامل | | Auth (OTP + JWT + switch-context) | ✅ کامل | `docs/api/auth.md` |
| Doctor profile | ✅ کامل | | Doctor profile | ✅ کامل | `docs/api/doctor.md` |
| Clinic management | ✅ کامل | | Clinic management | ✅ کامل | `docs/api/clinic.md` |
| Appointment booking | ✅ کامل | | Appointment booking + settings | ✅ کامل | `docs/api/appointment.md`, `appointment-settings.md` |
| Secretary entity (DoctorSecretary) | ✅ موجود — نیاز به UI و permissions بیشتر | | Secretary (`DoctorSecretary` entity) | ✅ entity + API موجود | `docs/api/secretary.md` |
| Payment gateway (Mellat, SEP) | ✅ کامل — برای نوبت + subscription | | Payment gateway (Mellat, SEP) | ✅ کامل — `Payment.type='subscription'` هم موجود | `docs/api/payment.md` |
| SMS (SmsLog, SmsTemplate) | ✅ زیرساخت موجود — نیاز به پنل کلینیک | | SMS log + template | ✅ زیرساخت موجود | `docs/api/sms.md` |
| Wallet / Settlement | ✅ کامل | | Wallet / Settlement | ✅ کامل | `docs/api/settlement.md` |
| Site config (SiteConfig key-value) | ✅ موجود | | SiteConfig (key-value store) | ✅ موجود | `docs/api/admin.md` §Settings |
| **Staff (پرسنل)** | ❌ موجود نیست | | Dashboard (clinic/doctor/secretary) | ✅ کامل | `docs/api/dashboard.md` |
| **Panel subscription tiers** | ❌ موجود نیست | | Rating, Blog, Representation, Location | ✅ کامل | فایل‌های مربوطه در `docs/api/` |
| **Patient records (پرونده بیمار)** | ❌ موجود نیست | | **Staff (پرسنل)** | ❌ entity وجود ندارد | ❌ فایل doc وجود ندارد |
| **Clinic services (خدمات)** | ❌ موجود نیست | | **Subscription tiers** | ❌ entity وجود ندارد | ❌ فایل doc وجود ندارد |
| **SMS panel per clinic** | ❌ UI نیاز دارد | | **Clinic services** | ❌ entity وجود ندارد | ❌ فایل doc وجود ندارد |
| **Patient records** | ❌ entity وجود ندارد | ❌ فایل doc وجود ندارد |
| **SMS wallet/settings per clinic** | ❌ entity وجود ندارد | ❌ بخش در sms.md وجود ندارد |
--- ---
## اپیک‌ها و قابلیت‌ها ## اپیک‌ها
--- ---
### اپیک ۱ — پرسنل (Staff) ### اپیک ۱ — پرسنل (Staff)
#### توضیح #### توضیح
مدیر کلینیک یا دکتر مستقل می‌تواند پرسنلی که نیازی به لاگین ندارند را ثبت کند. این پرسنل در سرویس‌دهی، سشن‌ها و گزارشات استفاده می‌شوند. مدیر کلینیک یا دکتر مستقل می‌تواند پرسنل کلینیک را ثبت کند. پرسنل حساب کاربری ندارند و نیازی به لاگین ندارند. در سرویس‌دهی، سشن‌ها و گزارشات به آن‌ها ارجاع می‌شود.
#### نیازمندی‌های کارکردی #### نیازمندی‌های کارکردی
- **F1.1** — ایجاد / ویرایش / غیرفعال‌سازی پرسنل (حذف نرم — soft delete)
- **F1.2** — فیلدهای پرسنل:
- نام و نام خانوادگی (اجباری)
- شماره تماس
- عنوان شغل (مثال: پرستار، منشی، تکنسین)
- آدرس
- کد ملی
- فعال / غیرفعال
- **F1.3** — پرسنل به کلینیک یا مطب دکتر وابسته است (FK به clinic_id یا doctor_id)
- **F1.4** — لیست پرسنل فقط در scope کلینیک/مطب فعال نمایش داده می‌شود
- **F1.5** — در ایجاد سرویس، از لیست پرسنل انتخاب می‌شود
#### موجودیت‌های جدید مورد نیاز | کد | نیازمندی |
|----|---------|
| F1.1 | ایجاد / ویرایش / غیرفعال‌سازی پرسنل — **حذف سخت ممنوع** (تاریخچه حفظ شود) |
| F1.2 | فیلدها: نام و نام‌خانوادگی (اجباری)، شماره تماس، عنوان شغل، آدرس، کد ملی، وضعیت فعال |
| F1.3 | پرسنل به `entity_type + entity_id` وابسته است (کلینیک یا مطب) |
| F1.4 | لیست پرسنل فقط در scope کلینیک/مطب کاربر جاری نمایش داده می‌شود |
| F1.5 | در ایجاد ServiceItem و SessionService از لیست پرسنل انتخاب می‌شود |
#### Entity جدید
``` ```
ClinicStaff: clinic_staff:
- id, uuid id, uuid
- clinic_id (FK → clinics) OR doctor_id (FK → doctors) [یکی از دو] entity_type: 'doctor' | 'clinic'
- full_name, phone, job_title, address, national_code entity_id: int
- active (bool, default: true) full_name: string (required)
- created_at, updated_at (Unix timestamp) phone: string|null
job_title: string|null
address: text|null
national_code: string(10)|null
active: bool = true
created_at, updated_at: int (Unix)
``` ```
#### API endpoints #### API
| Method | Path | Permission | | Method | Path | توضیح |
|--------|------|-----------| |--------|------|-------|
| `GET` | `/api/v1/staff` | clinic/doctor | | `GET` | `/api/v1/staff` | لیست پرسنل کاربر جاری |
| `POST` | `/api/v1/staff` | clinic/doctor | | `POST` | `/api/v1/staff` | ایجاد پرسنل جدید |
| `PATCH` | `/api/v1/staff/{uuid}` | clinic/doctor | | `PATCH` | `/api/v1/staff/{uuid}` | ویرایش اطلاعات |
| `PATCH` | `/api/v1/staff/{uuid}/toggle` | clinic/doctor | | `PATCH` | `/api/v1/staff/{uuid}/toggle` | فعال/غیرفعال (soft delete) |
#### Frontend #### Frontend
- صفحه: `StaffPage.tsx` لیست با DataTable + Modal ایجاد/ویرایش - `StaffPage.tsx` — DataTable + Modal ایجاد/ویرایش
- در Sidebar زیر بخش «تنظیمات» - Sidebar: زیر «تنظیمات»
#### فایل‌های جدید
- `src/Staff/Entity/ClinicStaff.php`
- `src/Staff/Controller/StaffController.php`
- `src/Staff/Repository/ClinicStaffRepository.php`
- `docs/api/staff.md`**باید ایجاد شود**
--- ---
### اپیک ۲ — پنل اشتراکی (Subscription Tiers) ### اپیک ۲ — پنل اشتراکی (Subscription Tiers)
#### توضیح #### توضیح
سه سطح پنل وجود دارد. برای هر پنل، ادمین می‌تواند چند **دوره زمانی** با قیمت‌های مختلف تعریف کند (مثلاً ۱ ماهه، ۶ ماهه، ۱۲ ماهه). کلینیک/دکتر دوره و پنل مورد نظر را انتخاب و پرداخت می‌کند. سه سطح پنل با قابلیت‌های متفاوت. ادمین برای هر پنل دوره‌های زمانی با قیمت مشخص تعریف می‌کند. کلینیک/دکتر دوره + پنل انتخاب کرده و پرداخت می‌کند.
#### پنل‌ها #### سطوح پنل
| سطح | نام | قابلیت‌ها |
|-----|-----|----------|
| 0 | Free | نوبت‌دهی + ۱ منشی — رایگان، بدون زمان انقضا |
| 1 | Basic | ۲ منشی + پرونده بیمار + مدیریت سرویس‌ها |
| 2 | Professional | ۵ منشی + پرونده بیمار + مدیریت سرویس‌ها |
#### دوره‌های اشتراک (مثال) | Level | Name | منشی | قابلیت‌های اضافه |
| دوره | مدت (ماه) | نوع | قیمت | |-------|------|------|----------------|
|------|-----------|-----|------| | 0 | Free | ۱ منشی | نوبت‌دهی — رایگان، بدون انقضا |
| تریال | 1 | trial — رایگان | ۰ ریال | | 1 | Basic | ۲ منشی | پرونده بیمار + سرویس‌ها |
| ۱ ماهه | 1 | paid | ادمین تعیین می‌کند | | 2 | Professional | ۵ منشی | پرونده بیمار + سرویس‌ها |
| ۳ ماهه | 3 | paid | ادمین تعیین می‌کند |
| ۶ ماهه | 6 | paid | ادمین تعیین می‌کند |
| ۱۲ ماهه | 12 | paid | ادمین تعیین می‌کند |
> ادمین می‌تواند دوره‌های دلخواه اضافه، ویرایش یا غیرفعال کند. #### دوره‌های اشتراک (مثال — ادمین تنظیم می‌کند)
#### دوره تریال | نوع | مدت | قیمت |
- تریال فقط برای پنل **Basic** فعال است |-----|-----|------|
- هر کلینیک/دکتر فقط **یک بار** می‌تواند از تریال استفاده کند (بررسی در `ClinicSubscription`) | تریال | ۱ ماه | ۰ ریال — فقط برای Basic، یک‌بار |
- تریال نیازی به پرداخت ندارد — مستقیم فعال می‌شود | ۱ ماهه | ۱ ماه | ادمین تعیین می‌کند |
- ادمین می‌تواند تریال را به‌طور کلی فعال/غیرفعال کند (`trial_enabled` در SiteConfig) | ۳ ماهه | ۳ ماه | ادمین تعیین می‌کند |
- بعد از انقضای تریال، سیستم به پنل Free بازمی‌گردد (نه حذف داده) | ۶ ماهه | ۶ ماه | ادمین تعیین می‌کند |
| ۱۲ ماهه | ۱۲ ماه | ادمین تعیین می‌کند |
#### قوانین تریال
- فقط برای پنل **Basic**
- هر کلینیک/دکتر **یک‌بار** — بعد از استفاده، دکمه تریال نمایش داده نمی‌شود
- بدون پرداخت — endpoint مستقل دارد
- بعد از انقضا → برگشت به Free (داده‌ها حفظ می‌شوند)
- ادمین می‌تواند تریال را کلاً غیرفعال کند (`trial_enabled` در SiteConfig)
#### نیازمندی‌های کارکردی #### نیازمندی‌های کارکردی
- **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** — نمایش تاریخ انقضا و هشدار ۷ روز قبل به کلینیک/دکتر
- **F2.9** — تریال Basic: فعال‌سازی رایگان یک‌بار — endpoint جداگانه `POST /api/v1/subscription/trial`
- **F2.10** — بررسی استفاده قبلی از تریال قبل از فعال‌سازی (`used_trial: bool` روی entity)
#### موجودیت‌های جدید مورد نیاز | کد | نیازمندی |
|----|---------|
| F2.1 | ادمین قیمت هر ترکیب (پنل × دوره) را تعریف می‌کند |
| F2.2 | ادمین می‌تواند دوره‌های جدید اضافه یا غیرفعال کند |
| F2.3 | پرداخت از طریق gateway موجود (Mellat/SEP) — `Payment.type='subscription'` ✅ |
| F2.4 | پس از پرداخت موفق: `starts_at=now`, `expires_at=now + months*30*86400` |
| F2.5 | تمدید از تاریخ انقضای قبلی محاسبه می‌شود (نه از now) |
| F2.6 | gate check: `hasFeature('patient_records')` — اشتراک فعال و منقضی نشده |
| F2.7 | هشدار ۷ روز قبل از انقضا به کلینیک/دکتر |
| F2.8 | گزارش فروش برای ادمین (تعداد، درآمد، بر اساس سطح و دوره) |
#### Entities جدید
``` ```
SubscriptionPlan: subscription_plans:
- id, uuid id, uuid
- name: 'free' | 'basic' | 'professional' name: 'free' | 'basic' | 'professional'
- level: int (0/1/2) level: int (0/1/2)
- max_secretaries: int (1/2/5) max_secretaries: int (1/2/5)
- features: json { patient_records: bool, services: bool, sms_panel: bool } features: json { patient_records, services, sms_panel }
- active: bool active: bool
- created_at, updated_at created_at, updated_at
SubscriptionPeriod: subscription_periods:
- id, uuid id, uuid
- plan_id (FK → subscription_plans) plan_id → subscription_plans
- label: string (مثال: "۶ ماهه", "تریال ۱ ماهه") label: string ('۶ ماهه', 'تریال ۱ ماهه')
- duration_months: int (1, 3, 6, 12, ...) duration_months: int
- price_rials: int (0 برای تریال) price_rials: int 0 برای تریال
- is_trial: bool (default: false — فقط یک دوره trial در Basic) is_trial: bool = false
- active: bool active: bool
- sort_order: int (ترتیب نمایش) sort_order: int
- created_at, updated_at created_at, updated_at
ClinicSubscription: clinic_subscriptions:
- id, uuid id, uuid
- entity_type: 'doctor' | 'clinic' entity_type: 'doctor' | 'clinic'
- entity_id: int entity_id: int
- plan_id (FK → subscription_plans) plan_id → subscription_plans
- period_id (FK → subscription_periods) period_id → subscription_periods
- payment_id (FK → payments, nullable — برای تریال null است) payment_id → payments nullable (تریال = null)
- is_trial: bool (کپی از period — برای گزارش راحت‌تر) is_trial: bool
- starts_at: int (Unix timestamp) starts_at: int
- expires_at: int (Unix timestamp — null = بی‌نهایت برای Free) expires_at: int null = بی‌نهایت (Free)
- created_at created_at
``` ```
#### وابستگی به Payment موجود #### API
- `Payment.type = 'subscription'` ✅ (از قبل موجود) | Method | Path | Permission | توضیح |
- Callback در `PaymentController::subscriptionCallback()`: ایجاد `ClinicSubscription` پس از تأیید |--------|------|-----------|-------|
| `GET` | `/api/v1/subscription/plans` | public | لیست پنل‌ها با دوره‌ها و قیمت‌ها |
#### API endpoints | `GET` | `/api/v1/subscription/my` | clinic/doctor | اشتراک فعال + `used_trial` + تاریخ انقضا |
| Method | Path | Permission | | `POST` | `/api/v1/subscription/trial` | clinic/doctor | فعال‌سازی تریال (رایگان، یک‌بار) |
|--------|------|-----------| | `POST` | `/api/v1/subscription-payment` | clinic/doctor | شروع پرداخت — body: `{ period_uuid }` |
| `GET` | `/api/v1/subscription/plans` | public — لیست پنل‌ها با دوره‌ها، قیمت‌ها و دوره تریال | | `GET` | `/api/v1/admin/subscription/plans` | admin | لیست پنل‌ها |
| `GET` | `/api/v1/subscription/my` | clinic/doctor — اشتراک فعال + تاریخ انقضا + `used_trial` | | `POST` | `/api/v1/admin/subscription/plan` | admin | ایجاد پنل |
| `POST` | `/api/v1/subscription/trial` | clinic/doctor — فعال‌سازی تریال Basic (رایگان، یک‌بار) | | `PATCH` | `/api/v1/admin/subscription/plan/{uuid}` | admin | ویرایش پنل |
| `POST` | `/api/v1/subscription-payment` | clinic/doctor — `{ period_uuid }` | | `POST` | `/api/v1/admin/subscription/period` | admin | افزودن دوره جدید |
| `GET` | `/api/v1/admin/subscription/plans` | admin — مدیریت پنل‌ها | | `PATCH` | `/api/v1/admin/subscription/period/{uuid}` | admin | ویرایش قیمت/مدت |
| `POST` | `/api/v1/admin/subscription/plan` | admin | | `DELETE` | `/api/v1/admin/subscription/period/{uuid}` | admin | غیرفعال‌سازی دوره |
| `PATCH` | `/api/v1/admin/subscription/plan/{uuid}` | admin | | `GET` | `/api/v1/admin/subscription/report` | 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 #### Frontend
- صفحه کلینیک/دکتر: `SubscriptionPage.tsx` - `SubscriptionPage.tsx`:
- اگر `used_trial = false`: بنر «۱ ماه رایگان امتحان کنید» با دکمه فعال‌سازی تریال - بنر تریال اگر `used_trial=false`
- نمایش پنل فعال + تاریخ انقضا (شمسی) + badge «تریال» اگر is_trial باشد - وضعیت پنل فعال + تاریخ انقضا (شمسی) + badge «تریال»
- جدول مقایسه پنل‌ها با انتخاب دوره (toggle) - جدول مقایسه پنل‌ها با انتخاب دوره
- دکمه خرید → redirect به gateway - دکمه خرید → redirect به gateway
- صفحه ادمین: `AdminSubscriptionPage.tsx` - `AdminSubscriptionPage.tsx`:
- تب «پنل‌ها»: ویرایش قیمت دوره‌ها + مشخص کردن کدام دوره تریال است - تب «پنل‌ها»: ویرایش دوره‌ها و قیمت‌ها
- تب «گزارش»: فروش + تریال‌ها بر اساس بازه زمانی - تب «گزارش»: فروش بر اساس بازه زمانی
#### فایل‌های جدید
- `src/Subscription/Entity/SubscriptionPlan.php`
- `src/Subscription/Entity/SubscriptionPeriod.php`
- `src/Subscription/Entity/ClinicSubscription.php`
- `src/Subscription/Controller/SubscriptionController.php`
- `src/Subscription/Service/SubscriptionService.php`
- `docs/api/subscription.md`**باید ایجاد شود**
--- ---
### اپیک ۳ — منشی (Secretary) — تکمیل ### اپیک ۳ — منشی (Secretary) — تکمیل
#### توضیح #### توضیح
جدول `doctor_secretaries` و entity `DoctorSecretary` موجود است. نیاز به: Entity `DoctorSecretary` و API کامل موجود است (`docs/api/secretary.md`). باید:
- محدودیت تعداد منشی بر اساس پنل - محدودیت تعداد بر اساس پنل اعمال شود
- صفحه فرانت‌اند کامل برای مدیریت منشیان و تنظیم دسترسی‌ها - UI مدیریت permissions تکمیل شود
#### نیازمندی‌های کارکردی #### نیازمندی‌های کارکردی
- **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 — موجود | F3.1 | بررسی سقف پنل قبل از ایجاد: Free→1، Basic→2، Pro→5 |
// API موجود: GET /api/v1/secretaries/{doctorUuid}, POST /api/v1/secretary, PATCH, DELETE | F3.2 | UI لیست منشیان + اضافه‌کردن + ویرایش + غیرفعال‌سازی |
// صفحه SecretariesPage.tsx موجود — نیاز به بررسی کامل بودن | F3.3 | ویرایشگر permissions: checkbox matrix (موجود در `DoctorSecretary.DEFAULT_PERMISSIONS`) |
``` | F3.4 | منشی هرگز حذف نمی‌شود — `active: false` |
#### تغییرات مورد نیاز #### تغییرات مورد نیاز
- Backend: اضافه کردن بررسی سقف پنل در `SecretaryController::create()` - **Backend:** `SecretaryController::create()` — بررسی سقف پنل با `SubscriptionService`
- Frontend: ویرایشگر گرافیکی permissions (checkbox matrix در Modal) - **Frontend:** Modal با checkbox matrix برای permissions در `SecretariesPage.tsx`
#### API موجود (نیاز به تغییر ندارند)
`GET /api/v1/secretaries/{doctorUuid}` | `POST /api/v1/secretary` | `PATCH /api/v1/secretary/{uuid}` | `DELETE /api/v1/secretary/{uuid}`
--- ---
### اپیک ۴ — سرویس‌های کلینیک (Clinic Services) ### اپیک ۴ — سرویس‌های کلینیک (Clinic Services)
#### توضیح #### توضیح
دسترسی فقط در پنل Basic و بالاتر. سرویس‌ها در دو سطح تعریف می‌شوند: بخش و زیر بخش. دسترسی فقط در پنل 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
#### موجودیت‌های جدید مورد نیاز | کد | نیازمندی |
|----|---------|
| F4.1 | CRUD بخش‌ها (ServiceSection) |
| F4.2 | CRUD زیربخش‌ها (ServiceItem) با انتخاب بخش + پرسنل انجام‌دهنده |
| F4.3 | قیمت هر زیربخش قابل ویرایش |
| F4.4 | gate: `hasFeature('services')` — بدون Basic+ → 403 |
| F4.5 | لیست ServiceItems در ثبت سشن بیمار استفاده می‌شود (وابستگی اپیک ۶) |
#### Entities جدید
``` ```
ServiceSection: service_sections:
- id, uuid id, uuid
- entity_type: 'doctor' | 'clinic', entity_id entity_type: 'doctor' | 'clinic'
- name entity_id: int
- active, created_at, updated_at name: string
active: bool
created_at, updated_at
ServiceItem: service_items:
- id, uuid id, uuid
- section_id (FK → service_sections) section_id → service_sections
- staff_id (FK → clinic_staff, nullable) staff_id → clinic_staff nullable
- name, price_rials name: string
- active, created_at, updated_at price_rials: int
active: bool
created_at, updated_at
``` ```
#### API endpoints #### API
| Method | Path | Permission | | Method | Path | توضیح |
|--------|------|-----------| |--------|------|-------|
| `GET` | `/api/v1/service-sections` | clinic/doctor | | `GET` | `/api/v1/service-sections` | لیست بخش‌ها |
| `POST` | `/api/v1/service-section` | clinic/doctor (basic+) | | `POST` | `/api/v1/service-section` | ایجاد بخش (Basic+) |
| `PATCH` | `/api/v1/service-section/{uuid}` | clinic/doctor | | `PATCH` | `/api/v1/service-section/{uuid}` | ویرایش |
| `DELETE` | `/api/v1/service-section/{uuid}` | clinic/doctor | | `DELETE` | `/api/v1/service-section/{uuid}` | حذف |
| `GET` | `/api/v1/service-items/{sectionUuid}` | clinic/doctor | | `GET` | `/api/v1/service-items/{sectionUuid}` | لیست زیربخش‌های یک بخش |
| `POST` | `/api/v1/service-item` | clinic/doctor (basic+) | | `POST` | `/api/v1/service-item` | ایجاد زیربخش (Basic+) |
| `PATCH` | `/api/v1/service-item/{uuid}` | clinic/doctor | | `PATCH` | `/api/v1/service-item/{uuid}` | ویرایش |
| `DELETE` | `/api/v1/service-item/{uuid}` | clinic/doctor | | `DELETE` | `/api/v1/service-item/{uuid}` | حذف |
#### Frontend #### Frontend
- صفحه: `ServicesPage.tsx` با دو سطح accordion: بخش‌ها و زیر بخش‌ها - `ServicesPage.tsx` accordion دو سطحی: بخش‌ها زیربخش‌ها
#### فایل‌های جدید
- `src/ClinicService/Entity/ServiceSection.php`
- `src/ClinicService/Entity/ServiceItem.php`
- `src/ClinicService/Controller/ClinicServiceController.php`
- `docs/api/clinic-services.md`**باید ایجاد شود**
--- ---
### اپیک ۵ — پنل پیامکی (SMS Panel) ### اپیک ۵ — پنل پیامکی (SMS Panel)
#### توضیح #### توضیح
زیرساخت SMS (SmsLog, SmsTemplate, SmsProvider) موجود است. باید پنل شارژی برای هر کلینیک/دکتر اضافه شود. زیرساخت SMS موجود است (`SmsLog`, `SmsTemplate`, `SmsProvider` در `src/Sms/`). باید:
- کیف پول پیامک اختصاصی برای هر کلینیک/دکتر
- تنظیمات ارسال پیامک (یادآوری، بعد از ویزیت)
- گزارش لاگ برای کلینیک و ادمین
#### نیازمندی‌های کارکردی #### نیازمندی‌های کارکردی
- **F5.1** — کیف پول پیامک جداگانه از کیف پول مالی (یا همان wallet با type مجزا)
- **F5.2** — شارژ کردن پنل پیامک از طریق gateway موجود
- **F5.3** — ادمین تعرفه هر پیامک را در SiteConfig تعیین می‌کند (`sms_price_rials`)
- **F5.4** — کسر خودکار هزینه از پنل پس از هر ارسال موفق
- **F5.5** — تنظیمات ارسال پیامک (در صفحه تنظیمات کلینیک):
- یادآوری نوبت: فعال/غیرفعال + چند ساعت قبل
- پیام بعد از ویزیت: فعال/غیرفعال + متن قابل تنظیم
- **F5.6** — گزارش لاگ پیامک‌های هر کلینیک/دکتر
- **F5.7** — گزارش ادمین: درآمد پیامکی، مصرف هر کلینیک
#### موجودیت‌های جدید/تغییر یافته | کد | نیازمندی |
|----|---------|
| F5.1 | کیف پول پیامک جدا از کیف پول مالی |
| F5.2 | شارژ از طریق gateway موجود |
| F5.3 | ادمین تعرفه هر پیامک را تعیین می‌کند (`sms_price_rials` در SiteConfig) |
| F5.4 | کسر خودکار هزینه بعد از هر ارسال موفق |
| F5.5 | تنظیمات: یادآوری نوبت (فعال/غیرفعال + چند ساعت قبل) |
| F5.6 | تنظیمات: پیام بعد از ویزیت (فعال/غیرفعال + متن) |
| F5.7 | گزارش لاگ پیامک‌های کلینیک/مطب |
| F5.8 | گزارش ادمین: مصرف هر کلینیک + درآمد پیامکی |
#### Entities جدید
``` ```
SmsWallet (یا افزودن فیلد به Doctor/Clinic): sms_wallets:
- entity_type, entity_id id
- balance_rials (موجودی) entity_type: 'doctor' | 'clinic'
- created_at, updated_at entity_id: int
balance_rials: int = 0
created_at, updated_at
SmsSettings: sms_settings:
- entity_type, entity_id id
- reminder_enabled (bool) entity_type: 'doctor' | 'clinic'
- reminder_hours_before (int) entity_id: int
- post_visit_enabled (bool) reminder_enabled: bool = false
- post_visit_text (text) reminder_hours_before: int = 2
- updated_at post_visit_enabled: bool = false
post_visit_text: text|null
updated_at
``` ```
#### API endpoints #### API
| Method | Path | Permission | | Method | Path | Permission | توضیح |
|--------|------|-----------| |--------|------|-----------|-------|
| `GET` | `/api/v1/sms/wallet/balance` | clinic/doctor | | `GET` | `/api/v1/sms/wallet/balance` | clinic/doctor | موجودی کیف پول پیامک |
| `POST` | `/api/v1/sms/wallet/charge` | clinic/doctor | | `POST` | `/api/v1/sms/wallet/charge` | clinic/doctor | شارژ — body: `{ gateway, amount_rials }` |
| `GET` | `/api/v1/sms/wallet/logs` | clinic/doctor | | `GET` | `/api/v1/sms/wallet/logs` | clinic/doctor | تاریخچه کسر/شارژ |
| `GET` | `/api/v1/sms/settings` | clinic/doctor | | `GET` | `/api/v1/sms/settings` | clinic/doctor | تنظیمات پیامک |
| `PATCH` | `/api/v1/sms/settings` | clinic/doctor | | `PATCH` | `/api/v1/sms/settings` | clinic/doctor | ذخیره تنظیمات |
| `GET` | `/api/v1/admin/sms/wallet-report` | admin | | `GET` | `/api/v1/admin/sms/wallet-report` | admin | گزارش مصرف و درآمد |
#### Frontend #### Frontend
- تب «پیامک» در صفحه تنظیمات کلینیک/دکتر - تب «پیامک» در صفحه تنظیمات کلینیک/دکتر
- نمایش موجودی + دکمه شارژ + لاگ - نمایش موجودی + دکمه شارژ + لاگ + فرم تنظیمات
#### تأثیر بر فایل‌های موجود
- `docs/api/sms.md`**بخش جدید wallet/settings اضافه شود**
--- ---
### اپیک ۶ — پرونده بیمار (Patient Records) ### اپیک ۶ — پرونده بیمار (Patient Records)
#### توضیح #### توضیح
دسترسی در پنل Basic+. پرونده بیمار به صورت خودکار پس از تأیید نوبت ایجاد می‌شود یا دستی توسط منشی/دکتر. دسترسی فقط در پنل 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 | |----|---------|
|--------|------|-----------| | F6.1 | ایجاد خودکار `PatientRecord` بعد از تأیید appointment (اگر وجود نداشت) |
| `GET` | `/api/v1/patients` | clinic/doctor (basic+) | | F6.2 | ایجاد دستی توسط منشی یا دکتر |
| `POST` | `/api/v1/patient` | clinic/doctor (basic+) | | F6.3 | ثبت سشن: بیمه پایه + مکمل، مبلغ ویزیت (پیش‌فرض یا دستی)، محاسبه خودکار کسورات |
| `GET` | `/api/v1/patient/{uuid}` | clinic/doctor | | F6.4 | ثبت سرویس‌های انجام‌شده در سشن با پرسنل انجام‌دهنده |
| `GET` | `/api/v1/patient/{uuid}/sessions` | clinic/doctor | | F6.5 | gate: `hasFeature('patient_records')` — بدون Basic+ → 403 |
| `POST` | `/api/v1/patient/{uuid}/session` | clinic/doctor | | F6.6 | تاریخچه کامل مراجعات یک بیمار |
| `PATCH` | `/api/v1/session/{uuid}` | clinic/doctor |
#### محاسبه مبلغ نهایی سشن
```
visit_price_rials ← ادمین/دکتر تعیین می‌کند
- base_insurance_discount% ← بیمه پایه
- supplementary_discount% ← بیمه مکمل
+ service_items total ← مجموع سرویس‌های انجام‌شده
= final_price_rials
```
#### Entities جدید
```
patient_records:
id, uuid
entity_type: 'doctor' | 'clinic'
entity_id: int
user_id → users ← بیمار
created_by_type: 'doctor' | 'secretary' | 'system'
created_by_id: int
created_at
patient_sessions:
id, uuid
record_id → patient_records
appointment_id → appointments ← nullable
insurance_base_id → categories (bundle='insurance_type') ← nullable
insurance_supplementary_id → categories ← nullable
visit_price_rials: int
base_insurance_discount_percent: float = 0
supplementary_discount_percent: float = 0
final_price_rials: int
payment_method: 'cash' | 'card' | 'insurance' | 'pending'
notes: text|null
created_at, updated_at
session_services:
id, uuid
session_id → patient_sessions
service_item_id → service_items
staff_id → clinic_staff ← nullable
price_rials: int ← قیمت در زمان ارائه (کپی از service_item)
created_at
```
#### API
| 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 #### Frontend
- صفحه `PatientsPage.tsx` — لیست بیماران با جستجو - `PatientsPage.tsx` — لیست بیماران + جستجو
- صفحه `PatientDetailPage.tsx` — پرونده کامل + تاریخچه مراجعات - `PatientDetailPage.tsx` — پرونده + تاریخچه مراجعات
- Modal ثبت سشن جدید - Modal ثبت سشن جدید (بیمه + ویزیت + سرویس‌ها)
#### فایل‌های جدید
- `src/Patient/Entity/PatientRecord.php`
- `src/Patient/Entity/PatientSession.php`
- `src/Patient/Entity/SessionService.php`
- `src/Patient/Controller/PatientController.php`
- `docs/api/patient.md`**باید ایجاد شود**
--- ---
### اپیک ۷ — داشبورد هوشمند (Smart Dashboard) ### اپیک ۷ — داشبورد هوشمند (Smart Dashboard)
#### توضیح #### توضیح
هر داشبورد (ادمین، کلینیک، مطب، منشی) باید چارت‌های مناسب با فیلترهای role-based داشته باشد. داشبوردهای موجود (clinic/doctor/secretary در `docs/api/dashboard.md`) باید با چارت و فیلتر بازه زمانی تکمیل شوند.
#### نیازمندی‌های کارکردی #### نیازمندی‌های کارکردی
- **F7.1** — داشبورد ادمین:
- چارت: درآمد پلتفرم (ماهانه، هفتگی) | کد | نیازمندی |
- چارت: نوبت‌های ثبت‌شده در بازه زمانی |----|---------|
- آمار: تعداد کلینیک‌ها، دکترها، کاربران فعال | F7.1 | داشبورد ادمین: چارت درآمد + چارت نوبت‌ها + گزارش فروش پنل‌ها |
- گزارش پنل‌های فروخته‌شده | F7.2 | داشبورد کلینیک/مطب: درآمد ماه جاری + بیماران منحصربه‌فرد + موجودی پیامک |
- **F7.2** — داشبورد کلینیک/مطب: | F7.3 | فیلتر بازه زمانی: این هفته / این ماه / ۳ ماه / سفارشی |
- نوبت‌های امروز (موجود) | F7.4 | فیلتر نقش: منشی فقط نوبت‌ها را می‌بیند |
- درآمد ماه جاری
- تعداد بیماران منحصربه‌فرد #### API موجود (نیاز به endpoint جدید ندارد)
- موجودی پیامک `GET /api/v1/admin/dashboard/stats` | `/charts` | `/recent`
- **F7.3** — فیلتر بازه زمانی در همه چارت‌ها (این هفته / این ماه / ۳ ماه / سفارشی) `GET /api/v1/dashboard/clinic` | `/doctor` | `/secretary`
- **F7.4** — فیلتر بر اساس نقش (منشی فقط نوبت‌ها را می‌بیند)
#### تغییر مورد نیاز
- backend: اضافه کردن پارامترهای `from` و `to` به endpoint های charts
- frontend: `DashboardPage.tsx` — افزودن date range selector + نمودارها (Recharts/Chart.js)
--- ---
## اولویت‌بندی پیاده‌سازی ## اولویت‌بندی پیاده‌سازی
| اولویت | اپیک | دلیل | | اولویت | اپیک | وابستگی |
|--------|------|-------| |--------|------|---------|
| 1 | اپیک ۱ (Staff) | پیش‌نیاز اپیک ۴ و ۶ | | ۱ | اپیک ۱ Staff | پیش‌نیاز اپیک ۴ و ۶ |
| 2 | اپیک ۲ (Subscription) | پیش‌نیاز gate checks | | ۲ | اپیک ۲ Subscription | پیش‌نیاز gate در اپیک ۳، ۴، ۶ |
| 3 | اپیک ۳ (Secretary - تکمیل) | وابسته به پنل | | ۳ | اپیک ۳ Secretary تکمیل | وابسته به اپیک ۲ |
| 4 | اپیک ۴ (Services) | پیش‌نیاز اپیک ۶ | | ۴ | اپیک ۴ Services | وابسته به اپیک ۱ و ۲ |
| 5 | اپیک ۵ (SMS Panel) | مستقل از بقیه | | ۵ | اپیک ۵ SMS Panel | مستقل |
| 6 | اپیک ۶ (Patient Records) | بیشترین وابستگی | | ۶ | اپیک ۶ Patient Records | وابسته به اپیک ۱، ۲، ۴ |
| 7 | اپیک ۷ (Dashboard) | آخر — نیاز به داده واقعی | | ۷ | اپیک ۷ Dashboard | آخر — نیاز به داده واقعی |
--- ---
## وابستگی‌های فنی ## نقشه Entity‌های جدید
### وضعیت زیرساخت موجود و قابل استفاده ```
clinic_staff ←──────────────── service_items ←─── session_services
↑ ↑ ↑
│ service_sections patient_sessions
│ ↑ ↑
│ entity_type/id patient_records
│ ↑
subscription_plans ←── subscription_periods entity_type/id
└──────────── clinic_subscriptions
payment (موجود)
| زیرساخت | فایل | نحوه استفاده در فاز ۲ | sms_wallets sms_settings
|---------|------|----------------------| (entity_type/id)
| 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 استفاده می‌کنند ## زیرساخت موجود قابل استفاده
| زیرساخت | مسیر | استفاده در فاز ۲ |
|---------|------|-----------------|
| Payment gateway | `src/Payment/` | پرداخت subscription + شارژ SMS wallet |
| `Payment.type='subscription'` | `Payment::TYPE_SUBSCRIPTION` | پرداخت پنل اشتراکی — موجود ✅ |
| SiteConfig (key-value) | `src/Config/Entity/SiteConfig.php` | `sms_price_rials`, `trial_enabled` |
| Categories (insurance) | `bundle='insurance_type'` | انتخاب بیمه در PatientSession |
| DoctorSecretary | `src/Secretary/Entity/` | اضافه کردن gate check |
| SmsLog/SmsTemplate | `src/Sms/Entity/` | ارسال پیامک‌های خودکار |
| WalletTransaction | `src/Settlement/Entity/` | الگوی مشابه برای SmsWallet |
---
## معماری مشترک
- همه entity‌های جدید: Unix timestamp (`int`) برای `created_at`, `updated_at`
- همه controller‌های جدید از `BaseController` ارث می‌برند - همه controller‌های جدید از `BaseController` ارث می‌برند
- gate check برای features با یک helper در `BaseController` یا `SubscriptionService` پیاده‌سازی شود - پاسخ‌ها: `$this->success()` / `$this->paginated()` / `$this->error()`
- `entity_type` + `entity_id` pattern (polymorphic) برای Staff، Services، SmsSettings، Patient Records - polymorphic pattern: `entity_type: 'doctor'|'clinic'` + `entity_id`
- gate check: `SubscriptionService::hasFeature(string $feature): bool`
--- ---
## معیارهای پذیرش (Definition of Done) ## Definition of Done (هر اپیک)
برای هر اپیک: - [ ] Migration ایجاد و اجرا شده
- [ ] Entity + Migration ایجاد شده - [ ] Entity + Repository + Service نوشته شده
- [ ] Controller + Repository + Service نوشته شده - [ ] Controller با همه endpoints کامل شده
- [ ] API endpoints تست‌پذیر در Swagger (`/api/doc`) - [ ] Type check frontend بدون خطا (`yarn dev`)
- [ ] Frontend page کامل با CRUD - [ ] API قابل تست در Swagger (`/api/doc`)
- [ ] مستندات `docs/api/` به‌روزرسانی شده - [ ] فایل `docs/api/xxx.md` ایجاد یا بروزرسانی شده
- [ ] بررسی gate/permission برای plan-gated features