feat: enhance staff management and payment gateway features
- Fix national code handling in staff creation and updates to support Persian digits. - Update ClinicStaff entity to allow longer national codes (up to 15 characters). - Implement support for clinic secretaries in SecretaryController, allowing creation without a doctor UUID. - Add a new endpoint to retrieve doctors associated with a clinic for secretary management. - Improve appointment management by ensuring doctors are selectable even when no appointments exist. - Extend PatientController to allow secretaries to create patient records if they have the appropriate permissions. - Introduce a PriceInput component for better price formatting in forms, supporting Persian digits. - Add a MockGateway for testing payment processes without real transactions. - Enhance SMS settings management with an approval flow for post-visit text messages, including new fields for pending text and status. - Update migrations to reflect changes in database schema for national codes and SMS settings.
This commit is contained in:
+29
-2
@@ -775,7 +775,16 @@ Returns all site configuration values.
|
||||
"site_name": "ClinicPro",
|
||||
"support_phone": "",
|
||||
"max_cancel_hours_before": "24",
|
||||
"appointment_reminder_hours": "2"
|
||||
"appointment_reminder_hours": "2",
|
||||
"payment_test_mode": "0",
|
||||
"mellat_terminal_id": "",
|
||||
"mellat_username": "",
|
||||
"mellat_password": "",
|
||||
"sep_terminal_id": "",
|
||||
"sms_provider": "kavenegar",
|
||||
"kavenegar_api_key": "",
|
||||
"kavenegar_sender": "",
|
||||
"sms_price_rials": "500"
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -791,7 +800,16 @@ Update one or more settings. Unknown keys are silently ignored.
|
||||
{
|
||||
"commission_enabled": "1",
|
||||
"commission_percent": "5",
|
||||
"site_name": "کلینیکپرو"
|
||||
"site_name": "کلینیکپرو",
|
||||
"payment_test_mode": "1",
|
||||
"mellat_terminal_id": "12345678",
|
||||
"mellat_username": "user",
|
||||
"mellat_password": "pass",
|
||||
"sep_terminal_id": "87654321",
|
||||
"sms_provider": "kavenegar",
|
||||
"kavenegar_api_key": "your-api-key",
|
||||
"kavenegar_sender": "10008664",
|
||||
"sms_price_rials": "500"
|
||||
}
|
||||
```
|
||||
|
||||
@@ -802,6 +820,15 @@ Update one or more settings. Unknown keys are silently ignored.
|
||||
- `commission_percent` — integer string, `0`–`100`
|
||||
- Commission applies only to regular users (`booked_by = user`); secretaries are exempt
|
||||
|
||||
**Payment gateway rules:**
|
||||
- `payment_test_mode` — `"1"` = all payments use MockGateway (no real bank calls), `"0"` = real gateways
|
||||
- Gateway credentials (mellat/sep) read from DB first, fallback to env vars if DB value is empty
|
||||
- MockGateway callback: same URL pattern + `&mock=1&ResCode=0&RefId=MOCK-{orderId}`
|
||||
|
||||
**SMS provider rules:**
|
||||
- `sms_provider` — `"kavenegar"` or `"rangineh"`
|
||||
- Kavenegar API key and sender read from DB first, fallback to env vars `KAVENEGAR_API_KEY`, `KAVENEGAR_SENDER`
|
||||
|
||||
---
|
||||
|
||||
## Pre-Registration Management
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
Patient records track patients per entity (doctor or clinic). Each record holds multiple sessions (visits). Access requires an active subscription with the `patient_records` feature.
|
||||
|
||||
**Base path:** `/api/v1`
|
||||
**Auth:** Bearer JWT (doctor or clinic role required)
|
||||
**Auth:** Bearer JWT (doctor, clinic, or secretary with `appointments.view` permission required)
|
||||
|
||||
---
|
||||
|
||||
|
||||
+56
-8
@@ -10,7 +10,7 @@ Secretaries are linked to a doctor and have granular permissions controlling wha
|
||||
|
||||
Create a secretary for a doctor.
|
||||
|
||||
**Permission:** `ROLE_DOCTOR` — must own the doctor profile
|
||||
**Permission:** `ROLE_DOCTOR` (must own the doctor) | `ROLE_CLINIC` (must have the doctor in its clinic) | `ROLE_ADMIN`
|
||||
|
||||
### Request Body (`application/json`)
|
||||
```json
|
||||
@@ -74,10 +74,12 @@ Create a secretary for a doctor.
|
||||
"success": true,
|
||||
"data": {
|
||||
"uuid": "sec-uuid-...",
|
||||
"user_name": "علی محمدی",
|
||||
"mobile_number": "09123456789",
|
||||
"active": true,
|
||||
"doctor_name": "دکتر احمد رضایی",
|
||||
"doctor_uuid": "...",
|
||||
"is_active": true,
|
||||
"permissions": { ... },
|
||||
"doctor": { "uuid": "...", "title": "دکتر علی احمدی" },
|
||||
"created_at": 1717000000
|
||||
}
|
||||
}
|
||||
@@ -87,9 +89,9 @@ Create a secretary for a doctor.
|
||||
| Code | HTTP | Description |
|
||||
|------|------|-------------|
|
||||
| `ERR_AUTH_001` | 401 | Missing token |
|
||||
| `ERR_AUTH_006` | 403 | Not a doctor or not the doctor's owner |
|
||||
| `ERR_AUTH_006` | 403 | Not the doctor owner / clinic owner / admin |
|
||||
| `ERR_NOT_FOUND_001` | 404 | Doctor not found |
|
||||
| `ERR_CONFLICT_001` | 409 | Mobile number already in use |
|
||||
| `ERR_CONFLICT_001` | 409 | Secretary already added for this doctor |
|
||||
| `ERR_SECRETARY_001` | 422 | Plan limit for secretaries reached |
|
||||
|
||||
---
|
||||
@@ -189,7 +191,7 @@ Delete a secretary.
|
||||
|
||||
Get all secretaries for a specific doctor.
|
||||
|
||||
**Permission:** `AUTH` — must be the doctor or `ROLE_ADMIN`
|
||||
**Permission:** `ROLE_DOCTOR` (must own doctor) | `ROLE_CLINIC` (must have doctor in clinic) | `ROLE_ADMIN`
|
||||
|
||||
### Path Parameters
|
||||
| Param | Type | Description |
|
||||
@@ -203,8 +205,11 @@ Get all secretaries for a specific doctor.
|
||||
"data": [
|
||||
{
|
||||
"uuid": "...",
|
||||
"user_name": "علی محمدی",
|
||||
"mobile_number": "09...",
|
||||
"active": true,
|
||||
"doctor_name": "دکتر احمد رضایی",
|
||||
"doctor_uuid": "...",
|
||||
"is_active": true,
|
||||
"permissions": { ... },
|
||||
"created_at": 1717000000
|
||||
}
|
||||
@@ -216,11 +221,54 @@ Get all secretaries for a specific doctor.
|
||||
| Code | HTTP | Description |
|
||||
|------|------|-------------|
|
||||
| `ERR_AUTH_001` | 401 | Missing token |
|
||||
| `ERR_FORBIDDEN_001` | 403 | Not the doctor |
|
||||
| `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`
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": [
|
||||
{
|
||||
"uuid": "...",
|
||||
"user_name": "علی محمدی",
|
||||
"mobile_number": "09...",
|
||||
"doctor_name": "دکتر احمد رضایی",
|
||||
"doctor_uuid": "...",
|
||||
"is_active": true,
|
||||
"permissions": { ... },
|
||||
"created_at": 1717000000
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Notes
|
||||
- یک منشی میتواند برای یک یا چند دکتر تعریف شود (جداگانه در جدول `doctor_secretaries`)
|
||||
- این endpoint همه منشیان همه دکترهای کلینیک را یکجا برمیگرداند با ستون `doctor_name` برای تشخیص
|
||||
|
||||
### 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 تعیین میشود:
|
||||
|
||||
@@ -390,6 +390,9 @@ Updated template with `status: "rejected"`.
|
||||
"reminder_hours_before": 2,
|
||||
"post_visit_enabled": false,
|
||||
"post_visit_text": null,
|
||||
"post_visit_text_pending": null,
|
||||
"post_visit_text_status": "none",
|
||||
"post_visit_text_reject_reason": null,
|
||||
"updated_at": 1718000000
|
||||
}
|
||||
}
|
||||
@@ -408,10 +411,50 @@ Updated template with `status: "rejected"`.
|
||||
}
|
||||
```
|
||||
|
||||
**تغییر رفتار `post_visit_text`:** متن ارسالشده مستقیماً اعمال نمیشود — در فیلد `post_visit_text_pending` ذخیره میشود و وضعیت `post_visit_text_status` به `pending` تغییر میکند. پس از تأیید ادمین، به `post_visit_text` منتقل میشود.
|
||||
|
||||
**مقادیر `post_visit_text_status`:** `none` | `pending` | `approved` | `rejected`
|
||||
|
||||
---
|
||||
|
||||
## Admin Endpoints
|
||||
|
||||
### GET /api/v1/admin/sms/settings/review
|
||||
|
||||
**Permission:** `ROLE_ADMIN` — لیست همه تنظیمات SMS با وضعیت `pending`
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"data": [
|
||||
{
|
||||
"id": 3,
|
||||
"entity_type": "doctor",
|
||||
"entity_id": 7,
|
||||
"post_visit_text_pending": "متن در انتظار تأیید",
|
||||
"post_visit_text_status": "pending",
|
||||
...
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### POST /api/v1/admin/sms/settings/{id}/approve
|
||||
|
||||
**Permission:** `ROLE_ADMIN` — تأیید متن پیامک. `post_visit_text_pending` به `post_visit_text` منتقل میشود.
|
||||
|
||||
### POST /api/v1/admin/sms/settings/{id}/reject
|
||||
|
||||
**Permission:** `ROLE_ADMIN` — رد متن پیامک.
|
||||
|
||||
```json
|
||||
{ "reason": "متن نامناسب است" }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### GET /api/v1/admin/sms/wallet-report
|
||||
|
||||
**Permission:** `ROLE_ADMIN` — لیست همه کیفهای پیامکی (paginated)
|
||||
|
||||
+1
-1
@@ -57,7 +57,7 @@
|
||||
| phone | string | ❌ |
|
||||
| job_title | string | ❌ |
|
||||
| address | string | ❌ |
|
||||
| national_code | string(10) | ❌ |
|
||||
| national_code | string(15) — ارقام فارسی به لاتین تبدیل میشوند | ❌ |
|
||||
|
||||
**Response 201:**
|
||||
```json
|
||||
|
||||
Reference in New Issue
Block a user