diff --git a/docs/PRD/prd.md b/docs/PRD/prd.md index d5cd67ef..bb586a00 100644 --- a/docs/PRD/prd.md +++ b/docs/PRD/prd.md @@ -1,449 +1,520 @@ # PRD — ClinicPro Phase 2 -**تاریخ:** ۲۴ خرداد ۱۴۰۵ -**نسخه:** 1.0 +**تاریخ:** ۲۴ خرداد ۱۴۰۵ +**نسخه:** 1.2 **وضعیت:** Draft --- ## خلاصه اجرایی -این سند نیازمندی‌های فاز ۲ پلتفرم **ClinicPro** را پوشش می‌دهد. هدف: تبدیل پلتفرم از یک سیستم نوبت‌دهی ساده به یک سیستم مدیریت کلینیک کامل با پنل‌های اشتراکی، پرونده بیمار، مدیریت پرسنل و پنل پیامکی. +فاز ۲ پلتفرم **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 نیاز دارد | +| ماژول | 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** — ایجاد / ویرایش / غیرفعال‌سازی پرسنل (حذف نرم — 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: - - 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) +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 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 | +#### 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 زیر بخش «تنظیمات» +- `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) #### توضیح -سه سطح پنل وجود دارد. برای هر پنل، ادمین می‌تواند چند **دوره زمانی** با قیمت‌های مختلف تعریف کند (مثلاً ۱ ماهه، ۶ ماهه، ۱۲ ماهه). کلینیک/دکتر دوره و پنل مورد نظر را انتخاب و پرداخت می‌کند. +سه سطح پنل با قابلیت‌های متفاوت. ادمین برای هر پنل دوره‌های زمانی با قیمت مشخص تعریف می‌کند. کلینیک/دکتر دوره + پنل انتخاب کرده و پرداخت می‌کند. -#### پنل‌ها -| سطح | نام | قابلیت‌ها | -|-----|-----|----------| -| 0 | Free | نوبت‌دهی + ۱ منشی — رایگان، بدون زمان انقضا | -| 1 | Basic | ۲ منشی + پرونده بیمار + مدیریت سرویس‌ها | -| 2 | Professional | ۵ منشی + پرونده بیمار + مدیریت سرویس‌ها | +#### سطوح پنل -#### دوره‌های اشتراک (مثال) -| دوره | مدت (ماه) | نوع | قیمت | -|------|-----------|-----|------| -| تریال | 1 | trial — رایگان | ۰ ریال | -| ۱ ماهه | 1 | paid | ادمین تعیین می‌کند | -| ۳ ماهه | 3 | paid | ادمین تعیین می‌کند | -| ۶ ماهه | 6 | paid | ادمین تعیین می‌کند | -| ۱۲ ماهه | 12 | paid | ادمین تعیین می‌کند | +| Level | Name | منشی | قابلیت‌های اضافه | +|-------|------|------|----------------| +| 0 | Free | ۱ منشی | نوبت‌دهی — رایگان، بدون انقضا | +| 1 | Basic | ۲ منشی | پرونده بیمار + سرویس‌ها | +| 2 | Professional | ۵ منشی | پرونده بیمار + سرویس‌ها | -> ادمین می‌تواند دوره‌های دلخواه اضافه، ویرایش یا غیرفعال کند. +#### دوره‌های اشتراک (مثال — ادمین تنظیم می‌کند) -#### دوره تریال -- تریال فقط برای پنل **Basic** فعال است -- هر کلینیک/دکتر فقط **یک بار** می‌تواند از تریال استفاده کند (بررسی در `ClinicSubscription`) -- تریال نیازی به پرداخت ندارد — مستقیم فعال می‌شود -- ادمین می‌تواند تریال را به‌طور کلی فعال/غیرفعال کند (`trial_enabled` در SiteConfig) -- بعد از انقضای تریال، سیستم به پنل Free بازمی‌گردد (نه حذف داده) +| نوع | مدت | قیمت | +|-----|-----|------| +| تریال | ۱ ماه | ۰ ریال — فقط برای Basic، یک‌بار | +| ۱ ماهه | ۱ ماه | ادمین تعیین می‌کند | +| ۳ ماهه | ۳ ماه | ادمین تعیین می‌کند | +| ۶ ماهه | ۶ ماه | ادمین تعیین می‌کند | +| ۱۲ ماهه | ۱۲ ماه | ادمین تعیین می‌کند | + +#### قوانین تریال + +- فقط برای پنل **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: - - 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 +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 -SubscriptionPeriod: - - id, uuid - - plan_id (FK → subscription_plans) - - label: string (مثال: "۶ ماهه", "تریال ۱ ماهه") - - duration_months: int (1, 3, 6, 12, ...) - - price_rials: int (0 برای تریال) - - is_trial: bool (default: false — فقط یک دوره trial در Basic) - - active: bool - - sort_order: int (ترتیب نمایش) - - 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 -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 — برای تریال null است) - - is_trial: bool (کپی از period — برای گزارش راحت‌تر) - - starts_at: int (Unix timestamp) - - expires_at: int (Unix timestamp — null = بی‌نهایت برای Free) - - created_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 ``` -#### وابستگی به 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 — اشتراک فعال + تاریخ انقضا + `used_trial` | -| `POST` | `/api/v1/subscription/trial` | clinic/doctor — فعال‌سازی تریال Basic (رایگان، یک‌بار) | -| `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 — گزارش فروش + تعداد تریال‌های فعال‌شده | +#### 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 «تریال» اگر is_trial باشد - - جدول مقایسه پنل‌ها با انتخاب دوره (toggle) +- `SubscriptionPage.tsx`: + - بنر تریال اگر `used_trial=false` + - وضعیت پنل فعال + تاریخ انقضا (شمسی) + badge «تریال» + - جدول مقایسه پنل‌ها با انتخاب دوره - دکمه خرید → 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) — تکمیل #### توضیح -جدول `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 — موجود -// API موجود: GET /api/v1/secretaries/{doctorUuid}, POST /api/v1/secretary, PATCH, DELETE -// صفحه SecretariesPage.tsx موجود — نیاز به بررسی کامل بودن -``` +| کد | نیازمندی | +|----|---------| +| 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()` -- Frontend: ویرایشگر گرافیکی permissions (checkbox matrix در Modal) +- **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 و بالاتر. سرویس‌ها در دو سطح تعریف می‌شوند: بخش و زیر بخش. - -#### ساختار سرویس -``` -ServiceSection (بخش — مثال: تزریقات، پانسمان) - └── ServiceItem (زیر بخش — مثال: سرم) - - section_id (FK) - - staff_id (FK → clinic_staff) — پرسنل انجام‌دهنده - - price (ریال) - - active -``` +دسترسی فقط در پنل Basic+. سرویس‌ها در دو سطح: بخش (مثلاً «تزریقات») و زیربخش (مثلاً «سرم»). #### نیازمندی‌های کارکردی -- **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: - - id, uuid - - entity_type: 'doctor' | 'clinic', entity_id - - name - - active, created_at, updated_at +service_sections: + id, uuid + entity_type: 'doctor' | 'clinic' + entity_id: int + name: string + active: bool + 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 +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 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 | +#### 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: بخش‌ها و زیر بخش‌ها +- `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) موجود است. باید پنل شارژی برای هر کلینیک/دکتر اضافه شود. +زیرساخت 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): - - entity_type, entity_id - - balance_rials (موجودی) - - created_at, updated_at +sms_wallets: + id + entity_type: 'doctor' | 'clinic' + entity_id: int + balance_rials: int = 0 + 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 +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 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 | +#### 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+. پرونده بیمار به صورت خودکار پس از تأیید نوبت ایجاد می‌شود یا دستی توسط منشی/دکتر. - -#### ساختار پرونده -``` -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 -``` +دسترسی فقط در پنل Basic+. پرونده به صورت خودکار پس از تأیید نوبت ایجاد می‌شود یا دستی توسط منشی/دکتر. هر مراجعه یک سشن است که شامل ویزیت + سرویس‌ها است. #### نیازمندی‌های کارکردی -- **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 | +| کد | نیازمندی | +|----|---------| +| 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 ثبت سشن جدید +- `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) #### توضیح -هر داشبورد (ادمین، کلینیک، مطب، منشی) باید چارت‌های مناسب با فیلترهای role-based داشته باشد. +داشبوردهای موجود (clinic/doctor/secretary در `docs/api/dashboard.md`) باید با چارت و فیلتر بازه زمانی تکمیل شوند. #### نیازمندی‌های کارکردی -- **F7.1** — داشبورد ادمین: - - چارت: درآمد پلتفرم (ماهانه، هفتگی) - - چارت: نوبت‌های ثبت‌شده در بازه زمانی - - آمار: تعداد کلینیک‌ها، دکترها، کاربران فعال - - گزارش پنل‌های فروخته‌شده -- **F7.2** — داشبورد کلینیک/مطب: - - نوبت‌های امروز (موجود) - - درآمد ماه جاری - - تعداد بیماران منحصربه‌فرد - - موجودی پیامک -- **F7.3** — فیلتر بازه زمانی در همه چارت‌ها (این هفته / این ماه / ۳ ماه / سفارشی) -- **F7.4** — فیلتر بر اساس نقش (منشی فقط نوبت‌ها را می‌بیند) + +| کد | نیازمندی | +|----|---------| +| 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) --- ## اولویت‌بندی پیاده‌سازی -| اولویت | اپیک | دلیل | -|--------|------|-------| -| 1 | اپیک ۱ (Staff) | پیش‌نیاز اپیک ۴ و ۶ | -| 2 | اپیک ۲ (Subscription) | پیش‌نیاز gate checks | -| 3 | اپیک ۳ (Secretary - تکمیل) | وابسته به پنل | -| 4 | اپیک ۴ (Services) | پیش‌نیاز اپیک ۶ | -| 5 | اپیک ۵ (SMS Panel) | مستقل از بقیه | -| 6 | اپیک ۶ (Patient Records) | بیشترین وابستگی | -| 7 | اپیک ۷ (Dashboard) | آخر — نیاز به داده واقعی | +| اولویت | اپیک | وابستگی | +|--------|------|---------| +| ۱ | اپیک ۱ — 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 (موجود) -| زیرساخت | فایل | نحوه استفاده در فاز ۲ | -|---------|------|----------------------| -| 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/` | زیرساخت ارسال موجود | +sms_wallets sms_settings +(entity_type/id) +``` -### نکات معماری +--- -- همه 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` ارث می‌برند -- gate check برای features با یک helper در `BaseController` یا `SubscriptionService` پیاده‌سازی شود -- `entity_type` + `entity_id` pattern (polymorphic) برای Staff، Services، SmsSettings، Patient Records +- پاسخ‌ها: `$this->success()` / `$this->paginated()` / `$this->error()` +- polymorphic pattern: `entity_type: 'doctor'|'clinic'` + `entity_id` +- gate check: `SubscriptionService::hasFeature(string $feature): bool` --- -## معیارهای پذیرش (Definition of Done) +## Definition of Done (هر اپیک) -برای هر اپیک: -- [ ] Entity + Migration ایجاد شده -- [ ] Controller + Repository + Service نوشته شده -- [ ] API endpoints تست‌پذیر در Swagger (`/api/doc`) -- [ ] Frontend page کامل با CRUD -- [ ] مستندات `docs/api/` به‌روزرسانی شده -- [ ] بررسی gate/permission برای plan-gated features +- [ ] Migration ایجاد و اجرا شده +- [ ] Entity + Repository + Service نوشته شده +- [ ] Controller با همه endpoints کامل شده +- [ ] Type check frontend بدون خطا (`yarn dev`) +- [ ] API قابل تست در Swagger (`/api/doc`) +- [ ] فایل `docs/api/xxx.md` ایجاد یا بروزرسانی شده