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:
hamed
2026-06-15 14:18:25 +03:30
parent 68dfe613a2
commit df7a784701
15 changed files with 3024 additions and 1417 deletions
+180 -145
View File
@@ -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
View File
@@ -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 داری)
```