Files
clinicpro/docs/api/representation.md
T

17 KiB
Raw Blame History

Representation (Agent) API

Prefix: /api/v1/representation

Representations are sales agents who earn commission on appointments booked through their referral.


POST /api/v1/representation

Create a new representation.

Permission: ROLE_ADMIN

Request Body (application/json)

{
  "full_name": "علی احمدی",
  "mobile_number": "09123456789",
  "city_id": 42,
  "commission_percent": 10,
  "bank_account": {
    "iban": "IR...",
    "account_number": "1234567890",
    "bank_name": "بانک ملت",
    "owner_name": "علی احمدی"
  }
}
Field Type Required Description
full_name string Agent full name
mobile_number string Login mobile (creates a User account)
city_id integer City ID (FK to categories where bundle=city)
commission_percent float Commission rate (0100)
bank_account object Bank details for settlements

Response 201

{
  "success": true,
  "data": {
    "uuid": "rep-uuid-...",
    "full_name": "علی احمدی",
    "mobile_number": "09123456789",
    "city_id": 42,
    "commission_percent": 10,
    "active": true,
    "bank_account": { ... },
    "created_at": 1717000000
  }
}

Errors

Code HTTP Description
ERR_AUTH_001 401 Missing token
ERR_AUTH_006 403 Not admin
ERR_CONFLICT_001 409 Mobile number already in use
ERR_VALIDATION_001 422 mobile_number فرمت معتبر موبایل ایران (^09\d{9}$) ندارد (field: mobile_number)
ERR_VALIDATION_002 422 mobile_number یا full_name خالی

GET /api/v1/representation/{uuid}

Get representation detail.

Permission: AUTH — must be the representation's user or ROLE_ADMIN

Path Parameters

Param Type Description
uuid string (UUID) Representation UUID

Response 200

{
  "success": true,
  "data": {
    "uuid": "...",
    "full_name": "علی احمدی",
    "mobile_number": "09123456789",
    "city_id": 42,
    "city_name": "تهران",
    "commission_percent": 10,
    "active": true,
    "bank_account": { ... },
    "created_at": 1717000000
  }
}

Errors

Code HTTP Description
ERR_AUTH_001 401 Missing token
ERR_FORBIDDEN_001 403 Not owner or admin
ERR_NOT_FOUND_001 404 Representation not found

PATCH /api/v1/representation/{uuid}

Update representation.

Permission: AUTH — must be the representation's user or ROLE_ADMIN

Request Body (application/json)

{
  "full_name": "علی احمدی جدید",
  "city_id": 50,
  "commission_percent": 12,
  "bank_account": { ... },
  "active": true
}

All fields optional.

Response 200

Updated representation object.

Errors

Code HTTP Description
ERR_AUTH_001 401 Missing token
ERR_FORBIDDEN_001 403 Not owner or admin
ERR_NOT_FOUND_001 404 Representation not found

DELETE /api/v1/representation/{uuid}

Delete a representation.

Permission: ROLE_ADMIN

Response 200

{ "success": true, "data": { "message": "نماینده حذف شد" } }

Errors

Code HTTP Description
ERR_AUTH_001 401 Missing token
ERR_AUTH_006 403 Not admin
ERR_NOT_FOUND_001 404 Representation not found

GET /api/v1/representation/{uuid}/dashboard/monthly

Get monthly earnings dashboard for a representation.

Permission: AUTH — must be the representation's user or ROLE_ADMIN

Path Parameters

Param Type Description
uuid string (UUID) Representation UUID

Query Parameters

Param Type Required Description
year integer e.g. 2024
month integer 112

Response 200

{
  "success": true,
  "data": {
    "period": { "year": 2024, "month": 6 },
    "stats": {
      "total_appointments": 15,
      "total_revenue_rials": 7500000,
      "commission_rials": 750000,
      "daily": [
        { "date": "2024-06-01", "appointments": 2, "revenue": 1000000, "commission": 100000 }
      ]
    }
  }
}

Errors

