- Redesign MySecretariesPage pixel-perfect to clinic-pro-tauri (active/previous tabs, desktop table, mobile cards, add/edit/view modal with permission accordions, deactivate confirm) - Permission sections based on existing admin pages (appointments, patients, payments, insurances, addresses, clinic_info) - Extend DoctorSecretary with national_code + address columns (+migration); wire create/update in SecretaryController; add patients/payments to DEFAULT_PERMISSIONS - Extend Secretary/SecretaryPermissions types; update admin SecretariesPage - Backend + frontend tests; update docs/api/secretary.md Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
13 KiB
Secretary API
Prefix:
/api/v1/secretary,/api/v1/secretaries
مدل Scope
هر رابطه منشی-پزشک دارای یک scope است که از تداخل بین محیطهای مختلف جلوگیری میکند:
| Scope | owner_type |
تعریفکننده | دسترسی |
|---|---|---|---|
| مطب شخصی | doctor |
خود پزشک | فقط نوبتها و دادههای مطب شخصی |
| کلینیک | clinic |
مدیر کلینیک | فقط نوبتها و دادههای کلینیک |
- یک منشی میتواند هم در مطب شخصی یک دکتر و هم در کلینیک همان دکتر فعال باشد (دو ردیف مجزا)
- منشی کلینیک میتواند به چند دکتر در همان کلینیک متصل باشد
- scope فعال در runtime از جدول
user_active_context(db_uuid) خوانده میشود
Secretaries are linked to a doctor and have granular permissions controlling what they can do on behalf of the doctor.
POST /api/v1/secretary
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)
{
"doctor_uuid": "550e8400-...",
"mobile_number": "09123456789",
"name": "سارا احمدی",
"national_code": "1234567890",
"address": "یزد، خیابان تست",
"password": "secretaryPass123",
"permissions": {
"version": 1,
"resources": {
"appointments": {
"view": true,
"create": true,
"cancel": false,
"update_status": true
},
"patients": {
"view": true,
"create": false,
"update": false,
"delete": false
},
"payments": {
"view": true,
"create": false,
"update": false,
"delete": false
},
"insurances": {
"view": true,
"create": false,
"update": false,
"delete": false
},
"addresses": {
"view": true,
"create": false,
"update": false,
"delete": false
},
"clinic_info": { "view": true, "update": false }
}
}
}
| Field | Type | Required | Description |
|---|---|---|---|
doctor_uuid |
string (UUID) | ✅ | Doctor to assign secretary to |
mobile_number |
string | ✅ | Secretary's login mobile |
name |
string | ❌ | Full name (نام + نام خانوادگی) → user_name |
national_code |
string | ❌ | کد ملی منشی (nullable) |
address |
string | ❌ | آدرس منشی (nullable) |
password |
string | ❌ | Initial password (auto-generated if omitted) |
permissions |
object | ❌ | Permission set (see structure below) |
Permissions Structure:
مجموعهٔ منابع (resources) بر اساس صفحات موجود پنل ادمین است. mergePermissions هر منبع/اکشن ارسالشده را deep-merge میکند؛ فقط appointments در بکاند enforce میشود (MyAppointmentsController, DashboardController)، بقیه UI/ذخیرهای هستند.
{
"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
},
"patients": {
"view": true,
"create": false,
"update": false,
"delete": false
},
"payments": {
"view": true,
"create": false,
"update": false,
"delete": false
},
"insurances": {
"view": true,
"create": false,
"update": false,
"delete": false
},
"addresses": {
"view": true,
"create": false,
"update": false,
"delete": false
},
"clinic_info": {
"view": true,
"update": false
}
}
}
Response 201
{
"success": true,
"data": {
"uuid": "sec-uuid-...",
"user_name": "علی محمدی",
"mobile_number": "09123456789",
"doctor_name": "دکتر احمد رضایی",
"doctor_uuid": "...",
"owner_type": "doctor",
"clinic_uuid": null,
"is_active": true,
"national_code": "1234567890",
"address": "یزد، خیابان تست",
"permissions": { ... },
"created_at": 1717000000
}
}
مقادیر owner_type:
| مقدار | معنی |
|---|---|
doctor |
منشی توسط خود دکتر تعریف شده — فقط مطب شخصی |
clinic |
منشی توسط مدیر کلینیک تعریف شده — فقط کلینیک |
پیامک خوشآمد: پس از ساخت موفق منشی، یک پیامک بهصورت async به mobile_number منشی ارسال میشود (تگ secretary). متن از قالب ویرایشپذیر DB میآید (fallback به پیشفرض) و placeholderهای {owner} (نام دکتر یا کلینیک بسته به owner_type)، {username} (موبایل منشی) و {link} (لینک ورود) را جایگزین میکند. ویرایش متن از PATCH /api/v1/admin/sms/messages/secretary — رجوع به sms.md.
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 |
GET /api/v1/secretary/{uuid}
Get secretary detail.
Permission: AUTH — must be the linked doctor or ROLE_ADMIN
Path Parameters
| Param | Type | Description |
|---|---|---|
uuid |
string (UUID) | Secretary UUID |
Response 200
{
"success": true,
"data": {
"uuid": "...",
"mobile_number": "09123456789",
"active": true,
"permissions": { ... },
"doctor": { "uuid": "...", "title": "دکتر علی احمدی" },
"created_at": 1717000000
}
}
Errors
| Code | HTTP | Description |
|---|---|---|
ERR_AUTH_001 |
401 | Missing token |
ERR_FORBIDDEN_001 |
403 | Not authorized |
ERR_NOT_FOUND_001 |
404 | Secretary not found |
PATCH /api/v1/secretary/{uuid}
Update secretary active status, profile fields (name/national_code/address), or permissions. تمام فیلدها اختیاریاند و فقط موارد ارسالشده اعمال میشوند.
Permission: ROLE_DOCTOR — must be the linked doctor
Request Body (application/json)
{
"active": false,
"name": "نام جدید",
"national_code": "9999999999",
"address": "آدرس جدید",
"permissions": {
"version": 1,
"resources": {
"appointments": {
"view": true,
"create": false,
"cancel": false,
"update_status": false
}
}
}
}
| Field | Type | Required | Description |
|---|---|---|---|
active |
boolean | ❌ | Enable/disable secretary |
name |
string | ❌ | بهروزرسانی نام کامل منشی (user_name) |
national_code |
string | ❌ | بهروزرسانی کد ملی (nullable) |
address |
string | ❌ | بهروزرسانی آدرس (nullable) |
permissions |
object | ❌ | New permissions object (deep-merged) |
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 |
DELETE /api/v1/secretary/{uuid}
Delete a secretary.
Permission: ROLE_DOCTOR — must be the linked doctor
Response 200
{ "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 |
GET /api/v1/secretaries/{doctorUuid}
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 |
|---|---|---|
doctorUuid |
string (UUID) | Doctor UUID |
Response 200
{
"success": true,
"data": [
{
"uuid": "...",
"user_name": "علی محمدی",
"mobile_number": "09...",
"doctor_name": "دکتر احمد رضایی",
"doctor_uuid": "...",
"is_active": true,
"permissions": { ... },
"created_at": 1717000000
}
]
}
Errors
| Code | HTTP | Description |
|---|---|---|
ERR_AUTH_001 |
401 | Missing token |
ERR_FORBIDDEN_001 |
403 | Not authorized |
ERR_NOT_FOUND_001 |
404 | Doctor not found |
GET /api/v1/secretaries/clinic/{clinicUuid}
Get all secretaries across all doctors of a clinic.
Permission: ROLE_CLINIC (must own clinic) | ROLE_ADMIN
Path Parameters
| Param | Type | Description |
|---|---|---|
clinicUuid |
string (UUID) | Clinic UUID |
Response 200
{
"success": true,
"data": [
{
"uuid": "...",
"user_name": "علی محمدی",
"mobile_number": "09...",
"doctor_name": "دکتر احمد رضایی",
"doctor_uuid": "...",
"is_active": true,
"permissions": { ... },
"created_at": 1717000000
}
]
}
Notes
- این 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 |
محدودیت پنل اشتراکی
تعداد منشیهای مجاز بر اساس پنل فعال doctor تعیین میشود:
| پنل | حداکثر منشی |
|---|---|
| Free (بدون اشتراک) | ۱ |
| Basic | ۳ |
| Professional | ۱۰ |
اگر تعداد منشیهای فعال به حد مجاز رسیده باشد، ایجاد منشی جدید خطای زیر را برمیگرداند:
{
"success": false,
"errors": [
{
"code": "ERR_SECRETARY_001",
"message": "پلن فعلی اجازه منشی بیشتر را نمیدهد"
}
]
}
برای افزایش محدودیت، باید پنل را از POST /api/v1/subscription/trial (تریال) یا POST /api/v1/subscription-payment (پرداخت) ارتقاء داد.