feat: implement realistic data seeding for doctors, clinics, and secretaries
- Added seed_realistic_data.php to clean existing data and populate the database with realistic entries for doctors, clinics, and secretaries. - Created a structured approach to generate 100 doctors per city with diverse specialties and services. - Implemented database cleanup routines to ensure a fresh start for data seeding. - Enhanced the DoctorSecretaryRepository with improved comments for clarity.
This commit is contained in:
+180
-145
@@ -1,4 +1,5 @@
|
||||
# PRD — ClinicPro Phase 2
|
||||
|
||||
**تاریخ:** ۲۴ خرداد ۱۴۰۵
|
||||
**نسخه:** 1.2
|
||||
**وضعیت:** Draft
|
||||
@@ -13,24 +14,24 @@
|
||||
|
||||
## وضعیت فعلی پروژه
|
||||
|
||||
| ماژول | 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 وجود ندارد |
|
||||
| ماژول | 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 وجود ندارد |
|
||||
|
||||
---
|
||||
|
||||
@@ -41,19 +42,21 @@
|
||||
### اپیک ۱ — پرسنل (Staff)
|
||||
|
||||
#### توضیح
|
||||
|
||||
مدیر کلینیک یا دکتر مستقل میتواند پرسنل کلینیک را ثبت کند. پرسنل حساب کاربری ندارند و نیازی به لاگین ندارند. در سرویسدهی، سشنها و گزارشات به آنها ارجاع میشود.
|
||||
|
||||
#### نیازمندیهای کارکردی
|
||||
|
||||
| کد | نیازمندی |
|
||||
|----|---------|
|
||||
| F1.1 | ایجاد / ویرایش / غیرفعالسازی پرسنل — **حذف سخت ممنوع** (تاریخچه حفظ شود) |
|
||||
| کد | نیازمندی |
|
||||
| ---- | ------------------------------------------------------------------------------------ |
|
||||
| F1.1 | ایجاد / ویرایش / غیرفعالسازی پرسنل — **حذف سخت ممنوع** (تاریخچه حفظ شود) |
|
||||
| F1.2 | فیلدها: نام و نامخانوادگی (اجباری)، شماره تماس، عنوان شغل، آدرس، کد ملی، وضعیت فعال |
|
||||
| F1.3 | پرسنل به `entity_type + entity_id` وابسته است (کلینیک یا مطب) |
|
||||
| F1.4 | لیست پرسنل فقط در scope کلینیک/مطب کاربر جاری نمایش داده میشود |
|
||||
| F1.5 | در ایجاد ServiceItem و SessionService از لیست پرسنل انتخاب میشود |
|
||||
| F1.3 | پرسنل به `entity_type + entity_id` وابسته است (کلینیک یا مطب) |
|
||||
| F1.4 | لیست پرسنل فقط در scope کلینیک/مطب کاربر جاری نمایش داده میشود |
|
||||
| F1.5 | در ایجاد ServiceItem و SessionService از لیست پرسنل انتخاب میشود |
|
||||
|
||||
#### Entity جدید
|
||||
|
||||
```
|
||||
clinic_staff:
|
||||
id, uuid
|
||||
@@ -69,18 +72,21 @@ clinic_staff:
|
||||
```
|
||||
|
||||
#### API
|
||||
| Method | Path | توضیح |
|
||||
|--------|------|-------|
|
||||
| `GET` | `/api/v1/staff` | لیست پرسنل کاربر جاری |
|
||||
| `POST` | `/api/v1/staff` | ایجاد پرسنل جدید |
|
||||
| `PATCH` | `/api/v1/staff/{uuid}` | ویرایش اطلاعات |
|
||||
|
||||
| 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`
|
||||
@@ -91,25 +97,26 @@ clinic_staff:
|
||||
### اپیک ۲ — پنل اشتراکی (Subscription Tiers)
|
||||
|
||||
#### توضیح
|
||||
|
||||
سه سطح پنل با قابلیتهای متفاوت. ادمین برای هر پنل دورههای زمانی با قیمت مشخص تعریف میکند. کلینیک/دکتر دوره + پنل انتخاب کرده و پرداخت میکند.
|
||||
|
||||
#### سطوح پنل
|
||||
|
||||
| Level | Name | منشی | قابلیتهای اضافه |
|
||||
|-------|------|------|----------------|
|
||||
| 0 | Free | ۱ منشی | نوبتدهی — رایگان، بدون انقضا |
|
||||
| 1 | Basic | ۲ منشی | پرونده بیمار + سرویسها |
|
||||
| 2 | Professional | ۵ منشی | پرونده بیمار + سرویسها |
|
||||
| Level | Name | منشی | قابلیتهای اضافه |
|
||||
| ----- | ------------ | ------ | ----------------------------- |
|
||||
| 0 | Free | ۱ منشی | نوبتدهی — رایگان، بدون انقضا |
|
||||
| 1 | Basic | ۲ منشی | پرونده بیمار + سرویسها |
|
||||
| 2 | Professional | ۵ منشی | پرونده بیمار + سرویسها |
|
||||
|
||||
#### دورههای اشتراک (مثال — ادمین تنظیم میکند)
|
||||
|
||||
| نوع | مدت | قیمت |
|
||||
|-----|-----|------|
|
||||
| تریال | ۱ ماه | ۰ ریال — فقط برای Basic، یکبار |
|
||||
| ۱ ماهه | ۱ ماه | ادمین تعیین میکند |
|
||||
| ۳ ماهه | ۳ ماه | ادمین تعیین میکند |
|
||||
| ۶ ماهه | ۶ ماه | ادمین تعیین میکند |
|
||||
| ۱۲ ماهه | ۱۲ ماه | ادمین تعیین میکند |
|
||||
| نوع | مدت | قیمت |
|
||||
| ------- | ------ | ------------------------------- |
|
||||
| تریال | ۱ ماه | ۰ ریال — فقط برای Basic، یکبار |
|
||||
| ۱ ماهه | ۱ ماه | ادمین تعیین میکند |
|
||||
| ۳ ماهه | ۳ ماه | ادمین تعیین میکند |
|
||||
| ۶ ماهه | ۶ ماه | ادمین تعیین میکند |
|
||||
| ۱۲ ماهه | ۱۲ ماه | ادمین تعیین میکند |
|
||||
|
||||
#### قوانین تریال
|
||||
|
||||
@@ -121,18 +128,19 @@ clinic_staff:
|
||||
|
||||
#### نیازمندیهای کارکردی
|
||||
|
||||
| کد | نیازمندی |
|
||||
|----|---------|
|
||||
| F2.1 | ادمین قیمت هر ترکیب (پنل × دوره) را تعریف میکند |
|
||||
| F2.2 | ادمین میتواند دورههای جدید اضافه یا غیرفعال کند |
|
||||
| کد | نیازمندی |
|
||||
| ---- | ---------------------------------------------------------------------------- |
|
||||
| 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 | گزارش فروش برای ادمین (تعداد، درآمد، بر اساس سطح و دوره) |
|
||||
| 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
|
||||
@@ -168,31 +176,34 @@ clinic_subscriptions:
|
||||
```
|
||||
|
||||
#### 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 | گزارش فروش + تریالها |
|
||||
|
||||
| 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
|
||||
- بنر تریال اگر `used_trial=false`
|
||||
- وضعیت پنل فعال + تاریخ انقضا (شمسی) + badge «تریال»
|
||||
- جدول مقایسه پنلها با انتخاب دوره
|
||||
- دکمه خرید → redirect به gateway
|
||||
- `AdminSubscriptionPage.tsx`:
|
||||
- تب «پنلها»: ویرایش دورهها و قیمتها
|
||||
- تب «گزارش»: فروش بر اساس بازه زمانی
|
||||
- تب «پنلها»: ویرایش دورهها و قیمتها
|
||||
- تب «گزارش»: فروش بر اساس بازه زمانی
|
||||
|
||||
#### فایلهای جدید
|
||||
|
||||
- `src/Subscription/Entity/SubscriptionPlan.php`
|
||||
- `src/Subscription/Entity/SubscriptionPeriod.php`
|
||||
- `src/Subscription/Entity/ClinicSubscription.php`
|
||||
@@ -205,24 +216,28 @@ clinic_subscriptions:
|
||||
### اپیک ۳ — منشی (Secretary) — تکمیل
|
||||
|
||||
#### توضیح
|
||||
|
||||
Entity `DoctorSecretary` و API کامل موجود است (`docs/api/secretary.md`). باید:
|
||||
|
||||
- محدودیت تعداد بر اساس پنل اعمال شود
|
||||
- UI مدیریت permissions تکمیل شود
|
||||
|
||||
#### نیازمندیهای کارکردی
|
||||
|
||||
| کد | نیازمندی |
|
||||
|----|---------|
|
||||
| F3.1 | بررسی سقف پنل قبل از ایجاد: Free→1، Basic→2، Pro→5 |
|
||||
| F3.2 | UI لیست منشیان + اضافهکردن + ویرایش + غیرفعالسازی |
|
||||
| کد | نیازمندی |
|
||||
| ---- | -------------------------------------------------------------------------------------- |
|
||||
| F3.1 | بررسی سقف پنل قبل از ایجاد: Free→1، Basic→2، Pro→5 |
|
||||
| F3.2 | UI لیست منشی ها + اضافهکردن + ویرایش + غیرفعالسازی |
|
||||
| F3.3 | ویرایشگر permissions: checkbox matrix (موجود در `DoctorSecretary.DEFAULT_PERMISSIONS`) |
|
||||
| F3.4 | منشی هرگز حذف نمیشود — `active: false` |
|
||||
| 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}`
|
||||
|
||||
---
|
||||
@@ -230,19 +245,21 @@ Entity `DoctorSecretary` و API کامل موجود است (`docs/api/secretary.
|
||||
### اپیک ۴ — سرویسهای کلینیک (Clinic Services)
|
||||
|
||||
#### توضیح
|
||||
|
||||
دسترسی فقط در پنل 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 جدید
|
||||
|
||||
```
|
||||
service_sections:
|
||||
id, uuid
|
||||
@@ -263,21 +280,24 @@ service_items:
|
||||
```
|
||||
|
||||
#### 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}` | حذف |
|
||||
|
||||
| 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`
|
||||
@@ -288,25 +308,28 @@ service_items:
|
||||
### اپیک ۵ — پنل پیامکی (SMS Panel)
|
||||
|
||||
#### توضیح
|
||||
|
||||
زیرساخت SMS موجود است (`SmsLog`, `SmsTemplate`, `SmsProvider` در `src/Sms/`). باید:
|
||||
|
||||
- کیف پول پیامک اختصاصی برای هر کلینیک/دکتر
|
||||
- تنظیمات ارسال پیامک (یادآوری، بعد از ویزیت)
|
||||
- گزارش لاگ برای کلینیک و ادمین
|
||||
|
||||
#### نیازمندیهای کارکردی
|
||||
|
||||
| کد | نیازمندی |
|
||||
|----|---------|
|
||||
| F5.1 | کیف پول پیامک جدا از کیف پول مالی |
|
||||
| F5.2 | شارژ از طریق gateway موجود |
|
||||
| کد | نیازمندی |
|
||||
| ---- | ---------------------------------------------------------------------- |
|
||||
| F5.1 | کیف پول پیامک جدا از کیف پول مالی |
|
||||
| F5.2 | شارژ از طریق gateway موجود |
|
||||
| F5.3 | ادمین تعرفه هر پیامک را تعیین میکند (`sms_price_rials` در SiteConfig) |
|
||||
| F5.4 | کسر خودکار هزینه بعد از هر ارسال موفق |
|
||||
| F5.5 | تنظیمات: یادآوری نوبت (فعال/غیرفعال + چند ساعت قبل) |
|
||||
| F5.6 | تنظیمات: پیام بعد از ویزیت (فعال/غیرفعال + متن) |
|
||||
| F5.7 | گزارش لاگ پیامکهای کلینیک/مطب |
|
||||
| F5.8 | گزارش ادمین: مصرف هر کلینیک + درآمد پیامکی |
|
||||
| F5.4 | کسر خودکار هزینه بعد از هر ارسال موفق |
|
||||
| F5.5 | تنظیمات: یادآوری نوبت (فعال/غیرفعال + چند ساعت قبل) |
|
||||
| F5.6 | تنظیمات: پیام بعد از ویزیت (فعال/غیرفعال + متن) |
|
||||
| F5.7 | گزارش لاگ پیامکهای کلینیک/مطب |
|
||||
| F5.8 | گزارش ادمین: مصرف هر کلینیک + درآمد پیامکی |
|
||||
|
||||
#### Entities جدید
|
||||
|
||||
```
|
||||
sms_wallets:
|
||||
id
|
||||
@@ -327,20 +350,23 @@ sms_settings:
|
||||
```
|
||||
|
||||
#### 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 | گزارش مصرف و درآمد |
|
||||
|
||||
| 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 اضافه شود**
|
||||
|
||||
---
|
||||
@@ -348,20 +374,22 @@ sms_settings:
|
||||
### اپیک ۶ — پرونده بیمار (Patient Records)
|
||||
|
||||
#### توضیح
|
||||
|
||||
دسترسی فقط در پنل Basic+. پرونده به صورت خودکار پس از تأیید نوبت ایجاد میشود یا دستی توسط منشی/دکتر. هر مراجعه یک سشن است که شامل ویزیت + سرویسها است.
|
||||
|
||||
#### نیازمندیهای کارکردی
|
||||
|
||||
| کد | نیازمندی |
|
||||
|----|---------|
|
||||
| F6.1 | ایجاد خودکار `PatientRecord` بعد از تأیید appointment (اگر وجود نداشت) |
|
||||
| F6.2 | ایجاد دستی توسط منشی یا دکتر |
|
||||
| کد | نیازمندی |
|
||||
| ---- | ----------------------------------------------------------------------------- |
|
||||
| F6.1 | ایجاد خودکار `PatientRecord` بعد از تأیید appointment (اگر وجود نداشت) |
|
||||
| F6.2 | ایجاد دستی توسط منشی یا دکتر |
|
||||
| F6.3 | ثبت سشن: بیمه پایه + مکمل، مبلغ ویزیت (پیشفرض یا دستی)، محاسبه خودکار کسورات |
|
||||
| F6.4 | ثبت سرویسهای انجامشده در سشن با پرسنل انجامدهنده |
|
||||
| F6.5 | gate: `hasFeature('patient_records')` — بدون Basic+ → 403 |
|
||||
| F6.6 | تاریخچه کامل مراجعات یک بیمار |
|
||||
| F6.4 | ثبت سرویسهای انجامشده در سشن با پرسنل انجامدهنده |
|
||||
| F6.5 | gate: `hasFeature('patient_records')` — بدون Basic+ → 403 |
|
||||
| F6.6 | تاریخچه کامل مراجعات یک بیمار |
|
||||
|
||||
#### محاسبه مبلغ نهایی سشن
|
||||
|
||||
```
|
||||
visit_price_rials ← ادمین/دکتر تعیین میکند
|
||||
- base_insurance_discount% ← بیمه پایه
|
||||
@@ -371,6 +399,7 @@ visit_price_rials ← ادمین/دکتر تعیین میکند
|
||||
```
|
||||
|
||||
#### Entities جدید
|
||||
|
||||
```
|
||||
patient_records:
|
||||
id, uuid
|
||||
@@ -405,21 +434,24 @@ session_services:
|
||||
```
|
||||
|
||||
#### 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 | ویرایش مراجعه |
|
||||
|
||||
| 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`
|
||||
@@ -431,22 +463,25 @@ session_services:
|
||||
### اپیک ۷ — داشبورد هوشمند (Smart Dashboard)
|
||||
|
||||
#### توضیح
|
||||
|
||||
داشبوردهای موجود (clinic/doctor/secretary در `docs/api/dashboard.md`) باید با چارت و فیلتر بازه زمانی تکمیل شوند.
|
||||
|
||||
#### نیازمندیهای کارکردی
|
||||
|
||||
| کد | نیازمندی |
|
||||
|----|---------|
|
||||
| F7.1 | داشبورد ادمین: چارت درآمد + چارت نوبتها + گزارش فروش پنلها |
|
||||
| کد | نیازمندی |
|
||||
| ---- | ----------------------------------------------------------------------- |
|
||||
| F7.1 | داشبورد ادمین: چارت درآمد + چارت نوبتها + گزارش فروش پنلها |
|
||||
| F7.2 | داشبورد کلینیک/مطب: درآمد ماه جاری + بیماران منحصربهفرد + موجودی پیامک |
|
||||
| F7.3 | فیلتر بازه زمانی: این هفته / این ماه / ۳ ماه / سفارشی |
|
||||
| F7.4 | فیلتر نقش: منشی فقط نوبتها را میبیند |
|
||||
| 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)
|
||||
|
||||
@@ -454,15 +489,15 @@ session_services:
|
||||
|
||||
## اولویتبندی پیادهسازی
|
||||
|
||||
| اولویت | اپیک | وابستگی |
|
||||
|--------|------|---------|
|
||||
| ۱ | اپیک ۱ — Staff | پیشنیاز اپیک ۴ و ۶ |
|
||||
| ۲ | اپیک ۲ — Subscription | پیشنیاز gate در اپیک ۳، ۴، ۶ |
|
||||
| ۳ | اپیک ۳ — Secretary تکمیل | وابسته به اپیک ۲ |
|
||||
| ۴ | اپیک ۴ — Services | وابسته به اپیک ۱ و ۲ |
|
||||
| ۵ | اپیک ۵ — SMS Panel | مستقل |
|
||||
| ۶ | اپیک ۶ — Patient Records | وابسته به اپیک ۱، ۲، ۴ |
|
||||
| ۷ | اپیک ۷ — Dashboard | آخر — نیاز به داده واقعی |
|
||||
| اولویت | اپیک | وابستگی |
|
||||
| ------ | ------------------------ | ----------------------------- |
|
||||
| ۱ | اپیک ۱ — Staff | پیشنیاز اپیک ۴ و ۶ |
|
||||
| ۲ | اپیک ۲ — Subscription | پیشنیاز gate در اپیک ۳، ۴، ۶ |
|
||||
| ۳ | اپیک ۳ — Secretary تکمیل | وابسته به اپیک ۲ |
|
||||
| ۴ | اپیک ۴ — Services | وابسته به اپیک ۱ و ۲ |
|
||||
| ۵ | اپیک ۵ — SMS Panel | مستقل |
|
||||
| ۶ | اپیک ۶ — Patient Records | وابسته به اپیک ۱، ۲، ۴ |
|
||||
| ۷ | اپیک ۷ — Dashboard | آخر — نیاز به داده واقعی |
|
||||
|
||||
---
|
||||
|
||||
@@ -488,15 +523,15 @@ sms_wallets sms_settings
|
||||
|
||||
## زیرساخت موجود قابل استفاده
|
||||
|
||||
| زیرساخت | مسیر | استفاده در فاز ۲ |
|
||||
|---------|------|-----------------|
|
||||
| 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 |
|
||||
| زیرساخت | مسیر | استفاده در فاز ۲ |
|
||||
| ----------------------------- | ---------------------------------- | ------------------------------------- |
|
||||
| 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 |
|
||||
|
||||
---
|
||||
|
||||
|
||||
+146
-107
@@ -6,10 +6,10 @@
|
||||
|
||||
هر رابطه منشی-پزشک دارای یک **scope** است که از تداخل بین محیطهای مختلف جلوگیری میکند:
|
||||
|
||||
| Scope | `owner_type` | تعریفکننده | دسترسی |
|
||||
|-------|-------------|-------------|---------|
|
||||
| مطب شخصی | `doctor` | خود پزشک | فقط نوبتها و دادههای مطب شخصی |
|
||||
| کلینیک | `clinic` | مدیر کلینیک | فقط نوبتها و دادههای کلینیک |
|
||||
| Scope | `owner_type` | تعریفکننده | دسترسی |
|
||||
| -------- | ------------ | ----------- | ------------------------------- |
|
||||
| مطب شخصی | `doctor` | خود پزشک | فقط نوبتها و دادههای مطب شخصی |
|
||||
| کلینیک | `clinic` | مدیر کلینیک | فقط نوبتها و دادههای کلینیک |
|
||||
|
||||
- یک منشی میتواند هم در مطب شخصی یک دکتر و هم در کلینیک همان دکتر فعال باشد (دو ردیف مجزا)
|
||||
- منشی کلینیک میتواند به چند دکتر در همان کلینیک متصل باشد
|
||||
@@ -26,62 +26,80 @@ Create a secretary for a doctor.
|
||||
**Permission:** `ROLE_DOCTOR` (must own the doctor — creates `owner_type='doctor'`) | `ROLE_CLINIC` (must have the doctor in its clinic — creates `owner_type='clinic'`) | `ROLE_ADMIN`
|
||||
|
||||
### Request Body (`application/json`)
|
||||
|
||||
```json
|
||||
{
|
||||
"doctor_uuid": "550e8400-...",
|
||||
"mobile_number": "09123456789",
|
||||
"password": "secretaryPass123",
|
||||
"permissions": {
|
||||
"version": 1,
|
||||
"resources": {
|
||||
"appointments": { "view": true, "create": true, "cancel": false, "update_status": true },
|
||||
"addresses": { "view": true, "create": false, "update": false, "delete": false },
|
||||
"clinic_info": { "view": true, "update": false },
|
||||
"insurances": { "view": true, "create": false, "update": false, "delete": false }
|
||||
"doctor_uuid": "550e8400-...",
|
||||
"mobile_number": "09123456789",
|
||||
"password": "secretaryPass123",
|
||||
"permissions": {
|
||||
"version": 1,
|
||||
"resources": {
|
||||
"appointments": {
|
||||
"view": true,
|
||||
"create": true,
|
||||
"cancel": false,
|
||||
"update_status": true
|
||||
},
|
||||
"addresses": {
|
||||
"view": true,
|
||||
"create": false,
|
||||
"update": false,
|
||||
"delete": false
|
||||
},
|
||||
"clinic_info": { "view": true, "update": false },
|
||||
"insurances": {
|
||||
"view": true,
|
||||
"create": false,
|
||||
"update": false,
|
||||
"delete": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| `doctor_uuid` | string (UUID) | ✅ | Doctor to assign secretary to |
|
||||
| `mobile_number` | string | ✅ | Secretary's login mobile |
|
||||
| `password` | string | ❌ | Initial password (auto-generated if omitted) |
|
||||
| `permissions` | object | ❌ | Permission set (see structure below) |
|
||||
| Field | Type | Required | Description |
|
||||
| --------------- | ------------- | -------- | -------------------------------------------- |
|
||||
| `doctor_uuid` | string (UUID) | ✅ | Doctor to assign secretary to |
|
||||
| `mobile_number` | string | ✅ | Secretary's login mobile |
|
||||
| `password` | string | ❌ | Initial password (auto-generated if omitted) |
|
||||
| `permissions` | object | ❌ | Permission set (see structure below) |
|
||||
|
||||
**Permissions Structure:**
|
||||
|
||||
```json
|
||||
{
|
||||
"version": 1,
|
||||
"resources": {
|
||||
"appointments": {
|
||||
"view": true, // Can view appointments list
|
||||
"create": true, // Can book appointments
|
||||
"cancel": false, // Can cancel appointments
|
||||
"update_status": true // Can mark as completed/no_show
|
||||
},
|
||||
"addresses": {
|
||||
"view": true,
|
||||
"create": false,
|
||||
"update": false,
|
||||
"delete": false
|
||||
},
|
||||
"clinic_info": {
|
||||
"view": true,
|
||||
"update": false
|
||||
},
|
||||
"insurances": {
|
||||
"view": true,
|
||||
"create": false,
|
||||
"update": false,
|
||||
"delete": false
|
||||
"version": 1,
|
||||
"resources": {
|
||||
"appointments": {
|
||||
"view": true, // Can view appointments list
|
||||
"create": true, // Can book appointments
|
||||
"cancel": false, // Can cancel appointments
|
||||
"update_status": true // Can mark as completed/no_show
|
||||
},
|
||||
"addresses": {
|
||||
"view": true,
|
||||
"create": false,
|
||||
"update": false,
|
||||
"delete": false
|
||||
},
|
||||
"clinic_info": {
|
||||
"view": true,
|
||||
"update": false
|
||||
},
|
||||
"insurances": {
|
||||
"view": true,
|
||||
"create": false,
|
||||
"update": false,
|
||||
"delete": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Response `201`
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
@@ -107,13 +125,14 @@ Create a secretary for a doctor.
|
||||
| `clinic` | منشی توسط مدیر کلینیک تعریف شده — فقط کلینیک |
|
||||
|
||||
### Errors
|
||||
| Code | HTTP | Description |
|
||||
|------|------|-------------|
|
||||
| `ERR_AUTH_001` | 401 | Missing token |
|
||||
| `ERR_AUTH_006` | 403 | Not the doctor owner / clinic owner / admin |
|
||||
| `ERR_NOT_FOUND_001` | 404 | Doctor not found |
|
||||
| `ERR_CONFLICT_001` | 409 | Secretary already added for this doctor |
|
||||
| `ERR_SECRETARY_001` | 422 | Plan limit for secretaries reached |
|
||||
|
||||
| Code | HTTP | Description |
|
||||
| ------------------- | ---- | ------------------------------------------- |
|
||||
| `ERR_AUTH_001` | 401 | Missing token |
|
||||
| `ERR_AUTH_006` | 403 | Not the doctor owner / clinic owner / admin |
|
||||
| `ERR_NOT_FOUND_001` | 404 | Doctor not found |
|
||||
| `ERR_CONFLICT_001` | 409 | Secretary already added for this doctor |
|
||||
| `ERR_SECRETARY_001` | 422 | Plan limit for secretaries reached |
|
||||
|
||||
---
|
||||
|
||||
@@ -124,11 +143,13 @@ Get secretary detail.
|
||||
**Permission:** `AUTH` — must be the linked doctor or `ROLE_ADMIN`
|
||||
|
||||
### Path Parameters
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
|
||||
| Param | Type | Description |
|
||||
| ------ | ------------- | -------------- |
|
||||
| `uuid` | string (UUID) | Secretary UUID |
|
||||
|
||||
### Response `200`
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
@@ -144,11 +165,12 @@ Get secretary detail.
|
||||
```
|
||||
|
||||
### Errors
|
||||
| Code | HTTP | Description |
|
||||
|------|------|-------------|
|
||||
| `ERR_AUTH_001` | 401 | Missing token |
|
||||
| `ERR_FORBIDDEN_001` | 403 | Not authorized |
|
||||
| `ERR_NOT_FOUND_001` | 404 | Secretary not found |
|
||||
|
||||
| Code | HTTP | Description |
|
||||
| ------------------- | ---- | ------------------- |
|
||||
| `ERR_AUTH_001` | 401 | Missing token |
|
||||
| `ERR_FORBIDDEN_001` | 403 | Not authorized |
|
||||
| `ERR_NOT_FOUND_001` | 404 | Secretary not found |
|
||||
|
||||
---
|
||||
|
||||
@@ -159,32 +181,40 @@ Update secretary active status or permissions.
|
||||
**Permission:** `ROLE_DOCTOR` — must be the linked doctor
|
||||
|
||||
### Request Body (`application/json`)
|
||||
|
||||
```json
|
||||
{
|
||||
"active": false,
|
||||
"permissions": {
|
||||
"version": 1,
|
||||
"resources": {
|
||||
"appointments": { "view": true, "create": false, "cancel": false, "update_status": false }
|
||||
"active": false,
|
||||
"permissions": {
|
||||
"version": 1,
|
||||
"resources": {
|
||||
"appointments": {
|
||||
"view": true,
|
||||
"create": false,
|
||||
"cancel": false,
|
||||
"update_status": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| `active` | boolean | ❌ | Enable/disable secretary |
|
||||
| `permissions` | object | ❌ | New permissions object |
|
||||
| Field | Type | Required | Description |
|
||||
| ------------- | ------- | -------- | ------------------------ |
|
||||
| `active` | boolean | ❌ | Enable/disable secretary |
|
||||
| `permissions` | object | ❌ | New permissions object |
|
||||
|
||||
### Response `200`
|
||||
|
||||
Updated secretary object.
|
||||
|
||||
### Errors
|
||||
| Code | HTTP | Description |
|
||||
|------|------|-------------|
|
||||
| `ERR_AUTH_001` | 401 | Missing token |
|
||||
| `ERR_FORBIDDEN_001` | 403 | Not the linked doctor |
|
||||
| `ERR_NOT_FOUND_001` | 404 | Secretary not found |
|
||||
|
||||
| Code | HTTP | Description |
|
||||
| ------------------- | ---- | --------------------- |
|
||||
| `ERR_AUTH_001` | 401 | Missing token |
|
||||
| `ERR_FORBIDDEN_001` | 403 | Not the linked doctor |
|
||||
| `ERR_NOT_FOUND_001` | 404 | Secretary not found |
|
||||
|
||||
---
|
||||
|
||||
@@ -195,16 +225,18 @@ Delete a secretary.
|
||||
**Permission:** `ROLE_DOCTOR` — must be the linked doctor
|
||||
|
||||
### Response `200`
|
||||
|
||||
```json
|
||||
{ "success": true, "data": { "message": "منشی حذف شد" } }
|
||||
```
|
||||
|
||||
### Errors
|
||||
| Code | HTTP | Description |
|
||||
|------|------|-------------|
|
||||
| `ERR_AUTH_001` | 401 | Missing token |
|
||||
| `ERR_FORBIDDEN_001` | 403 | Not the linked doctor |
|
||||
| `ERR_NOT_FOUND_001` | 404 | Secretary not found |
|
||||
|
||||
| Code | HTTP | Description |
|
||||
| ------------------- | ---- | --------------------- |
|
||||
| `ERR_AUTH_001` | 401 | Missing token |
|
||||
| `ERR_FORBIDDEN_001` | 403 | Not the linked doctor |
|
||||
| `ERR_NOT_FOUND_001` | 404 | Secretary not found |
|
||||
|
||||
---
|
||||
|
||||
@@ -215,11 +247,13 @@ Get all secretaries for a specific doctor.
|
||||
**Permission:** `ROLE_DOCTOR` (must own doctor) | `ROLE_CLINIC` (must have doctor in clinic) | `ROLE_ADMIN`
|
||||
|
||||
### Path Parameters
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
|
||||
| Param | Type | Description |
|
||||
| ------------ | ------------- | ----------- |
|
||||
| `doctorUuid` | string (UUID) | Doctor UUID |
|
||||
|
||||
### Response `200`
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
@@ -239,11 +273,12 @@ Get all secretaries for a specific doctor.
|
||||
```
|
||||
|
||||
### Errors
|
||||
| Code | HTTP | Description |
|
||||
|------|------|-------------|
|
||||
| `ERR_AUTH_001` | 401 | Missing token |
|
||||
| `ERR_FORBIDDEN_001` | 403 | Not authorized |
|
||||
| `ERR_NOT_FOUND_001` | 404 | Doctor not found |
|
||||
|
||||
| Code | HTTP | Description |
|
||||
| ------------------- | ---- | ---------------- |
|
||||
| `ERR_AUTH_001` | 401 | Missing token |
|
||||
| `ERR_FORBIDDEN_001` | 403 | Not authorized |
|
||||
| `ERR_NOT_FOUND_001` | 404 | Doctor not found |
|
||||
|
||||
---
|
||||
|
||||
@@ -254,11 +289,13 @@ Get all secretaries across **all doctors** of a clinic.
|
||||
**Permission:** `ROLE_CLINIC` (must own clinic) | `ROLE_ADMIN`
|
||||
|
||||
### Path Parameters
|
||||
| Param | Type | Description |
|
||||
|-------|------|-------------|
|
||||
|
||||
| Param | Type | Description |
|
||||
| ------------ | ------------- | ----------- |
|
||||
| `clinicUuid` | string (UUID) | Clinic UUID |
|
||||
|
||||
### Response `200`
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
@@ -278,16 +315,18 @@ Get all secretaries across **all doctors** of a clinic.
|
||||
```
|
||||
|
||||
### Notes
|
||||
- این endpoint فقط منشیانی را برمیگرداند که با `owner_type='clinic'` تعریف شدهاند
|
||||
- منشیانی که خود دکتر (با `owner_type='doctor'`) تعریف کرده از این لیست مخفی هستند
|
||||
|
||||
- این endpoint فقط منشی های را برمیگرداند که با `owner_type='clinic'` تعریف شدهاند
|
||||
- منشی های که خود دکتر (با `owner_type='doctor'`) تعریف کرده از این لیست مخفی هستند
|
||||
- یک منشی میتواند به چند دکتر در همان کلینیک متصل باشد — در لیست چندبار ظاهر میشود (یک ردیف به ازای هر دکتر)
|
||||
|
||||
### Errors
|
||||
| Code | HTTP | Description |
|
||||
|------|------|-------------|
|
||||
| `ERR_AUTH_001` | 401 | Missing token |
|
||||
| `ERR_FORBIDDEN_001` | 403 | Not clinic owner |
|
||||
| `ERR_NOT_FOUND_001` | 404 | Clinic not found |
|
||||
|
||||
| Code | HTTP | Description |
|
||||
| ------------------- | ---- | ---------------- |
|
||||
| `ERR_AUTH_001` | 401 | Missing token |
|
||||
| `ERR_FORBIDDEN_001` | 403 | Not clinic owner |
|
||||
| `ERR_NOT_FOUND_001` | 404 | Clinic not found |
|
||||
|
||||
---
|
||||
|
||||
@@ -295,23 +334,23 @@ Get all secretaries across **all doctors** of a clinic.
|
||||
|
||||
تعداد منشیهای مجاز بر اساس پنل فعال doctor تعیین میشود:
|
||||
|
||||
| پنل | حداکثر منشی |
|
||||
|-----|-------------|
|
||||
| Free (بدون اشتراک) | ۱ |
|
||||
| Basic | ۳ |
|
||||
| Professional | ۱۰ |
|
||||
| پنل | حداکثر منشی |
|
||||
| ------------------ | ----------- |
|
||||
| Free (بدون اشتراک) | ۱ |
|
||||
| Basic | ۳ |
|
||||
| Professional | ۱۰ |
|
||||
|
||||
اگر تعداد منشیهای فعال به حد مجاز رسیده باشد، ایجاد منشی جدید خطای زیر را برمیگرداند:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"errors": [
|
||||
{
|
||||
"code": "ERR_SECRETARY_001",
|
||||
"message": "پلن فعلی اجازه منشی بیشتر را نمیدهد"
|
||||
}
|
||||
]
|
||||
"success": false,
|
||||
"errors": [
|
||||
{
|
||||
"code": "ERR_SECRETARY_001",
|
||||
"message": "پلن فعلی اجازه منشی بیشتر را نمیدهد"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
# تسک ۱۲: تکمیل منشی — محدودیت پنل + UI Permissions
|
||||
|
||||
## توضیح
|
||||
|
||||
Entity `DoctorSecretary` و API کامل موجود است (`src/Secretary/`). این تسک فقط دو چیز اضافه میکند:
|
||||
|
||||
1. Backend: بررسی سقف تعداد منشی بر اساس پنل اشتراکی
|
||||
2. Frontend: Modal با checkbox matrix برای ویرایش permissions
|
||||
|
||||
@@ -9,30 +11,33 @@ Entity `DoctorSecretary` و API کامل موجود است (`src/Secretary/`).
|
||||
|
||||
## فایلهای موجود که تغییر میکنند
|
||||
|
||||
| فایل | تغییر |
|
||||
|------|-------|
|
||||
| فایل | تغییر |
|
||||
| -------------------------------------------------- | ------------------------------------------------------------------ |
|
||||
| `src/Secretary/Controller/SecretaryController.php` | inject `SubscriptionService`، اضافه کردن limit check در `create()` |
|
||||
| `assets/admin/pages/SecretariesPage.tsx` | Modal ویرایش با checkbox matrix برای permissions |
|
||||
| `assets/admin/pages/SecretariesPage.tsx` | Modal ویرایش با checkbox matrix برای permissions |
|
||||
|
||||
## API موجود (بدون تغییر)
|
||||
|
||||
| متد | مسیر | توضیح |
|
||||
|-----|------|-------|
|
||||
| GET | `/api/v1/secretaries/{doctorUuid}` | لیست منشیان |
|
||||
| POST | `/api/v1/secretary` | ایجاد منشی ← **اینجا limit check اضافه میشود** |
|
||||
| PATCH | `/api/v1/secretary/{uuid}` | ویرایش ← **permissions هم قابل ویرایش میشود** |
|
||||
| DELETE | `/api/v1/secretary/{uuid}` | غیرفعالسازی (soft delete) |
|
||||
| متد | مسیر | توضیح |
|
||||
| ------ | ---------------------------------- | ----------------------------------------------- |
|
||||
| GET | `/api/v1/secretaries/{doctorUuid}` | لیست منشی ها |
|
||||
| POST | `/api/v1/secretary` | ایجاد منشی ← **اینجا limit check اضافه میشود** |
|
||||
| PATCH | `/api/v1/secretary/{uuid}` | ویرایش ← **permissions هم قابل ویرایش میشود** |
|
||||
| DELETE | `/api/v1/secretary/{uuid}` | غیرفعالسازی (soft delete) |
|
||||
|
||||
## پیشنیازها
|
||||
|
||||
- تسک ۱۴ (Secretary — موجود در کد)
|
||||
- **تسک ۱۱** (Subscription — `SubscriptionService` باید موجود باشد)
|
||||
|
||||
## زمان تخمینی
|
||||
|
||||
۴ تا ۵ ساعت
|
||||
|
||||
## تغییر Backend — SecretaryController::create()
|
||||
|
||||
### وضعیت فعلی (قبل):
|
||||
|
||||
```php
|
||||
public function create(Request $request): JsonResponse
|
||||
{
|
||||
@@ -44,6 +49,7 @@ public function create(Request $request): JsonResponse
|
||||
```
|
||||
|
||||
### وضعیت جدید (بعد):
|
||||
|
||||
```php
|
||||
public function create(Request $request): JsonResponse
|
||||
{
|
||||
@@ -68,6 +74,7 @@ public function create(Request $request): JsonResponse
|
||||
```
|
||||
|
||||
### خطای جدید که باید در ErrorCodes.php اضافه شود:
|
||||
|
||||
```php
|
||||
// src/Shared/Constant/ErrorCodes.php
|
||||
const ERR_SECRETARY_LIMIT_REACHED = 'ERR_SECRETARY_LIMIT_REACHED';
|
||||
@@ -77,6 +84,7 @@ const ERR_SECRETARY_LIMIT_REACHED = 'ERR_SECRETARY_LIMIT_REACHED';
|
||||
## تغییر Frontend — SecretariesPage.tsx
|
||||
|
||||
### ساختار DEFAULT_PERMISSIONS (از DoctorSecretary::DEFAULT_PERMISSIONS):
|
||||
|
||||
```php
|
||||
[
|
||||
'version' => 1,
|
||||
@@ -90,6 +98,7 @@ const ERR_SECRETARY_LIMIT_REACHED = 'ERR_SECRETARY_LIMIT_REACHED';
|
||||
```
|
||||
|
||||
### نمونه UI Permissions checkbox matrix در Modal:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ دسترسیهای منشی │
|
||||
@@ -104,31 +113,45 @@ const ERR_SECRETARY_LIMIT_REACHED = 'ERR_SECRETARY_LIMIT_REACHED';
|
||||
```
|
||||
|
||||
### PATCH /api/v1/secretary/{uuid} با permissions:
|
||||
|
||||
```json
|
||||
{
|
||||
"permissions": {
|
||||
"version": 1,
|
||||
"resources": {
|
||||
"appointments": { "view": true, "create": true, "cancel": true, "update_status": true },
|
||||
"addresses": { "view": true, "create": false, "update": false, "delete": false }
|
||||
"permissions": {
|
||||
"version": 1,
|
||||
"resources": {
|
||||
"appointments": {
|
||||
"view": true,
|
||||
"create": true,
|
||||
"cancel": true,
|
||||
"update_status": true
|
||||
},
|
||||
"addresses": {
|
||||
"view": true,
|
||||
"create": false,
|
||||
"update": false,
|
||||
"delete": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## کدهای خطای جدید
|
||||
|
||||
| کد | پیام فارسی |
|
||||
|----|-----------|
|
||||
| کد | پیام فارسی |
|
||||
| ----------------------------- | ---------------------------------------- |
|
||||
| `ERR_SECRETARY_LIMIT_REACHED` | سقف تعداد منشی پنل اشتراکی شما رسیده است |
|
||||
|
||||
## Response خطا (403)
|
||||
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"errors": [{
|
||||
"code": "ERR_SECRETARY_LIMIT_REACHED",
|
||||
"message": "سقف تعداد منشی پنل اشتراکی شما رسیده است. برای افزودن منشی بیشتر پنل را ارتقاء دهید."
|
||||
}]
|
||||
"success": false,
|
||||
"errors": [
|
||||
{
|
||||
"code": "ERR_SECRETARY_LIMIT_REACHED",
|
||||
"message": "سقف تعداد منشی پنل اشتراکی شما رسیده است. برای افزودن منشی بیشتر پنل را ارتقاء دهید."
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
@@ -64,7 +64,7 @@ Professional → 5 → 403
|
||||
## نکته: منشی غیرفعال از سقف کم نمیشود
|
||||
|
||||
```
|
||||
countActive() فقط منشیان active=true را میشمارد
|
||||
countActive() فقط منشی ها active=true را میشمارد
|
||||
یعنی: اگر 2 منشی داری و یکی را غیرفعال کنی → میتوانی دوباره 1 منشی جدید اضافه کنی
|
||||
(مادامی که در پنل Basic هستی و سقف 2 داری)
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user