feat: Add online share functionality for secretaries
- Introduced `online_share_enabled` and `online_share_percent` fields in the `doctor_secretaries` table to manage secretary shares from online appointments. - Added `bank_account` field in the `profiles` table to store user-level IBANs for settlements. - Created `secretary_earnings` table to track earnings per secretary from online appointments, including a foreign key relationship with `financial_breakdowns`. - Implemented `SecretaryEarning` entity and repository for managing secretary earnings. - Developed `SecretaryShareResolver` service to determine which secretaries earn from online payments. - Added `UserIbanResolver` service to handle user IBAN retrieval and management. - Created `HasIbansTrait` for entities to manage IBANs in a JSON format. - Implemented tests for secretary earnings and API endpoints for managing secretary shares and IBANs.
This commit is contained in:
@@ -534,3 +534,176 @@ Get all secretaries across **all doctors** of a clinic.
|
||||
```
|
||||
|
||||
برای افزایش محدودیت، باید پنل را از `POST /api/v1/subscription/trial` (تریال) یا `POST /api/v1/subscription-payment` (پرداخت) ارتقاء داد.
|
||||
|
||||
---
|
||||
|
||||
## سهم منشی از نوبتهای آنلاین (درآمد و تسویه)
|
||||
|
||||
ادمین میتواند برای هر رابطهٔ منشی–پزشک/کلینیک، محاسبهٔ درآمد از نوبتهای آنلاین را
|
||||
فعال کند و درصد بدهد ([admin.md](admin.md#put-apiv1adminsecretaryuuidonline-share)).
|
||||
سهم از **مبلغ خالص** نوبت گرفته میشود: ابتدا هزینهٔ پنل پیامک، بعد مالیات، سپس درصدِ
|
||||
منشی روی «خالصِ پس از مالیات» — همان مبنایی که پورسانت نماینده از آن محاسبه میشود
|
||||
([settlement.md](settlement.md)).
|
||||
|
||||
**«آنلاین» یعنی چه؟** تقسیم مالی تنها پس از پرداخت موفق درگاه (`PaymentManager`) اجرا
|
||||
میشود؛ نوبتی که در پنل ثبت و «قطعی» میشود از این مسیر عبور نمیکند و سهمی نمیسازد.
|
||||
انتساب بر پایهٔ محیط نوبت است: کلینیکِ نوبت، وگرنه خودِ پزشک. اگر چند منشیِ سهمبر وجود
|
||||
داشته باشد، **هر کدام درصد خودش** را میگیرد (تقسیم نمیشود)؛ اگر مجموع درصدها از ۱۰۰
|
||||
بگذرد به نسبت کلیپ میشود و هشدار لاگ میگردد تا سهم سیستم منفی نشود.
|
||||
|
||||
سهم هر منشی در جدول `secretary_earnings` ثبت و بهصورت اعتبار در کیف پول همان کاربر
|
||||
منظور میشود؛ برداشت از طریق `POST /api/v1/settlement` انجام میگیرد.
|
||||
|
||||
---
|
||||
|
||||
### GET `/api/v1/secretary/earnings/summary`
|
||||
|
||||
خلاصهٔ درآمد منشیِ جاری.
|
||||
|
||||
**Permission:** `AUTH` (کاربر منشی)
|
||||
|
||||
#### Response `200`
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"data": {
|
||||
"enabled": true,
|
||||
"share_percent": 5,
|
||||
"relations": [
|
||||
{ "relation_uuid": "rel-…", "doctor_name": "دکتر احمدی", "clinic_name": null, "share_percent": 5 }
|
||||
],
|
||||
"today_rials": 500000,
|
||||
"this_month_rials": 3000000,
|
||||
"total_rials": 9000000,
|
||||
"appointments_count": 4,
|
||||
"wallet_balance_rials": 9000000
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| فیلد | توضیح |
|
||||
|------|-------|
|
||||
| `enabled` | `false` یعنی هیچ رابطهٔ فعالی با سهمِ روشن ندارد؛ پنل پیام «فعال نیست» نشان میدهد (خطا نمیدهیم) |
|
||||
| `share_percent` | درصد اولین رابطهٔ سهمبر؛ تفکیک کامل در `relations` |
|
||||
| `today_rials` | از نیمهشب امروز |
|
||||
| `this_month_rials` | ۳۰ روز گذشته |
|
||||
| `wallet_balance_rials` | موجودی کیف پول همان کاربر (مبنای تسویه) |
|
||||
|
||||
---
|
||||
|
||||
### GET `/api/v1/secretary/earnings/report`
|
||||
|
||||
گزارش سطر-به-سطر سهم منشی (paginated).
|
||||
|
||||
**Permission:** `AUTH` (کاربر منشی)
|
||||
|
||||
#### Query Parameters
|
||||
| Param | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| `page` | integer | ❌ | پیشفرض ۱ |
|
||||
| `limit` | integer | ❌ | پیشفرض ۱۵، حداکثر ۱۰۰ |
|
||||
| `from` | integer | ❌ | Unix — از تاریخ |
|
||||
| `to` | integer | ❌ | Unix — تا تاریخ |
|
||||
|
||||
#### Response `200`
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": [
|
||||
{
|
||||
"uuid": "earning-uuid-…",
|
||||
"appointment_uuid": "appt-uuid-…",
|
||||
"doctor_name": "دکتر احمدی",
|
||||
"gross_rials": 10000000,
|
||||
"sms_fee_rials": 1000000,
|
||||
"tax_rials": 818182,
|
||||
"net_after_tax_rials": 8181818,
|
||||
"share_percent": 5,
|
||||
"share_rials": 409091,
|
||||
"created_at": 1700000000
|
||||
}
|
||||
],
|
||||
"meta": { "totalRecords": 1, "totalPages": 1, "currentPage": 1 }
|
||||
}
|
||||
```
|
||||
|
||||
منشیِ بدون سهم، پاسخ `200` با آرایهٔ خالی میگیرد (نه `403`).
|
||||
|
||||
---
|
||||
|
||||
### GET `/api/v1/secretary/me`
|
||||
|
||||
پروفایل منشیِ جاری: رابطهها با تنظیمات سهم + شمارههای شبا.
|
||||
|
||||
**Permission:** `AUTH` (کاربر منشی)
|
||||
|
||||
#### Response `200`
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"data": {
|
||||
"full_name": "زهرا رضایی",
|
||||
"mobile": "0912…",
|
||||
"bank_account": [
|
||||
{ "id": "iban-uuid-…", "iban": "IR…", "bank_name": "ملی", "owner_name": null, "verified": false, "created_at": 1700000000 }
|
||||
],
|
||||
"relations": [
|
||||
{ "relation_uuid": "rel-…", "doctor_name": "دکتر احمدی", "clinic_name": null, "online_share_enabled": true, "online_share_percent": 5 }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### POST `/api/v1/secretary/iban`
|
||||
|
||||
افزودن شماره شبا (حداکثر ۲) به پروفایل کاربرِ منشی — مثل پنل نماینده.
|
||||
|
||||
**Permission:** `AUTH` (کاربر منشی)
|
||||
|
||||
#### Request Body (`application/json`)
|
||||
```json
|
||||
{ "iban": "IR123456789012345678901234", "bank_name": "ملی", "owner_name": "زهرا رضایی" }
|
||||
```
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| `iban` | string | ✅ | الگوی `IR` + ۲۴ رقم (فاصلهها حذف میشود) |
|
||||
| `bank_name` | string | ❌ | نام بانک |
|
||||
| `owner_name` | string | ❌ | نام صاحب حساب |
|
||||
|
||||
#### Response `201`
|
||||
```json
|
||||
{ "success": true, "data": { "data": { "bank_account": [ { "id": "…", "iban": "IR…", "verified": false } ] } } }
|
||||
```
|
||||
|
||||
`verified` همیشه `false` ثبت میشود؛ **تأیید فقط از سمت ادمین** انجام میگیرد و تسویه تنها با شبای تأییدشده مجاز است.
|
||||
|
||||
#### Errors
|
||||
| Code | HTTP | Description |
|
||||
|------|------|-------------|
|
||||
| `ERR_VALIDATION_001` | 422 | شبا نامعتبر (`field: iban`) |
|
||||
| `ERR_VALIDATION_001` | 422 | بیش از دو شبا (`field: iban`) |
|
||||
|
||||
---
|
||||
|
||||
### DELETE `/api/v1/secretary/iban/{id}`
|
||||
|
||||
حذف یکی از شباهای منشیِ جاری.
|
||||
|
||||
**Permission:** `AUTH` (کاربر منشی)
|
||||
|
||||
#### Response `200`
|
||||
`{ success, data: { data: { bank_account: [...] } } }`
|
||||
|
||||
#### Errors
|
||||
| Code | HTTP | Description |
|
||||
|------|------|-------------|
|
||||
| `ERR_NOT_FOUND_001` | 404 | پروفایل/شبا یافت نشد |
|
||||
|
||||
> کیف پول و تسویه اندپوینت اختصاصی ندارند: `GET /api/v1/wallet/balance`، `GET /api/v1/wallet/transactions` و `POST /api/v1/settlement` کاربر-محورند ([settlement.md](settlement.md)).
|
||||
|
||||
Reference in New Issue
Block a user