Code HTTP Description
ERR_AUTH_001 401 Missing token
ERR_FORBIDDEN_001 403 Not owner or admin
ERR_NOT_FOUND_001 404 Representation not found

GET /api/v1/representation/{uuid}/dashboard/yearly

Get yearly earnings dashboard for a representation.

Permission: AUTH — must be the representation's user or ROLE_ADMIN

Query Parameters

Param Type Required Description
year integer e.g. 2024

Response 200

{
  "success": true,
  "data": {
    "period": { "year": 2024 },
    "months": [
      { "month": 1, "appointments": 10, "revenue_rials": 5000000, "commission_rials": 500000 },
      { "month": 2, "appointments": 8, "revenue_rials": 4000000, "commission_rials": 400000 }
    ],
    "totals": {
      "appointments": 97,
      "revenue_rials": 48500000,
      "commission_rials": 4850000
    }
  }
}

پنل نماینده (ROLE_REPRESENTATION)

این endpointها برای کاربرِ دارای نقش ROLE_REPRESENTATION در پنل ادمین (/admin) هستند. مالکیت همیشه از کاربر جاری (#[CurrentUser] + findByUser) تعیین می‌شود؛ هیچ uuid/id ورودی برای تعیین مالکیت پذیرفته نمی‌شود.

Permission (همه‌ی این بخش): ROLE_REPRESENTATION

GET /api/v1/representation/me

پروفایل نماینده‌ی کاربر جاری.

Response 200

{
  "success": true,
  "data": {
    "data": {
      "uuid": "...",
      "full_name": "حامد حسینی",
      "mobile_number": "09120671756",
      "city_id": 132,
      "commission_percent": "10.00",
      "bank_account": null,
      "active": true,
      "created_at": 1718000000
    }
  }
}

double-nested: مقدار با data.data استخراج می‌شود.

Errors

Code HTTP Description
ERR_NOT_FOUND_001 404 کاربر جاری نماینده نیست

POST /api/v1/representation/doctor

افزودن پزشک توسط نماینده. representation_id پزشک به‌صورت خودکار روی نماینده‌ی کاربر جاری ست می‌شود. پس از ثبت موفق، یک پیامک خوش‌آمد (تگ welcome) به‌صورت async به موبایل پزشک ارسال می‌شود.

Request Body

{ "mobile": "0935...", "name": "دکتر ...", "gender": "man", "degree": "...", "medical_system_code": "...", "specialties": [1,2] }
Field Type Required
mobile string
name string
gender / degree / medical_system_code / info string
specialties integer[]

Response 201

{ "success": true, "data": { "uuid": "..." } }

Errors

Code HTTP Description
ERR_VALIDATION_002 422 موبایل یا نام خالی
ERR_VALIDATION_001 422 mobile فرمت معتبر موبایل ایران ندارد (field: mobile)
ERR_CONFLICT_001 409 این کاربر قبلاً پزشک است

POST /api/v1/representation/clinic

افزودن کلینیک توسط نماینده. representation_id کلینیک خودکار روی نماینده‌ی کاربر جاری ست می‌شود (مثل createDoctor) تا در لیست‌های scoped دیده شود. پس از ثبت موفق، یک پیامک خوش‌آمد (تگ welcome) به‌صورت async به موبایل مالک کلینیک ارسال می‌شود.

Request Body

{ "owner_mobile": "0935...", "name": "کلینیک ...", "telephone": "...", "address": "..." }
Field Type Required
owner_mobile string
name string
telephone / address / info string

Response 200

{ "success": true, "data": { "uuid": "...", "name": "...", "is_active": true } }

Errors

Code HTTP Description
ERR_VALIDATION_002 422 موبایل یا نام خالی
ERR_VALIDATION_001 422 owner_mobile فرمت معتبر موبایل ایران ندارد (field: owner_mobile)

GET /api/v1/representation/appointments

نوبت‌های همه‌ی پزشکانی که representation_id آن‌ها = نماینده‌ی کاربر جاری است (paginated، با شکل آیتمِ یکسان با /api/v1/admin/appointments).

Query Parameters

Param Type Required Description
page integer پیش‌فرض 1
limit integer پیش‌فرض 15، حداکثر 500
status string فیلتر وضعیت
date string (YYYY-MM-DD) فیلتر تاریخِ نوبت
search string جستجو در موبایل/نام بیمار یا نام پزشک

Response 200

{
  "success": true,
  "data": [
    {
      "uuid": "...",
      "patient_name": "...",
      "patient_mobile": "0912...",
      "doctor_uuid": "...",
      "doctor_name": "دکتر ...",
      "slot_start": 1718000000,
      "slot_end": 1718001800,
      "appointment_date": "2025-06-15",
      "appointment_time": "10:00",
      "end_time": "10:30",
      "status": "confirmed",
      "created_at": 1717900000
    }
  ],
  "meta": { "totalRecords": 12, "totalPages": 1, "currentPage": 1 }
}

Errors

Code HTTP Description
ERR_NOT_FOUND_001 404 کاربر جاری نماینده نیست

GET /api/v1/representation/doctors

پزشکانِ ثبت‌شده توسط نماینده‌ی جاری (فقط ردیف‌های representation_id = نماینده‌ی کاربر جاری). شکل آیتم یکسان با GET /api/v1/admin/doctors است.

Permission: ROLE_REPRESENTATION — id نماینده از #[CurrentUser] تعیین می‌شود، نه از query (نماینده نمی‌تواند داده‌ی نماینده‌ی دیگر را ببیند).

Query Parameters

Param Type Required Description
page integer پیش‌فرض 1
limit integer پیش‌فرض 15، حداکثر 100
search string جستجو در نام یا موبایل پزشک

Response 200

{
  "success": true,
  "data": [
    {
      "uuid": "...", "id": 12, "name": "دکتر ...", "gender": "man", "degree": "...",
      "medical_code": "...", "mobile": "0912...", "email": null,
      "is_active": true, "rate": 3.5, "specialties": [],
      "profile_image": null, "created_at": "2026-06-18T..."
    }
  ],
  "meta": { "totalRecords": 1, "totalPages": 1, "currentPage": 1 }
}

Errors

Code HTTP Description
ERR_NOT_FOUND_001 404 کاربر جاری نماینده نیست

GET /api/v1/representation/doctors/stats

آمار پزشکانِ ثبت‌شده توسط نماینده‌ی جاری (فقط representation_id = نماینده‌ی کاربر جاری). شکل پاسخ سازگار با GET /api/v1/admin/doctors/stats (بدون top_specialty). فرانت‌اند کارت‌های «کل پزشکان / فعال / غیرفعال / مرد / زن» را از این endpoint برای نقش نماینده پر می‌کند.

Permission: ROLE_REPRESENTATION — id نماینده از #[CurrentUser].

Response 200

{
  "success": true,
  "data": { "total": 12, "active": 9, "inactive": 3, "male": 7, "female": 5 }
}

Errors

Code HTTP Description
ERR_NOT_FOUND_001 404 کاربر جاری نماینده نیست

POST /api/v1/representation/doctors/{uuid}/status

فعال/غیرفعال کردن پزشکِ زیرمجموعه‌ی نماینده‌ی جاری (toggle active_doctor_appointment). فقط روی پزشکانی که representation_id آن‌ها برابر نماینده‌ی کاربر جاری است؛ در غیر این صورت 404.

Permission: ROLE_REPRESENTATION — مالکیت از #[CurrentUser] چک می‌شود.

Path Parameters

Param Type Description
uuid string uuid پزشک

Response 200

{ "success": true, "data": { "is_active": false } }

Errors

Code HTTP Description
ERR_NOT_FOUND_001 404 کاربر جاری نماینده نیست، یا پزشک یافت نشد / متعلق به این نماینده نیست

GET /api/v1/representation/clinics

کلینیک‌های ثبت‌شده توسط نماینده‌ی جاری (فقط representation_id = نماینده‌ی کاربر جاری). شکل آیتم سازگار با GET /api/v1/admin/clinics.

Permission: ROLE_REPRESENTATION — id نماینده از #[CurrentUser].

Query Parameters

Param Type Required Description
page integer پیش‌فرض 1
limit integer پیش‌فرض 15، حداکثر 100
search string جستجو در نام یا تلفن کلینیک

Response 200

{
  "success": true,
  "data": [
    {
      "uuid": "...", "id": 5, "name": "کلینیک ...", "telephone": "...",
      "logo": null, "clinic_logo": null, "is_active": true,
      "doctors_count": 0, "created_at": "2026-06-18T..."
    }
  ],
  "meta": { "totalRecords": 1, "totalPages": 1, "currentPage": 1 }
}

Errors

Code HTTP Description
ERR_NOT_FOUND_001 404 کاربر جاری نماینده نیست

داشبورد، عملکرد و مالیِ نماینده‌ی جاری

همه‌ی این endpointها #[IsGranted('ROLE_REPRESENTATION')] و scope بر اساس #[CurrentUser] (نه uuid مسیر). درآمد همیشه از FinancialBreakdown.representation_share_rials (پورسانت واقعیِ ثبت‌شده) محاسبه می‌شود، نه مبلغ کل نوبت. بازه‌ها: امروز=strtotime('today'), هفته=۷ روز اخیر, ماه=۳۰ روز اخیر.

GET /api/v1/representation/dashboard/summary

خلاصه‌ی آمار نوبت و درآمد نماینده‌ی جاری.

Response 200:

{
  "success": true,
  "data": {
    "appointments": { "today": 0, "week": 3, "month": 12, "total": 40 },
    "income": {
      "today": 0, "week": 270000, "month": 909090, "total": 3000000,
      "settlable_rials": 2090910, "settled_rials": 500000, "pending_rials": 0
    }
  }
}

settlable_rials = موجودی کیف‌پول (getWalletBalancesettled_rials = جمع Settlementهای paid؛ pending_rials = جمع pending+approved.

Errors

Code HTTP Description
ERR_NOT_FOUND_001 404 کاربر جاری نماینده نیست

GET /api/v1/representation/doctors/performance

عملکرد پزشکانِ نماینده‌ی جاری (paginated). Query: page, limit.

Response 200 (paginated):

{
  "success": true,
  "data": [
    {
      "uuid": "...", "name": "دکتر ...",
      "appointments": { "today": 0, "week": 1, "month": 4, "total": 18 },
      "representation_income_rials": 363636,
      "subscription_status": "active"
    }
  ],
  "meta": { "totalRecords": 1, "totalPages": 1, "currentPage": 1 }
}

subscription_status: active (اشتراک فعال دارد) یا none.

GET /api/v1/representation/finance/report

گزارش مالی بازه‌ای از ردیف‌های FinancialBreakdown نماینده‌ی جاری (paginated). Query: page, limit, from (Unix ts), to (Unix ts).

Response 200 (paginated):

{
  "success": true,
  "data": [
    {
      "uuid": "...", "appointment_uuid": "...", "doctor_name": "دکتر ...",
      "gross_rials": 2000000, "tax_rials": 45455, "sms_fee_rials": 1500000,
      "commission_percent": 20, "representation_share_rials": 90909,
      "created_at": "2026-06-24T..."
    }
  ],
  "meta": { "totalRecords": 1, "totalPages": 1, "currentPage": 1 }
}

اصلاح buildStats (در GET /api/v1/representation/{uuid}/dashboard/monthly|yearly): قبلاً آمار را به نماینده فیلتر نمی‌کرد (کلِ پلتفرم). اکنون total_appointments فقط نوبت‌های پزشکانِ همان نماینده، commission_rials از FinancialBreakdown.representation_share_rials، و total_revenue_rials از FinancialBreakdown.gross_rials (source=appointment) همان نماینده محاسبه می‌شود.