Files
clinicpro/docs/api/billing.md
T

6.7 KiB

Billing API — صورتحساب (فاز ۴ سیستم صورتحساب/بیمه)

Prefix: /api/v1/billing دامنه: App\Billing. مرجع معماری: docs/architecture/insurance-billing-system.md. tenant از #[CurrentUser] resolve می‌شود (ROLE_DOCTOR→doctor، ROLE_CLINIC→clinic).

صورتحساب (Invoice) از یک Encounter (PatientSession) ساخته می‌شود. برای هر آیتم سهم بیمه‌ی پایه، بیمه‌ی مکمل و بیمار با BillingCalculator محاسبه می‌شود:

  • تعرفه‌ی خدمت از Tariff سال جاری (با fallback به ServiceItem.priceRials).
  • قانون پوشش از قرارداد بیمه‌ی tenant (TenantInsurance) + override خدمت (TenantServiceCoverage).
  • ترتیب محاسبه: کل → پوشش پایه (با سقف) → باقیمانده → پوشش مکمل → فرانشیز سهم بیمار.

نمونه: کل ۶۰۰٬۰۰۰ · پایه ۷۰٪ → ۴۲۰٬۰۰۰ · مکمل روی باقیمانده → ۱۲۰٬۰۰۰ · بیمار ۶۰٬۰۰۰.


POST /api/v1/billing/invoices

ساخت صورتحساب از یک مراجعه. اگر صورتحساب برای آن مراجعه قبلاً ساخته شده، همان برگردانده می‌شود (idempotent).

Permission: AUTH (مالک مراجعه)

Body:

{ "session_uuid": "…" }

Response 201:

{
  "success": true,
  "data": {
    "data": {
      "uuid": "…",
      "entity_type": "doctor",
      "entity_id": 7,
      "patient_session_id": 33,
      "base_insurance_id": 3,
      "supplementary_insurance_id": 9,
      "total_rials": 600000,
      "base_insurance_rials": 420000,
      "supplementary_rials": 120000,
      "patient_rials": 60000,
      "status": "draft",
      "issued_at": 1718900000,
      "items": [
        {
          "uuid": "…",
          "service_item_id": 12,
          "title": "ویزیت",
          "tariff_rials": 600000,
          "quantity": 1,
          "total_rials": 600000,
          "base_insurance_rials": 420000,
          "supplementary_rials": 120000,
          "patient_rials": 60000
        }
      ]
    }
  }
}

Errors: 422 ERR_VALIDATION_001 session_uuid الزامی · 404 ERR_NOT_FOUND_001 مراجعه یافت نشد · 403 ERR_FORBIDDEN_001 پروفایل یافت نشد.


GET /api/v1/billing/invoices/{uuid}

دریافت صورتحساب (فقط مالک tenant).

Response 200: همان ساختار بالا. Errors: 404 ERR_NOT_FOUND_001.


POST /api/v1/billing/invoices/{uuid}/finalize

نهایی‌سازی صورتحساب (draftfinalized). صورتحساب نهایی‌شده مبنای ساخت Claim (فاز ۵) است.

Response 200: صورتحساب با status: "finalized". Errors: 404 ERR_NOT_FOUND_001.


وضعیت‌های Invoice

draft (پیش‌نویس، قابل بازسازی) → finalized (نهایی) → paid (پرداخت‌شده) · void (باطل).

نکات

  • پول: integer ریال. تاریخ: Unix timestamp.
  • BillingCalculator خالص و واحد-تست‌شده است (tests/Billing/BillingCalculatorTest.php).
  • این فاز جایگزین تدریجی PatientService::calculateFinalPrice است؛ آن متد فعلاً برای سازگاری باقی مانده.

Claims — مطالبات بیمه (فاز ۵)

مطالبه (Claim) از یک صورتحساب نهایی‌شده ساخته می‌شود: یک Claim برای بیمه‌ی پایه و یک Claim برای بیمه‌ی مکمل (فقط اگر سهم بیمه > ۰). هر ClaimItem به یک InvoiceItem ارجاع می‌دهد. چرخه‌ی وضعیت با state machine.

ساخت خودکار: هنگام ثبت session بیمه‌دار (POST /api/v1/patient/{uuid}/session)، زنجیره‌ی صورتحساب→نهایی‌سازی→مطالبه به‌صورت خودکار اجرا می‌شود؛ نیازی به فراخوانی دستی POST /billing/claims نیست. برای هر صورتحساب تنها یک‌بار مطالبه ساخته می‌شود (تلاش مجدد 422).

وضعیت‌ها: pending → submitted → {approved → paid | rejected}. از paid/rejected خروجی ندارد.

POST /api/v1/billing/claims

ساخت مطالبات از یک صورتحساب نهایی‌شده.

Body: { "invoice_uuid": "…" } Response 201: { success, data: [ …claims ] } (یک یا دو مطالبه: پایه/مکمل). Errors: 422 صورتحساب نهایی نشده / سهم بیمه ندارد / مطالبه از قبل ثبت شده · 404 صورتحساب یافت نشد.

هر مطالبه در پاسخ علاوه بر insurance_id، فیلد insurance_name (نام بیمه، یا null اگر بیمه حذف شده باشد) را نیز دارد. در POST و transition و GET یکسان است.

GET /api/v1/billing/claims?status=

لیست مطالبات tenant. فیلتر اختیاری status (pending|submitted|approved|rejected|paid). هر آیتم شامل insurance_name است.

POST /api/v1/billing/claims/{uuid}/{action}

انتقال وضعیت. actionsubmit|approve|reject|pay.

action body اختیاری اثر
submit pending → submitted
approve approved_rials submitted → approved (پیش‌فرض = کل ادعا)
reject reason (الزامی) submitted → rejected
pay paid_rials approved → paid (پیش‌فرض = approved)

Errors: 422 انتقال نامعتبر یا دلیل رد خالی · 404 مطالبه یافت نشد.

GET /api/v1/billing/reports/insurance-debt

گزارش بدهی بیمه‌ها برای tenant (group بر اساس بیمه).

Response 200:

{
  "success": true,
  "data": {
    "data": [
      { "insurance_id": 3, "insurance_name": "تأمین اجتماعی", "claimed": 840000, "approved": 800000, "paid": 500000, "debt": 340000 }
    ]
  }
}

debt = claimed - paid (حداقل صفر).


ارسال مطالبه (ClaimSubmitter)

عملِ submit از طریق interface App\Billing\Contract\ClaimSubmitterInterface انجام می‌شود. پیاده‌سازی پیش‌فرض ManualClaimSubmitter است (ارسال دستی/آفلاین — همیشه موفق). برای اتصال آینده به API شرکت‌های بیمه‌ی ایران کافی است یک پیاده‌سازی جدید از این interface ساخته و در config/services.yaml bind شود؛ ClaimService تغییر نمی‌کند (Dependency Inversion). اگر submit ناموفق باشد، انتقال وضعیت با 422 متوقف می‌شود.