# PRD — ClinicPro Phase 2 **تاریخ:** ۲۴ خرداد ۱۴۰۵ **نسخه:** 1.2 **وضعیت:** Draft --- ## خلاصه اجرایی فاز ۲ پلتفرم **ClinicPro** را از یک سیستم نوبت‌دهی ساده به یک سیستم مدیریت کلینیک کامل تبدیل می‌کند. شامل پنل‌های اشتراکی، مدیریت پرسنل، پرونده بیمار، سرویس‌های درمانی و پنل پیامکی اختصاصی برای هر کلینیک/مطب. --- ## وضعیت فعلی پروژه | ماژول | Backend | API Doc | |--------|---------|---------| | Auth (OTP + JWT + switch-context) | ✅ کامل | `docs/api/auth.md` | | Doctor profile | ✅ کامل | `docs/api/doctor.md` | | Clinic management | ✅ کامل | `docs/api/clinic.md` | | Appointment booking + settings | ✅ کامل | `docs/api/appointment.md`, `appointment-settings.md` | | Secretary (`DoctorSecretary` entity) | ✅ entity + API موجود | `docs/api/secretary.md` | | Payment gateway (Mellat, SEP) | ✅ کامل — `Payment.type='subscription'` هم موجود | `docs/api/payment.md` | | SMS log + template | ✅ زیرساخت موجود | `docs/api/sms.md` | | Wallet / Settlement | ✅ کامل | `docs/api/settlement.md` | | SiteConfig (key-value store) | ✅ موجود | `docs/api/admin.md` §Settings | | Dashboard (clinic/doctor/secretary) | ✅ کامل | `docs/api/dashboard.md` | | Rating, Blog, Representation, Location | ✅ کامل | فایل‌های مربوطه در `docs/api/` | | **Staff (پرسنل)** | ❌ entity وجود ندارد | ❌ فایل doc وجود ندارد | | **Subscription tiers** | ❌ entity وجود ندارد | ❌ فایل doc وجود ندارد | | **Clinic services** | ❌ entity وجود ندارد | ❌ فایل doc وجود ندارد | | **Patient records** | ❌ entity وجود ندارد | ❌ فایل doc وجود ندارد | | **SMS wallet/settings per clinic** | ❌ entity وجود ندارد | ❌ بخش در sms.md وجود ندارد | --- ## اپیک‌ها --- ### اپیک ۱ — پرسنل (Staff) #### توضیح مدیر کلینیک یا دکتر مستقل می‌تواند پرسنل کلینیک را ثبت کند. پرسنل حساب کاربری ندارند و نیازی به لاگین ندارند. در سرویس‌دهی، سشن‌ها و گزارشات به آن‌ها ارجاع می‌شود. #### نیازمندی‌های کارکردی | کد | نیازمندی | |----|---------| | F1.1 | ایجاد / ویرایش / غیرفعال‌سازی پرسنل — **حذف سخت ممنوع** (تاریخچه حفظ شود) | | F1.2 | فیلدها: نام و نام‌خانوادگی (اجباری)، شماره تماس، عنوان شغل، آدرس، کد ملی، وضعیت فعال | | F1.3 | پرسنل به `entity_type + entity_id` وابسته است (کلینیک یا مطب) | | F1.4 | لیست پرسنل فقط در scope کلینیک/مطب کاربر جاری نمایش داده می‌شود | | F1.5 | در ایجاد ServiceItem و SessionService از لیست پرسنل انتخاب می‌شود | #### Entity جدید ``` clinic_staff: id, uuid entity_type: 'doctor' | 'clinic' entity_id: int full_name: string (required) 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 | Method | Path | توضیح | |--------|------|-------| | `GET` | `/api/v1/staff` | لیست پرسنل کاربر جاری | | `POST` | `/api/v1/staff` | ایجاد پرسنل جدید | | `PATCH` | `/api/v1/staff/{uuid}` | ویرایش اطلاعات | | `PATCH` | `/api/v1/staff/{uuid}/toggle` | فعال/غیرفعال (soft delete) | #### Frontend - `StaffPage.tsx` — DataTable + Modal ایجاد/ویرایش - Sidebar: زیر «تنظیمات» #### فایل‌های جدید - `src/Staff/Entity/ClinicStaff.php` - `src/Staff/Controller/StaffController.php` - `src/Staff/Repository/ClinicStaffRepository.php` - `docs/api/staff.md` ← **باید ایجاد شود** --- ### اپیک ۲ — پنل اشتراکی (Subscription Tiers) #### توضیح سه سطح پنل با قابلیت‌های متفاوت. ادمین برای هر پنل دوره‌های زمانی با قیمت مشخص تعریف می‌کند. کلینیک/دکتر دوره + پنل انتخاب کرده و پرداخت می‌کند. #### سطوح پنل | Level | Name | منشی | قابلیت‌های اضافه | |-------|------|------|----------------| | 0 | Free | ۱ منشی | نوبت‌دهی — رایگان، بدون انقضا | | 1 | Basic | ۲ منشی | پرونده بیمار + سرویس‌ها | | 2 | Professional | ۵ منشی | پرونده بیمار + سرویس‌ها | #### دوره‌های اشتراک (مثال — ادمین تنظیم می‌کند) | نوع | مدت | قیمت | |-----|-----|------| | تریال | ۱ ماه | ۰ ریال — فقط برای Basic، یک‌بار | | ۱ ماهه | ۱ ماه | ادمین تعیین می‌کند | | ۳ ماهه | ۳ ماه | ادمین تعیین می‌کند | | ۶ ماهه | ۶ ماه | ادمین تعیین می‌کند | | ۱۲ ماهه | ۱۲ ماه | ادمین تعیین می‌کند | #### قوانین تریال - فقط برای پنل **Basic** - هر کلینیک/دکتر **یک‌بار** — بعد از استفاده، دکمه تریال نمایش داده نمی‌شود - بدون پرداخت — endpoint مستقل دارد - بعد از انقضا → برگشت به Free (داده‌ها حفظ می‌شوند) - ادمین می‌تواند تریال را کلاً غیرفعال کند (`trial_enabled` در SiteConfig) #### نیازمندی‌های کارکردی | کد | نیازمندی | |----|---------| | 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 جدید ``` subscription_plans: id, uuid name: 'free' | 'basic' | 'professional' level: int (0/1/2) max_secretaries: int (1/2/5) features: json { patient_records, services, sms_panel } active: bool created_at, updated_at subscription_periods: id, uuid plan_id → subscription_plans label: string ('۶ ماهه', 'تریال ۱ ماهه') duration_months: int price_rials: int ← 0 برای تریال is_trial: bool = false active: bool sort_order: int created_at, updated_at clinic_subscriptions: id, uuid entity_type: 'doctor' | 'clinic' entity_id: int plan_id → subscription_plans period_id → subscription_periods payment_id → payments ← nullable (تریال = null) is_trial: bool starts_at: int expires_at: int ← null = بی‌نهایت (Free) created_at ``` #### API | Method | Path | Permission | توضیح | |--------|------|-----------|-------| | `GET` | `/api/v1/subscription/plans` | public | لیست پنل‌ها با دوره‌ها و قیمت‌ها | | `GET` | `/api/v1/subscription/my` | clinic/doctor | اشتراک فعال + `used_trial` + تاریخ انقضا | | `POST` | `/api/v1/subscription/trial` | clinic/doctor | فعال‌سازی تریال (رایگان، یک‌بار) | | `POST` | `/api/v1/subscription-payment` | clinic/doctor | شروع پرداخت — body: `{ 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`: - بنر تریال اگر `used_trial=false` - وضعیت پنل فعال + تاریخ انقضا (شمسی) + badge «تریال» - جدول مقایسه پنل‌ها با انتخاب دوره - دکمه خرید → redirect به gateway - `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) — تکمیل #### توضیح Entity `DoctorSecretary` و API کامل موجود است (`docs/api/secretary.md`). باید: - محدودیت تعداد بر اساس پنل اعمال شود - UI مدیریت permissions تکمیل شود #### نیازمندی‌های کارکردی | کد | نیازمندی | |----|---------| | F3.1 | بررسی سقف پنل قبل از ایجاد: Free→1، Basic→2، Pro→5 | | F3.2 | UI لیست منشیان + اضافه‌کردن + ویرایش + غیرفعال‌سازی | | F3.3 | ویرایشگر permissions: checkbox matrix (موجود در `DoctorSecretary.DEFAULT_PERMISSIONS`) | | F3.4 | منشی هرگز حذف نمی‌شود — `active: false` | #### تغییرات مورد نیاز - **Backend:** `SecretaryController::create()` — بررسی سقف پنل با `SubscriptionService` - **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) #### توضیح دسترسی فقط در پنل Basic+. سرویس‌ها در دو سطح: بخش (مثلاً «تزریقات») و زیربخش (مثلاً «سرم»). #### نیازمندی‌های کارکردی | کد | نیازمندی | |----|---------| | F4.1 | CRUD بخش‌ها (ServiceSection) | | F4.2 | CRUD زیربخش‌ها (ServiceItem) با انتخاب بخش + پرسنل انجام‌دهنده | | F4.3 | قیمت هر زیربخش قابل ویرایش | | F4.4 | gate: `hasFeature('services')` — بدون Basic+ → 403 | | F4.5 | لیست ServiceItems در ثبت سشن بیمار استفاده می‌شود (وابستگی اپیک ۶) | #### Entities جدید ``` service_sections: id, uuid entity_type: 'doctor' | 'clinic' entity_id: int name: string active: bool created_at, updated_at service_items: id, uuid section_id → service_sections staff_id → clinic_staff ← nullable name: string price_rials: int active: bool created_at, updated_at ``` #### API | Method | Path | توضیح | |--------|------|-------| | `GET` | `/api/v1/service-sections` | لیست بخش‌ها | | `POST` | `/api/v1/service-section` | ایجاد بخش (Basic+) | | `PATCH` | `/api/v1/service-section/{uuid}` | ویرایش | | `DELETE` | `/api/v1/service-section/{uuid}` | حذف | | `GET` | `/api/v1/service-items/{sectionUuid}` | لیست زیربخش‌های یک بخش | | `POST` | `/api/v1/service-item` | ایجاد زیربخش (Basic+) | | `PATCH` | `/api/v1/service-item/{uuid}` | ویرایش | | `DELETE` | `/api/v1/service-item/{uuid}` | حذف | #### Frontend - `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 موجود است (`SmsLog`, `SmsTemplate`, `SmsProvider` در `src/Sms/`). باید: - کیف پول پیامک اختصاصی برای هر کلینیک/دکتر - تنظیمات ارسال پیامک (یادآوری، بعد از ویزیت) - گزارش لاگ برای کلینیک و ادمین #### نیازمندی‌های کارکردی | کد | نیازمندی | |----|---------| | F5.1 | کیف پول پیامک جدا از کیف پول مالی | | F5.2 | شارژ از طریق gateway موجود | | F5.3 | ادمین تعرفه هر پیامک را تعیین می‌کند (`sms_price_rials` در SiteConfig) | | F5.4 | کسر خودکار هزینه بعد از هر ارسال موفق | | F5.5 | تنظیمات: یادآوری نوبت (فعال/غیرفعال + چند ساعت قبل) | | F5.6 | تنظیمات: پیام بعد از ویزیت (فعال/غیرفعال + متن) | | F5.7 | گزارش لاگ پیامک‌های کلینیک/مطب | | F5.8 | گزارش ادمین: مصرف هر کلینیک + درآمد پیامکی | #### Entities جدید ``` sms_wallets: id entity_type: 'doctor' | 'clinic' entity_id: int balance_rials: int = 0 created_at, updated_at sms_settings: id entity_type: 'doctor' | 'clinic' entity_id: int reminder_enabled: bool = false reminder_hours_before: int = 2 post_visit_enabled: bool = false post_visit_text: text|null updated_at ``` #### API | Method | Path | Permission | توضیح | |--------|------|-----------|-------| | `GET` | `/api/v1/sms/wallet/balance` | 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/settings` | clinic/doctor | تنظیمات پیامک | | `PATCH` | `/api/v1/sms/settings` | clinic/doctor | ذخیره تنظیمات | | `GET` | `/api/v1/admin/sms/wallet-report` | admin | گزارش مصرف و درآمد | #### Frontend - تب «پیامک» در صفحه تنظیمات کلینیک/دکتر - نمایش موجودی + دکمه شارژ + لاگ + فرم تنظیمات #### تأثیر بر فایل‌های موجود - `docs/api/sms.md` ← **بخش جدید wallet/settings اضافه شود** --- ### اپیک ۶ — پرونده بیمار (Patient Records) #### توضیح دسترسی فقط در پنل Basic+. پرونده به صورت خودکار پس از تأیید نوبت ایجاد می‌شود یا دستی توسط منشی/دکتر. هر مراجعه یک سشن است که شامل ویزیت + سرویس‌ها است. #### نیازمندی‌های کارکردی | کد | نیازمندی | |----|---------| | F6.1 | ایجاد خودکار `PatientRecord` بعد از تأیید appointment (اگر وجود نداشت) | | F6.2 | ایجاد دستی توسط منشی یا دکتر | | F6.3 | ثبت سشن: بیمه پایه + مکمل، مبلغ ویزیت (پیش‌فرض یا دستی)، محاسبه خودکار کسورات | | F6.4 | ثبت سرویس‌های انجام‌شده در سشن با پرسنل انجام‌دهنده | | F6.5 | gate: `hasFeature('patient_records')` — بدون Basic+ → 403 | | F6.6 | تاریخچه کامل مراجعات یک بیمار | #### محاسبه مبلغ نهایی سشن ``` 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 - `PatientsPage.tsx` — لیست بیماران + جستجو - `PatientDetailPage.tsx` — پرونده + تاریخچه مراجعات - 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) #### توضیح داشبوردهای موجود (clinic/doctor/secretary در `docs/api/dashboard.md`) باید با چارت و فیلتر بازه زمانی تکمیل شوند. #### نیازمندی‌های کارکردی | کد | نیازمندی | |----|---------| | F7.1 | داشبورد ادمین: چارت درآمد + چارت نوبت‌ها + گزارش فروش پنل‌ها | | F7.2 | داشبورد کلینیک/مطب: درآمد ماه جاری + بیماران منحصربه‌فرد + موجودی پیامک | | F7.3 | فیلتر بازه زمانی: این هفته / این ماه / ۳ ماه / سفارشی | | F7.4 | فیلتر نقش: منشی فقط نوبت‌ها را می‌بیند | #### API موجود (نیاز به endpoint جدید ندارد) `GET /api/v1/admin/dashboard/stats` | `/charts` | `/recent` `GET /api/v1/dashboard/clinic` | `/doctor` | `/secretary` #### تغییر مورد نیاز - backend: اضافه کردن پارامترهای `from` و `to` به endpoint های charts - frontend: `DashboardPage.tsx` — افزودن date range selector + نمودارها (Recharts/Chart.js) --- ## اولویت‌بندی پیاده‌سازی | اولویت | اپیک | وابستگی | |--------|------|---------| | ۱ | اپیک ۱ — Staff | پیش‌نیاز اپیک ۴ و ۶ | | ۲ | اپیک ۲ — Subscription | پیش‌نیاز gate در اپیک ۳، ۴، ۶ | | ۳ | اپیک ۳ — Secretary تکمیل | وابسته به اپیک ۲ | | ۴ | اپیک ۴ — Services | وابسته به اپیک ۱ و ۲ | | ۵ | اپیک ۵ — SMS Panel | مستقل | | ۶ | اپیک ۶ — Patient Records | وابسته به اپیک ۱، ۲، ۴ | | ۷ | اپیک ۷ — 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/` | پرداخت 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` ارث می‌برند - پاسخ‌ها: `$this->success()` / `$this->paginated()` / `$this->error()` - polymorphic pattern: `entity_type: 'doctor'|'clinic'` + `entity_id` - gate check: `SubscriptionService::hasFeature(string $feature): bool` --- ## Definition of Done (هر اپیک) - [ ] Migration ایجاد و اجرا شده - [ ] Entity + Repository + Service نوشته شده - [ ] Controller با همه endpoints کامل شده - [ ] Type check frontend بدون خطا (`yarn dev`) - [ ] API قابل تست در Swagger (`/api/doc`) - [ ] فایل `docs/api/xxx.md` ایجاد یا بروزرسانی شده