forked from Yara724/api
Merge pull request 'Added case type for all GET APIs' (#274) from s.yahyaee/yara724-api:main into main
Reviewed-on: Yara724/api#274
This commit is contained in:
1506
docs/blame-claim-flow-architecture.fa.html
Normal file
1506
docs/blame-claim-flow-architecture.fa.html
Normal file
File diff suppressed because it is too large
Load Diff
1893
docs/blame-claim-flow-architecture.html
Normal file
1893
docs/blame-claim-flow-architecture.html
Normal file
File diff suppressed because it is too large
Load Diff
596
docs/external-integrations-reference.fa.html
Normal file
596
docs/external-integrations-reference.fa.html
Normal file
@@ -0,0 +1,596 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="fa" dir="rtl">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>مرجع یکپارچهسازیهای خارجی</title>
|
||||||
|
<style>
|
||||||
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
body {
|
||||||
|
font-family: "Vazirmatn", "Tahoma", "Segoe UI", system-ui, sans-serif;
|
||||||
|
font-size: 14px; line-height: 1.8;
|
||||||
|
background: #ffffff; color: #1f2328; padding: 24px;
|
||||||
|
}
|
||||||
|
h1 { font-size: 20px; font-weight: 700; margin-bottom: 4px; }
|
||||||
|
.subtitle { font-size: 13px; color: #57606a; margin-bottom: 28px; }
|
||||||
|
h2 {
|
||||||
|
font-size: 15px; font-weight: 700;
|
||||||
|
margin-bottom: 10px; margin-top: 32px;
|
||||||
|
border-bottom: 1px solid #e5e7eb; padding-bottom: 6px;
|
||||||
|
}
|
||||||
|
h3 {
|
||||||
|
font-size: 12px; font-weight: 700;
|
||||||
|
text-transform: uppercase; letter-spacing: 0.03em;
|
||||||
|
color: #57606a; margin-bottom: 8px; margin-top: 14px;
|
||||||
|
}
|
||||||
|
.section-intro {
|
||||||
|
font-size: 13px; color: #57606a;
|
||||||
|
margin-bottom: 14px; line-height: 1.7;
|
||||||
|
}
|
||||||
|
.card {
|
||||||
|
border: 1px solid #e5e7eb; border-radius: 6px;
|
||||||
|
padding: 16px; background: #f7f8fa; margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
.card.card-blue { border-right: 4px solid #3b82f6; }
|
||||||
|
.card.card-green { border-right: 4px solid #22c55e; }
|
||||||
|
.card.card-purple { border-right: 4px solid #8b5cf6; }
|
||||||
|
.card.card-orange { border-right: 4px solid #f97316; }
|
||||||
|
.card.card-teal { border-right: 4px solid #14b8a6; }
|
||||||
|
.card.card-indigo { border-right: 4px solid #6366f1; }
|
||||||
|
.card.card-gray { border-right: 4px solid #94a3b8; }
|
||||||
|
.card.card-red { border-right: 4px solid #ef4444; }
|
||||||
|
.card.card-yellow { border-right: 4px solid #eab308; }
|
||||||
|
table {
|
||||||
|
border-collapse: collapse; width: 100%;
|
||||||
|
font-size: 12px; margin-top: 4px; direction: rtl;
|
||||||
|
}
|
||||||
|
th {
|
||||||
|
background: #f1f5f9; font-weight: 600;
|
||||||
|
text-align: right; padding: 5px 8px; border: 1px solid #e5e7eb;
|
||||||
|
}
|
||||||
|
td { padding: 4px 8px; border: 1px solid #e5e7eb; vertical-align: top; }
|
||||||
|
tr:nth-child(even) td { background: #ffffff; }
|
||||||
|
code {
|
||||||
|
font-family: monospace; font-size: 11px; color: #3b82d4;
|
||||||
|
direction: ltr; unicode-bidi: embed;
|
||||||
|
}
|
||||||
|
.method {
|
||||||
|
font-family: monospace; font-size: 11px;
|
||||||
|
font-weight: 700; white-space: nowrap;
|
||||||
|
direction: ltr; unicode-bidi: embed;
|
||||||
|
}
|
||||||
|
.method.get { color: #059669; }
|
||||||
|
.method.post { color: #2563eb; }
|
||||||
|
.method.put { color: #d97706; }
|
||||||
|
.method.patch { color: #7c3aed; }
|
||||||
|
.note { font-size: 11px; color: #57606a; font-style: normal; margin-top: 6px; }
|
||||||
|
.warn { font-size: 11px; color: #9a3412; font-style: normal; margin-top: 6px; }
|
||||||
|
.status-badge {
|
||||||
|
display: inline-block; font-size: 11px; font-weight: 600;
|
||||||
|
padding: 1px 7px; border-radius: 10px;
|
||||||
|
}
|
||||||
|
.status-live { background: #dcfce7; color: #166534; }
|
||||||
|
.status-partial { background: #ffedd5; color: #9a3412; }
|
||||||
|
.status-disabled { background: #fee2e2; color: #991b1b; }
|
||||||
|
.status-internal { background: #f1f5f9; color: #475569; border: 1px solid #e2e8f0; }
|
||||||
|
.toc {
|
||||||
|
background: #f7f8fa; border: 1px solid #e5e7eb;
|
||||||
|
border-radius: 6px; padding: 14px 18px; margin-bottom: 28px;
|
||||||
|
}
|
||||||
|
.toc-title { font-size: 13px; font-weight: 700; margin-bottom: 8px; }
|
||||||
|
.toc ol { padding-right: 18px; padding-left: 0; }
|
||||||
|
.toc li { font-size: 13px; margin-bottom: 3px; }
|
||||||
|
.toc a { color: #3b82d4; text-decoration: none; }
|
||||||
|
.toc a:hover { text-decoration: underline; }
|
||||||
|
footer {
|
||||||
|
text-align: center; font-size: 12px; color: #57606a;
|
||||||
|
border-top: 1px solid #e5e7eb; margin-top: 40px; padding-top: 12px;
|
||||||
|
}
|
||||||
|
.max-wrap { max-width: 760px; margin: 0 auto; }
|
||||||
|
.flow-box {
|
||||||
|
background: #ffffff; border: 1px solid #e5e7eb; border-radius: 4px;
|
||||||
|
padding: 10px 14px; font-size: 12px; margin-top: 8px;
|
||||||
|
}
|
||||||
|
.flow-step {
|
||||||
|
display: flex; gap: 10px; align-items: flex-start; margin-bottom: 6px;
|
||||||
|
direction: rtl;
|
||||||
|
}
|
||||||
|
.flow-num {
|
||||||
|
flex-shrink: 0; width: 20px; height: 20px; border-radius: 50%;
|
||||||
|
background: #3b82d4; color: #fff; font-size: 11px; font-weight: 700;
|
||||||
|
display: flex; align-items: center; justify-content: center;
|
||||||
|
}
|
||||||
|
.flow-text { flex: 1; padding-top: 2px; }
|
||||||
|
.decision-tree {
|
||||||
|
font-size: 12px; background: #ffffff;
|
||||||
|
border: 1px solid #e5e7eb; border-radius: 4px; padding: 12px 16px;
|
||||||
|
margin-top: 8px; line-height: 1.9;
|
||||||
|
}
|
||||||
|
.decision-tree ul { padding-right: 20px; padding-left: 0; }
|
||||||
|
.decision-tree li { margin-bottom: 2px; }
|
||||||
|
.env-table th:last-child { width: 220px; }
|
||||||
|
pre {
|
||||||
|
font-family: monospace; font-size: 11px;
|
||||||
|
background: #f1f5f9; border: 1px solid #e5e7eb;
|
||||||
|
border-radius: 4px; padding: 10px 12px;
|
||||||
|
white-space: pre-wrap; word-break: break-all;
|
||||||
|
margin-top: 6px; color: #1f2328;
|
||||||
|
direction: ltr; unicode-bidi: embed;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="max-wrap">
|
||||||
|
<h1>مرجع یکپارچهسازیهای خارجی</h1>
|
||||||
|
<p class="subtitle">
|
||||||
|
تمام یکپارچهسازیهای خروجی: کاربرد، زمان فعالشدن، نحوه احراز هویت،
|
||||||
|
رفتار retry، fallbackها و تمام متغیرهای محیطی. سرویسهای داخلی
|
||||||
|
(کپچا، دادههای پرسوجوی آفلاین) برای کاملبودن گنجانده شدهاند.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<!-- TOC -->
|
||||||
|
<div class="toc">
|
||||||
|
<div class="toc-title">فهرست</div>
|
||||||
|
<ol>
|
||||||
|
<li><a href="#inquiry-routing">درخت تصمیم مسیریابی پرسوجو</a></li>
|
||||||
|
<li><a href="#fanavaran">فناوران — پلتفرم خسارت بیمه</a></li>
|
||||||
|
<li><a href="#sanhub">SandHub — درگاه پرسوجوی قدیمی</a></li>
|
||||||
|
<li><a href="#tejarat">پرسوجوی تجارت — درگاه block-inquiry (V2+)</a></li>
|
||||||
|
<li><a href="#esg">ESG — ارائهدهنده پرسوجوی تنانت پارسیان</a></li>
|
||||||
|
<li><a href="#sms">پیامک — درگاههای کاوهنگار و پارسیان</a></li>
|
||||||
|
<li><a href="#ai">سرویس هوش مصنوعی — تشخیص خسارت خودرو</a></li>
|
||||||
|
<li><a href="#car-pricing">سرویس قیمت خودرو — جستجوی ارزش بازار</a></li>
|
||||||
|
<li><a href="#offline-inquiry">پرسوجوی آفلاین — دادههای fallback</a></li>
|
||||||
|
<li><a href="#env-ref">مرجع متغیرهای محیطی</a></li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||||
|
<h2 id="inquiry-routing">۱ — درخت تصمیم مسیریابی پرسوجو</h2>
|
||||||
|
<p class="section-intro">
|
||||||
|
هر فایل تقصیر با فراخوانی "run-inquiries" آغاز میشود که بیمهنامه طرف مقصر
|
||||||
|
را از یک ارائهدهنده خارجی دریافت میکند. اینکه کدام ارائهدهنده واقعاً فراخوانی
|
||||||
|
میشود به سه عامل بستگی دارد: تنانت (<code>CLIENT_ID</code>)، نوع فایل
|
||||||
|
(THIRD_PARTY در مقابل CAR_BODY) و اینکه آیا حالت API زنده در تنظیمات سیستم
|
||||||
|
فعال است یا خیر. لایه دادههای پرسوجوی آفلاین در جلوی هر سه ارائهدهنده قرار دارد.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="card card-indigo">
|
||||||
|
<h3>انتخاب ارائهدهنده</h3>
|
||||||
|
<div class="decision-tree">
|
||||||
|
<strong>برای هر پرسوجوی مبتنی بر پلاک:</strong>
|
||||||
|
<ul>
|
||||||
|
<li>۱. بررسی دادههای آفلاین (MongoDB) — اگر داده مطابق یافت شد، آن را برگردانده و تمام HTTP را رد کن.</li>
|
||||||
|
<li>۲. اگر <code>CLIENT_ID=8</code> (تنانت پارسیان/ESG) → مسیریابی به <strong>ESG</strong> <code>/inquiry/policyByPlate</code> یا <code>/inquiry/policyByChassis</code>.</li>
|
||||||
|
<li>۳. در غیر این صورت → مسیریابی به <strong>پرسوجوی تجارت</strong> <code>/block-inquiry-tejarat</code> (THIRD_PARTY) یا <code>/block-inquiry-tejarat/badane</code> (CAR_BODY).</li>
|
||||||
|
<li>۴. اگر <code>system_settings.externalApis.sandHubUseLiveApi = false</code> (پیشفرض) → پاسخ mock برگردانده شود به جای انجام فراخوانیهای HTTP.</li>
|
||||||
|
</ul>
|
||||||
|
<br>
|
||||||
|
<strong>برای بررسیهای هویت شخصی، گواهینامه، مالکیت و شبا:</strong>
|
||||||
|
<ul>
|
||||||
|
<li>اگر <code>CLIENT_ID=8</code> → ESG <code>/inquiry/person</code> و <code>/inquiry/sheba</code>.</li>
|
||||||
|
<li>در غیر این صورت → تجارت/SandHub <code>/personal-inquiry/tejarat-no</code>، <code>/driver-license-check</code>، <code>/ownership</code>، <code>/sheba/sheba-tejaratno</code>.</li>
|
||||||
|
</ul>
|
||||||
|
<br>
|
||||||
|
<strong>تفاوت کلیدی — فرمت تاریخ تولد:</strong>
|
||||||
|
SandHub/تجارت تاریخ تولد <em>میلادی</em> انتظار دارند (داخلی از جلالی تبدیل میشود).
|
||||||
|
ESG مستقیماً تاریخ <em>جلالی</em> انتظار دارد.
|
||||||
|
</div>
|
||||||
|
<p class="note" style="margin-top:8px;">
|
||||||
|
اندپوینتهای SandHub فقط در مسیرهای قدیمی کد استفاده میشوند. تمام جریانهای فعال تقصیر V2+ از طریق ارائهدهندگان تجارت یا ESG میروند.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||||
|
<h2 id="fanavaran">۲ — فناوران <span class="status-badge status-live">فعال</span></h2>
|
||||||
|
<p class="section-intro">
|
||||||
|
فناوران (<code>apimanager.iraneit.com</code>) پلتفرم ملی پرونده خسارت بیمه است.
|
||||||
|
پس از اینکه کارشناس خسارت ارزیابی خود را ارسال میکند، سیستم بهصورت خودکار
|
||||||
|
یک خسارت ساختاریافته را از طریق یک پروتکل چهار مرحلهای به فناوران ارسال میکند.
|
||||||
|
فناوران همچنین بهعنوان منبع جستجوی code-listها (انواع تصادف، اجزای خودرو،
|
||||||
|
کدهای شهر و غیره) در سراسر پلتفرم عمل میکند.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="card card-blue">
|
||||||
|
<h3>چرخه حیات احراز هویت</h3>
|
||||||
|
<div class="flow-box">
|
||||||
|
<div class="flow-step"><div class="flow-num">۱</div><div class="flow-text"><strong>GET AppToken</strong> — <code>POST /EITAuthentication/GetAppToken</code> با هدرهای <code>appname</code> + <code>secret</code>. هدر <code>apptoken</code> را برمیگرداند.</div></div>
|
||||||
|
<div class="flow-step"><div class="flow-num">۲</div><div class="flow-text"><strong>Login</strong> — <code>POST /EITAuthentication/Login</code> با هدرهای <code>appToken</code> + <code>userName</code> + <code>password</code>. هدر <code>authenticationToken</code> را برمیگرداند.</div></div>
|
||||||
|
<div class="flow-step"><div class="flow-num">۳</div><div class="flow-text"><strong>Cache</strong> — توکن در حافظه <em>و</em> پایدار در MongoDB (<code>fanavaran_auth_tokens</code>) ذخیره میشود. تا نیمهشب <strong>Asia/Tehran</strong> معتبر است — اولین فراخوانی پس از ۰۰:۰۰ یک توکن تازه دریافت میکند.</div></div>
|
||||||
|
<div class="flow-step"><div class="flow-num">۴</div><div class="flow-text"><strong>تمام فراخوانیهای بعدی</strong> چهار هدر شامل میشوند: <code>authenticationToken</code>، <code>CorpId</code>، <code>ContractId</code>، <code>Location</code> — مختص تنانت، hardcoded به ازای هر کلید <code>FANAVARAN_CLIENT</code>.</div></div>
|
||||||
|
</div>
|
||||||
|
<p class="note" style="margin-top:8px;">
|
||||||
|
یک اثر انگشت پیکربندی (هش appName + secret + username + password + corpId + contractId + location)
|
||||||
|
یک ورود تازه را زمانی که هر مدرکی تغییر کند، حتی قبل از نیمهشب، مجبور میکند.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-blue">
|
||||||
|
<h3>پروتکل ارسال خسارت (۴ مرحله)</h3>
|
||||||
|
<div class="flow-box">
|
||||||
|
<div class="flow-step"><div class="flow-num">۱</div><div class="flow-text"><strong>خسارت پایه (GEN.03)</strong> — <code>POST /car/third-party-car-financial-claims</code>. دادههای مالک، راننده، بیمه، وسیله نقلیه و تصادف را ارسال میکند. یک <code>claimId</code> و <code>claimNo</code> فناوران برمیگرداند. پیامک با هر دو شناسه برای مالک ارسال میشود.</div></div>
|
||||||
|
<div class="flow-step"><div class="flow-num">۲</div><div class="flow-text"><strong>موارد خسارت (GEN.05)</strong> — <code>POST /car/third-party-car-financial-claims/{claimId}/dmg-cases</code>. یک ورودی به ازای هر قطعه آسیبدیده با شناسه کامپوننت، شدت و قیمت. سقف: کل ≤ ۵۳،۰۰۰،۰۰۰ تومان.</div></div>
|
||||||
|
<div class="flow-step"><div class="flow-num">۳</div><div class="flow-text"><strong>پیوستها (GEN.07)</strong> — <code>POST /car/third-party-car-financial-claims/{claimId}/files</code>. اسناد، تصاویر car-capture و ویدیوها که با شناسه فایل ارجاع داده شدهاند.</div></div>
|
||||||
|
<div class="flow-step"><div class="flow-num">۴</div><div class="flow-text"><strong>کارشناسی (GEN.08)</strong> — <code>POST /car/third-party-car-financial-claims/{claimId}/expertise</code>. متادیتای ارزیابی کارشناس (نقش کارشناس، تاریخ، نتیجه). ارسال را نهایی میکند.</div></div>
|
||||||
|
</div>
|
||||||
|
<p class="note" style="margin-top:8px;">
|
||||||
|
هر چهار مرحله در مجموعه <code>fanavaran_audit_logs</code> با بدنه کامل درخواست/پاسخ، وضعیت HTTP، مدت زمان و کد ردیابی برای اشکالزدایی ثبت میشوند.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-blue">
|
||||||
|
<h3>اندپوینتهای Lookup</h3>
|
||||||
|
<p class="note">همه زیر <code>https://apimanager.iraneit.com/BimeApiManager/api/BimeApi/v2.0/</code>. نتایج روی دیسک (به ازای کلید مشتری) و در مجموعه MongoDB <code>lookups</code> کش میشوند. تنانت پارسیان قبل از درخواست API از DB میخواند؛ دیگران ابتدا به API میروند.</p>
|
||||||
|
<table>
|
||||||
|
<tr><th>کاربرد</th><th>مسیر</th></tr>
|
||||||
|
<tr><td>گزینههای dropdown برای accidentReason (نگاشت شده به شناسههای محلی)</td><td><code>/car/base-info/accident-causes</code></td></tr>
|
||||||
|
<tr><td>گزینههای accidentWay</td><td><code>/car/code-list/accident-report-type</code></td></tr>
|
||||||
|
<tr><td>طبقهبندی استفاده از وسیله نقلیه</td><td><code>/car/base-info/vehicle-use-types</code></td></tr>
|
||||||
|
<tr><td>روش پرداخت خسارت</td><td><code>/car/code-list/dmg-pay-method</code></td></tr>
|
||||||
|
<tr><td>گزینههای نوع گواهینامه</td><td><code>/car/base-info/driving-licence-types</code></td></tr>
|
||||||
|
<tr><td>طبقهبندی طرف مقصر</td><td><code>/car/code-list/accident-culprit-type</code></td></tr>
|
||||||
|
<tr><td>گزینههای محل بازرسی</td><td><code>/car/code-list/inspection-place</code></td></tr>
|
||||||
|
<tr><td>کدهای وضعیت کاهش قیمت</td><td><code>/car/code-list/drop-amount-status</code></td></tr>
|
||||||
|
<tr><td>کاتالوگ کامپوننت (نگاشت به قطعات بیرونی/داخلی)</td><td><code>/car/base-info/car-components</code></td></tr>
|
||||||
|
<tr><td>گزینههای شدت تصادف</td><td><code>/car/code-list/accident-level</code></td></tr>
|
||||||
|
<tr><td>تطبیق <code>INSURANCE_CORP_ID</code> ← corpId فناوران</td><td><code>/common/code-list/insurance-corp</code></td></tr>
|
||||||
|
<tr><td>انتخابگرهای شهر/استان</td><td><code>/common/base-info/cities</code>، <code>/common/base-info/Provinces</code></td></tr>
|
||||||
|
<tr><td>دریافت بیمهنامه کامل بر اساس شناسه پس از استعلام</td><td><code>/car/third-party-car-policies/{policyId}</code></td></tr>
|
||||||
|
<tr><td>جستجوی وسیله نقلیه بر اساس VIN</td><td><code>/car/vehicles/inquiry-by-vin?vin=…</code></td></tr>
|
||||||
|
<tr><td>فهرست بیمهنامهها برای یک کد ملی</td><td><code>/common/Policies/inquiry-my-policies</code></td></tr>
|
||||||
|
<tr><td>دریافت رکورد مشتری بر اساس شناسه</td><td><code>/common/customers/{customerId}</code></td></tr>
|
||||||
|
<tr><td>جستجوی طرف بر اساس کد ملی + تاریخ تولد</td><td><code>/common/parties/inquiry-by-unique-identifier</code></td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-blue">
|
||||||
|
<h3>مدیریت خطا و انعطافپذیری</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>توضیح</th><th>مکانیزم</th></tr>
|
||||||
|
<tr><td>۳ تلاش، ۵۰۰ ms ← ۱۰۰۰ ms backoff نمایی در تمام فراخوانیهای HTTP.</td><td>Retry</td></tr>
|
||||||
|
<tr><td>وقتی فناوران پیام فارسی "دوباره تلاش کنید" (یا tracking-code 500) برمیگرداند، یک مکث ۵ دقیقهای در سطح تنانت فعال میشود. تمام فراخوانیها در این پنجره بلافاصله <code>503 ServiceUnavailable</code> دریافت میکنند — بدون فشار.</td><td>Backoff گذرا</td></tr>
|
||||||
|
<tr><td>در ۴۰۱، توکن از حافظه و MongoDB پاک میشود؛ فراخوانی بعدی GetAppToken + Login تازه را فعال میکند.</td><td>ابطال توکن</td></tr>
|
||||||
|
<tr><td>درخواستهای همزمان ورود برای همان تنانت به یک Promise در حال پرواز جمع میشوند.</td><td>حذف تکراری Inflight</td></tr>
|
||||||
|
<tr><td>هر مرحله (GET_APP_TOKEN، LOGIN و هر چهار مرحله ارسال) در <code>fanavaran_audit_logs</code> با وضعیت STARTED / SUCCESS / FAILURE، هدرهای کامل، بدنه و مدت زمان نوشته میشود.</td><td>لاگ Audit</td></tr>
|
||||||
|
<tr><td>۲۰–۳۰ ثانیه به ازای هر فراخوانی HTTP.</td><td>Timeout</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-blue">
|
||||||
|
<h3>پروفایلهای تنانت (<code>FANAVARAN_CLIENT</code>)</h3>
|
||||||
|
<p class="section-intro" style="margin-top:6px; margin-bottom:8px;">سه پروفایل تنانت از پیش تعیینشده وجود دارد. پروفایل فعال توسط متغیر محیطی <code>FANAVARAN_CLIENT</code> انتخاب میشود. هر پروفایل <code>appName</code>، <code>secret</code>، <code>username</code>، <code>password</code>، <code>CorpId</code>، <code>ContractId</code> و <code>Location</code> هدرهای خود را به علاوه پیشفرضهای payload (AccidentCityId و غیره) دارد.</p>
|
||||||
|
<table>
|
||||||
|
<tr><th>شرکت بیمه</th><th>کلید</th></tr>
|
||||||
|
<tr><td>بیمه پارسیان</td><td><code>parsian</code></td></tr>
|
||||||
|
<tr><td>بیمه تجارت نو</td><td><code>tejaratno</code></td></tr>
|
||||||
|
<tr><td>بیمه معلم</td><td><code>moallem</code></td></tr>
|
||||||
|
</table>
|
||||||
|
<p class="note" style="margin-top:8px;">
|
||||||
|
<code>INSURANCE_CORP_ID</code> یک رشته عنوان نمایشی است (مثلاً <em>"بیمه پارسیان"</em>) که در برابر فهرست زنده فناوران <code>insurance-corp</code> تطبیق داده میشود تا <code>corpId</code> عددی مورد استفاده در ارسالها را تولید کند. شناسه تطبیقیافته روی دیسک کش میشود.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||||
|
<h2 id="sanhub">۳ — SandHub <span class="status-badge status-partial">قدیمی</span></h2>
|
||||||
|
<p class="section-intro">
|
||||||
|
SandHub درگاه پرسوجوی اصلی است. هنوز در کدبیس حضور دارد اما تمام جریانهای
|
||||||
|
فعال تقصیر (V2+) به ارائهدهنده پرسوجوی تجارت منتقل شدهاند. اندپوینتهای
|
||||||
|
SandHub قابل فراخوانی هستند اما فقط از طریق مسیرهای قدیمی کد قابل دسترسی هستند.
|
||||||
|
حالت mock آن توسط همان تنظیم سیستم <code>sandHubUseLiveApi</code> کنترل میشود.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="card card-gray">
|
||||||
|
<h3>احراز هویت</h3>
|
||||||
|
<p class="note">
|
||||||
|
<code>POST {SANHUB_BASE_URL}/user/login</code> با بدنه JSON نام کاربری + رمز عبور.
|
||||||
|
توکن در حافظه برای <strong>۵۵ دقیقه</strong> کش میشود. در ۴۰۱، توکن پاک میشود و یک تلاش مجدد انجام میشود.
|
||||||
|
۳ تلاش با ۱۰۰۰ ms ← ۲۰۰۰ ms backoff نمایی.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-gray">
|
||||||
|
<h3>اندپوینتها</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th style="width:70px">متد</th><th>مسیر</th><th>توضیح</th></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>/block-inquiry-tejarat</code></td><td>پرسوجوی بیمهنامه مبتنی بر پلاک (THIRD_PARTY). بدنه: <code>leftTwoDigits</code>، <code>serialLetter</code>، <code>threeDigits</code>، <code>rightTwoDigits</code>، <code>nationalCode</code>.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>/block-inquiry-tejarat/badane</code></td><td>پرسوجوی بیمهنامه CAR_BODY. Timeout ۵۰ ثانیه (طولانیتر از استاندارد).</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>/personal-inquiry/tejarat-no</code></td><td>بررسی هویت شخصی. بدنه: <code>nationalCode</code> + <code>birthDate</code> <em>میلادی</em> (داخلی از جلالی تبدیل میشود).</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>/driver-license-check</code></td><td>اعتبارسنجی گواهینامه. پرچم <code>IsSucceed</code> را برمیگرداند.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>/ownership</code></td><td>بررسی مالکیت وسیله نقلیه. پرچم <code>IsSuccess</code> را برمیگرداند.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>/sheba/sheba-tejaratno</code></td><td>اعتبارسنجی شبا / حساب بانکی. <code>ReturnValue</code> + <code>HasError</code> را برمیگرداند.</td></tr>
|
||||||
|
</table>
|
||||||
|
<p class="note" style="margin-top:8px;">
|
||||||
|
تمام اندپوینتها پاسخهای mock کامل را زمانی که <code>sandHubUseLiveApi=false</code> در تنظیمات سیستم (پیشفرض) پشتیبانی میکنند. دادههای mock قطعی هستند و بهصورت محلی بدون هیچ فراخوانی HTTP تولید میشوند.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||||
|
<h2 id="tejarat">۴ — پرسوجوی تجارت <span class="status-badge status-live">فعال</span></h2>
|
||||||
|
<p class="section-intro">
|
||||||
|
درگاه فعال block-inquiry برای تمام تنانتهای غیر ESG. در هر فراخوانی V2+
|
||||||
|
<code>run-inquiries</code> که <code>CLIENT_ID ≠ 8</code> استفاده میشود.
|
||||||
|
URL پایه قابل پیکربندی است؛ در تولید به همان هاست SandHub اشاره میکند اما از
|
||||||
|
اعتبارنامههای جداگانه استفاده میکند.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="card card-teal">
|
||||||
|
<h3>احراز هویت</h3>
|
||||||
|
<p class="note">
|
||||||
|
<code>POST {TEJARAT_INQUIRY_BASE_URL}/user/login</code> با بدنه JSON ایمیل + رمز عبور.
|
||||||
|
توکن برای <strong>۵۵ دقیقه</strong> کش میشود. ۲ تلاش با ۵۰۰ ms ← ۱۰۰۰ ms backoff.
|
||||||
|
جدا از اعتبارنامههای SandHub — از <code>TEJARAT_INQUIRY_EMAIL</code> / <code>TEJARAT_INQUIRY_PASSWORD</code> استفاده میکند.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-teal">
|
||||||
|
<h3>اندپوینتها</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th style="width:70px">متد</th><th>مسیر</th><th>توضیح</th></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>/block-inquiry-tejarat</code></td><td>پرسوجوی پلاک THIRD_PARTY. بدنه: فیلدهای پلاک + <code>nationalCode</code>. ابتدا داده آفلاین بررسی میشود.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>/block-inquiry-tejarat/badane</code></td><td>پرسوجوی پلاک CAR_BODY. بدنه: <code>part1–part4</code> (عددی) + <code>nationalCode</code>. همیشه زنده میشود (mock برای مسیر badane وجود ندارد).</td></tr>
|
||||||
|
</table>
|
||||||
|
<p class="note" style="margin-top:8px;">
|
||||||
|
وقتی <code>sandHubUseLiveApi=false</code>، مسیر THIRD_PARTY یک پاسخ mock بدون HTTP برمیگرداند. مسیر CAR_BODY همیشه API زنده را صرفنظر از این پرچم فراخوانی میکند.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||||
|
<h2 id="esg">۵ — ESG <span class="status-badge status-live">فعال (CLIENT_ID=8)</span></h2>
|
||||||
|
<p class="section-intro">
|
||||||
|
ESG یک درگاه API بیمه داخلی است که منحصراً توسط تنانت پارسیان
|
||||||
|
(<code>CLIENT_ID=8</code>) استفاده میشود. برای تمام انواع پرسوجو زمانی که
|
||||||
|
این تنانت فعال است، جایگزین تجارت/SandHub میشود. شکل پاسخ متفاوتی دارد،
|
||||||
|
TTL توکن پویا دارد و تاریخ تولد را در فرمت <strong>جلالی</strong> انتظار دارد
|
||||||
|
(نه میلادی، برخلاف SandHub/تجارت).
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="card card-purple">
|
||||||
|
<h3>احراز هویت</h3>
|
||||||
|
<p class="note">
|
||||||
|
<code>POST {ESG_URL}/auth/login</code> با بدنه JSON <code>{ username, password }</code>.
|
||||||
|
TTL توکن از فیلد <code>expiresIn</code> پاسخ خوانده میشود (پیشفرض ۱۴ دقیقه).
|
||||||
|
۲ تلاش با ۵۰۰ ms ← ۱۰۰۰ ms backoff. در ۴۰۱، توکن پاک و یک تلاش مجدد.
|
||||||
|
URL پیشفرض: <code>http://192.168.20.22:8085</code> (شبکه داخلی).
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-purple">
|
||||||
|
<h3>اندپوینتها</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th style="width:70px">متد</th><th>مسیر</th><th>توضیح</th></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>/inquiry/policyByPlate</code></td><td>جستجوی بیمهنامه مبتنی بر پلاک (THIRD_PARTY). بدنه: <code>nationalCode</code>، <code>plk1–plk4</code>. پاسخ قبل از ذخیره به فرمت قدیمی تجارت نگاشت میشود.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>/inquiry/policyByChassis</code></td><td>جایگزین مبتنی بر VIN/شاسی برای پرسوجوی پلاک. توسط اندپوینتهای <code>run-inquiries-vin</code> فراخوانی میشود. از جستجوی شاسی ESG استفاده میکند (نه مسیر SandHub). بدنه: <code>nationalCode</code>، <code>chassis</code>.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>/inquiry/person</code></td><td>بررسی هویت شخصی. بدنه: <code>nationalCode</code>، <code>birthDate</code> (جلالی، نه میلادی).</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>/inquiry/sheba</code></td><td>اعتبارسنجی شبا / حساب بانکی.</td></tr>
|
||||||
|
</table>
|
||||||
|
<p class="note" style="margin-top:8px;">
|
||||||
|
ESG هر پاسخ را به صورت <code>{ success: boolean, data: … }</code> میپیچد. یک بدنه <code>success=false</code> به یک خطای فارسی "استعلام در دسترس نیست" ترجمه میشود.
|
||||||
|
بررسی داده آفلاین-پرسوجو هنوز ابتدا اجرا میشود، قبل از هر فراخوانی HTTP ESG.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||||
|
<h2 id="sms">۶ — پیامک <span class="status-badge status-live">فعال</span></h2>
|
||||||
|
<p class="section-intro">
|
||||||
|
دو ارائهدهنده پیامک پشتیبانی میشوند: <strong>کاوهنگار</strong> (پیشفرض)
|
||||||
|
و <strong>درگاه پیامک پارسیان</strong>. ارائهدهنده فعال توسط متغیر محیطی
|
||||||
|
<code>SMS_PROVIDER</code> (یا <code>SMS</code>) انتخاب میشود. هر دو ارائهدهنده
|
||||||
|
رابط درگاه داخلی یکسانی را پیادهسازی میکنند بنابراین لایه ارکستراسیون
|
||||||
|
مستقل از ارائهدهنده است.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="card card-green">
|
||||||
|
<h3>انتخاب ارائهدهنده</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>ارائهدهنده فعال</th><th>مقدار</th><th>متغیر محیطی</th></tr>
|
||||||
|
<tr><td>کاوهنگار — <code>api.kavenegar.com</code></td><td><code>kavenegar</code> (پیشفرض)</td><td><code>SMS_PROVIDER</code> (یا <code>SMS</code>)</td></tr>
|
||||||
|
<tr><td>درگاه پیامک پارسیان — <code>PARSIAN_SMS_URL</code></td><td><code>parsian</code></td><td><code>SMS_PROVIDER</code> (یا <code>SMS</code>)</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-green">
|
||||||
|
<h3>اندپوینتهای کاوهنگار</h3>
|
||||||
|
<p class="note">URL پایه: <code>https://api.kavenegar.com/v1/{SMS_API_KEY}/</code></p>
|
||||||
|
<table>
|
||||||
|
<tr><th style="width:70px">متد</th><th>مسیر</th><th>زمان استفاده</th></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>sms/send.json</code></td><td>پیامهای متن ساده (مثلاً متنهای اطلاعرسانی مبتنی بر کلید ذخیرهشده در مجموعه <code>sms_texts</code>).</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>verify/lookup.json</code></td><td>تمام پیامهای مبتنی بر قالب (OTPها، لینکهای دعوت، اطلاعرسانی کارشناس). پارامترها: <code>receptor</code>، <code>token</code>[، <code>token2</code>، <code>token3</code>، <code>token10</code>]، <code>template</code>.</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-green">
|
||||||
|
<h3>درگاه پیامک پارسیان</h3>
|
||||||
|
<p class="note">URL پایه از <code>PARSIAN_SMS_URL</code>. احراز هویت: هدر <code>X-PACKAGE-API-KEY</code> + <code>Authorization: Basic {PARSIAN_BASIC_TOKEN}</code>. بهصورت GET با پارامترهای URL-encoded <code>ReceiverNumbers</code> و <code>Message</code> ارسال میکند. پیامهای قالب قبل از ارسال به یک بدنه متن ساده پیشرندر میشوند (معادل verify/lookup ندارد).</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-green">
|
||||||
|
<h3>قالبهای پیامک در حال استفاده</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>توکنها</th><th>ماشه</th><th>نام قالب</th></tr>
|
||||||
|
<tr><td><code>token</code> = کد OTP</td><td>ورود OTP کاربر / اکتور، فراموشی رمز، OTPهای طرف</td><td><code>AUTH_SMS_TEMPLATE</code> (محیطی)</td></tr>
|
||||||
|
<tr><td><code>token</code> = publicId، <code>token2</code> = لینک</td><td>طرف دوم لینک دعوت تقصیر را از طریق پیامک دریافت میکند</td><td><code>yara724-invite-link</code></td></tr>
|
||||||
|
<tr><td><code>token</code> = نوع فایل، <code>token2</code> = نام خانوادگی کارشناس، <code>token3</code> = لینک</td><td>کارشناس میدانی لینک را برای یک طرف ارسال میکند</td><td><code>yara-field-expert-link</code></td></tr>
|
||||||
|
<tr><td><code>token</code> = publicId، <code>token2</code> = لینک</td><td>اطلاع به طرف که طرف دیگر با رأی کارشناس موافقت کرده است</td><td><code>yara-blame-agreement</code></td></tr>
|
||||||
|
<tr><td><code>token</code> = publicId، <code>token2</code> = لینک</td><td>طرف زیاندیده مطلع میشود که جریان خسارت را پس از تکمیل تقصیر باز کند</td><td><code>yara-claim-link</code></td></tr>
|
||||||
|
<tr><td><code>token</code> = "تصادف"/"خسارت"، <code>token2</code> = publicId، <code>token3</code> = نام خانوادگی کارشناس</td><td>کارشناس یک فایل تقصیر یا خسارت را قفل میکند</td><td><code>yara-expert-lock</code></td></tr>
|
||||||
|
<tr><td><code>token</code> = نوع فایل، <code>token2</code> = publicId، <code>token3</code> = لینک</td><td>کارشناس درخواست ارسال مجدد اسناد میدهد</td><td><code>yara-resend-documents</code></td></tr>
|
||||||
|
<tr><td><code>token</code> = نوع فایل، <code>token2</code> = publicId، <code>token3</code> = نام خانوادگی کارشناس، <code>token10</code> = لینک</td><td>طرف مطلع میشود که ارزیابی خسارت کارشناس را امضا کند</td><td><code>yara-signature</code></td></tr>
|
||||||
|
<tr><td><code>token</code> = publicId، <code>token2</code> = claimId فناوران، <code>token3</code> = claimNo فناوران</td><td>ارسال فناوران تأیید شد — با شماره و شناسه خسارت فناوران برای مالک خسارت ارسال میشود</td><td><code>yara-fanavaran-claim</code></td></tr>
|
||||||
|
</table>
|
||||||
|
<p class="note" style="margin-top:8px;">
|
||||||
|
تمام فراخوانیهای پیامک fire-and-forget هستند — هرگز throw نمیکنند. شکستها log میشوند اما جریان اصلی را مسدود نمیکنند.
|
||||||
|
یک مجموعه MongoDB <code>sms_send_logs</code> هر پیام خروجی را با نوع آن (OTP در مقابل TEMPLATE)، ارائهدهنده، نام قالب و وضعیت موفقیت/شکست ثبت میکند.
|
||||||
|
پیامهای متنی اطلاعرسانی (اختلاف طرفین، امضای یک طرف و غیره) در راهاندازی در مجموعه <code>sms_texts</code> seed میشوند و در زمان اجرا قابل ویرایش هستند.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||||
|
<h2 id="ai">۷ — سرویس هوش مصنوعی <span class="status-badge status-disabled">غیرفعال (کد موجود)</span></h2>
|
||||||
|
<p class="section-intro">
|
||||||
|
یک سرویس تشخیص خسارت خودرو مبتنی بر تصویر در کدبیس یکپارچهسازی شده است اما
|
||||||
|
فراخوانیهای HTTP آن <strong>کاملاً comment شدهاند</strong>. ماژول در راهاندازی
|
||||||
|
مقداردهی اولیه میشود، تلاش برای ورود میکند (در صورت شکست به صورت خاموش
|
||||||
|
بلعیده میشود)، و یک متد <code>aiRequestImage</code> را expose میکند — اما
|
||||||
|
فراخوانیهای axios زیرین غیرفعال هستند. سرویس هیچ جریان تولیدی را تحت تأثیر
|
||||||
|
قرار نمیدهد.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="card card-yellow">
|
||||||
|
<h3>رابط مورد نظر (زمانی که دوباره فعال شود)</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th style="width:70px">متد</th><th>مسیر</th><th>توضیح</th></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>{AI_URL_V2}/auth/login</code></td><td>احراز هویت با نام کاربری + رمز عبور. <code>accessToken</code> را برمیگرداند.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>{AI_URL_V2}/auth/profile</code></td><td>دریافت <code>apiKey.key</code> مورد نیاز بهعنوان هدر درخواست <code>gateway-api-key</code>.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>{AI_URL_V2}/services/car-damage/detector?version=ai-v7</code></td><td>ارسال تصویر قطعه خودرو (multipart). <code>downloadLink</code> با نتیجه حاشیهنویسیشده را برمیگرداند.</td></tr>
|
||||||
|
</table>
|
||||||
|
<p class="warn" style="margin-top:8px;">
|
||||||
|
وضعیت: هر سه فراخوانی در بلوکهای <code>axios.request(…)</code> comment-شده پیچیده شدهاند.
|
||||||
|
<code>CW_URL</code> در <code>.env.example</code> نیست. برای فعالسازی مجدد، فراخوانیهای axios login، getApiKey و aiRequestImage را uncomment کنید و <code>AI_URL_V2</code>، <code>AI_USERNAME</code>، <code>AI_PASSWORD</code> را پیکربندی کنید.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||||
|
<h2 id="car-pricing">۸ — سرویس قیمت خودرو <span class="status-badge status-partial">نیمهفعال</span></h2>
|
||||||
|
<p class="section-intro">
|
||||||
|
فقط در طول محاسبه کاهش قیمت کارشناس-خسارت استفاده میشود. وقتی یک کارشناس
|
||||||
|
مقادیر شدت برای هر قطعه ارائه میدهد، سیستم قیمتهای بازار بلادرنگ برای مدل
|
||||||
|
خودروی آسیبدیده را دریافت میکند، سپس کاهش قیمت را با استفاده از فرمول
|
||||||
|
محاسبه میکند: <strong>قیمت خودرو × ضریب سال × مجموع ضرایب قطعات ÷ ۴۰۰</strong>.
|
||||||
|
سرویس دو منبع داده (اندپوینت) دارد که بهصورت موازی امتحان میشوند.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="card card-orange">
|
||||||
|
<h3>اندپوینتها</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th style="width:70px">متد</th><th>مسیر</th><th>توضیح</th></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>{CW_URL}price?akharin</code></td><td>دریافت قیمتهای بازار خودرو از منبع "آخرین". آرایه <code>{ carName, marketPrice }</code> را برمیگرداند.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>{CW_URL}price?hamrah</code></td><td>دریافت قیمتهای بازار خودرو از منبع "همراه". همان شکل پاسخ.</td></tr>
|
||||||
|
</table>
|
||||||
|
<p class="note" style="margin-top:8px;">
|
||||||
|
هر دو اندپوینت امتحان میشوند؛ نتایج ادغام و حذف تکراری میشوند. بهترین تطابق برای
|
||||||
|
نام خودروی آسیبدیده با استفاده از <strong>فاصله Levenshtein</strong> (تطابق رشته فازی) پیدا میشود.
|
||||||
|
اگر هر دو اندپوینت شکست بخورند یا خالی برگردانند، محاسبه کاهش قیمت رد میشود (ناقص علامتگذاری میشود) — ارسال خسارت را مسدود نمیکند.
|
||||||
|
</p>
|
||||||
|
<p class="warn" style="margin-top:6px;">
|
||||||
|
<strong><code>CW_URL</code> در <code>.env.example</code> مستندسازی نشده است.</strong>
|
||||||
|
این سرویس در صورت تنظیم نشدن متغیر، بهصورت خاموش هیچ کاهش قیمتی تولید نخواهد کرد.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||||
|
<h2 id="offline-inquiry">۹ — پرسوجوی آفلاین <span class="status-badge status-internal">داخلی / fallback</span></h2>
|
||||||
|
<p class="section-intro">
|
||||||
|
لایه پرسوجوی آفلاین فراخوانیهای پرسوجوی مبتنی بر پلاک را قبل از اینکه هر
|
||||||
|
HTTP خارجی انجام شود رهگیری میکند. عمدتاً برای توسعه و تست (پلاکهای شناختهشده
|
||||||
|
از پیش seed شده) استفاده میشود اما همچنین بهعنوان fallback انعطافپذیری
|
||||||
|
زمانی که سرویسهای پرسوجوی زنده در دسترس نیستند عمل میکند. توسط یک پرچم
|
||||||
|
پایگاهداده زمان اجرا کنترل میشود، نه یک متغیر محیطی.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="card card-gray">
|
||||||
|
<h3>نحوه کار</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>جزئیات</th><th>جنبه</th></tr>
|
||||||
|
<tr><td>مجموعه MongoDB <code>offline-inquiries</code>. اسناد شامل <code>clientKey</code>، فیلدهای نرمالشده پلاک، <code>nationalCode</code> و پاسخ از پیش ساختهشده <code>raw</code> + <code>mapped</code> برای برگرداندن هستند.</td><td>ذخیرهسازی</td></tr>
|
||||||
|
<tr><td><code>system_settings.offlineInquiry.enabled</code> — پیشفرض <code>true</code>. تغییر از طریق <code>PATCH /super-admin/system-settings/offline-inquiry</code>.</td><td>سوئیچ اصلی</td></tr>
|
||||||
|
<tr><td>پلاک نرمالشده (فقط ارقام، عربی→فارسی) + کد ملی + کلید مشتری فناوران باید همه مطابقت داشته باشند. اگر پیدا شد، بلافاصله برگردانده میشود؛ هیچ فراخوانی HTTP انجام نمیشود.</td><td>ترتیب جستجو</td></tr>
|
||||||
|
<tr><td>فقط برای پرسوجوی block مبتنی بر پلاک (THIRD_PARTY) اعمال میشود. پرسوجوی CAR_BODY (<code>/badane</code>) همیشه API زنده را میزند.</td><td>محدوده</td></tr>
|
||||||
|
<tr><td><code>system_settings.externalApis.sandHubUseLiveApi</code> — وقتی <code>false</code> (پیشفرض)، حتی اگر هیچ داده آفلاینی مطابقت نداشته باشد، یک پاسخ mock داخلی برگردانده میشود به جای فراخوانی تجارت/ESG.</td><td>پرچم API زنده</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||||
|
<h2 id="env-ref">۱۰ — مرجع متغیرهای محیطی</h2>
|
||||||
|
<p class="section-intro">
|
||||||
|
تمام متغیرهای محیطی در سراسر تمام یکپارچهسازیها، گروهبندیشده بر اساس سرویس.
|
||||||
|
متغیرهای علامتگذاریشده با <strong>*</strong> در <code>.env.example</code> وجود ندارند.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="card card-blue">
|
||||||
|
<h3>فناوران</h3>
|
||||||
|
<table class="env-table">
|
||||||
|
<tr><th>شرح</th><th>متغیر</th></tr>
|
||||||
|
<tr><td>کلید پروفایل تنانت فعال: <code>parsian</code> | <code>tejaratno</code> | <code>moallem</code></td><td><code>FANAVARAN_CLIENT</code></td></tr>
|
||||||
|
<tr><td>عنوان نمایشی شرکت بیمهگر (مثلاً <em>"بیمه پارسیان"</em>) — در راهاندازی در برابر فهرست insurance-corp فناوران به یک corpId عددی تطبیق داده میشود.</td><td><code>INSURANCE_CORP_ID</code></td></tr>
|
||||||
|
</table>
|
||||||
|
<p class="note" style="margin-top:8px;">اعتبارنامههای هر تنانت (appName، secret، username، password، CorpId، ContractId، Location) در <code>src/core/config/fanavaran-client.config.ts</code> زیر <code>SEED_FANAVARAN_CLIENT_PROFILES</code> hardcoded شدهاند.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-gray">
|
||||||
|
<h3>SandHub (قدیمی)</h3>
|
||||||
|
<table class="env-table">
|
||||||
|
<tr><th>شرح</th><th>متغیر</th></tr>
|
||||||
|
<tr><td>URL پایه برای SandHub. پیشفرض: <code>http://82.99.202.245:3027</code></td><td><code>SANHUB_BASE_URL</code></td></tr>
|
||||||
|
<tr><td>URL کامل ورود (معمولاً base + <code>/user/login</code>)</td><td><code>SANHUB_URL_LOGIN</code></td></tr>
|
||||||
|
<tr><td>ایمیل ورود SandHub</td><td><code>SANHUB_USERNAME</code></td></tr>
|
||||||
|
<tr><td>رمز عبور ورود SandHub</td><td><code>SANHUB_PASSWORD</code></td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-teal">
|
||||||
|
<h3>پرسوجوی تجارت</h3>
|
||||||
|
<table class="env-table">
|
||||||
|
<tr><th>شرح</th><th>متغیر</th></tr>
|
||||||
|
<tr><td>URL پایه. پیشفرض: <code>http://82.99.202.245:3027</code></td><td><code>TEJARAT_INQUIRY_BASE_URL</code></td></tr>
|
||||||
|
<tr><td>ایمیل ورود</td><td><code>TEJARAT_INQUIRY_EMAIL</code></td></tr>
|
||||||
|
<tr><td>رمز عبور ورود</td><td><code>TEJARAT_INQUIRY_PASSWORD</code></td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-purple">
|
||||||
|
<h3>ESG (فقط CLIENT_ID=8)</h3>
|
||||||
|
<table class="env-table">
|
||||||
|
<tr><th>شرح</th><th>متغیر</th></tr>
|
||||||
|
<tr><td>به <code>8</code> تنظیم کنید تا ارائهدهنده پرسوجوی ESG برای تنانت پارسیان فعال شود.</td><td><code>CLIENT_ID</code></td></tr>
|
||||||
|
<tr><td>URL پایه ESG. پیشفرض: <code>http://192.168.20.22:8085</code> (شبکه داخلی)</td><td><code>ESG_URL</code></td></tr>
|
||||||
|
<tr><td>نام کاربری ورود ESG</td><td><code>ESG_USERNAME</code></td></tr>
|
||||||
|
<tr><td>رمز عبور ورود ESG</td><td><code>ESG_PASSWORD</code></td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-green">
|
||||||
|
<h3>پیامک</h3>
|
||||||
|
<table class="env-table">
|
||||||
|
<tr><th>شرح</th><th>متغیر</th></tr>
|
||||||
|
<tr><td><code>kavenegar</code> (پیشفرض) یا <code>parsian</code></td><td><code>SMS_PROVIDER</code> (یا <code>SMS</code>)</td></tr>
|
||||||
|
<tr><td>کلید API کاوهنگار (الزامی وقتی provider = kavenegar)</td><td><code>SMS_API_KEY</code></td></tr>
|
||||||
|
<tr><td>نام قالب کاوهنگار برای پیامهای OTP (مثلاً <code>yara-otp</code>)</td><td><code>AUTH_SMS_TEMPLATE</code></td></tr>
|
||||||
|
<tr><td>URL پایه درگاه پیامک پارسیان (الزامی وقتی provider = parsian)</td><td><code>PARSIAN_SMS_URL</code></td></tr>
|
||||||
|
<tr><td>مقدار هدر پیامک پارسیان <code>X-PACKAGE-API-KEY</code></td><td><code>PARSIAN_API_KEY</code></td></tr>
|
||||||
|
<tr><td>اعتبارنامههای رمزگذاریشده Base64 برای هدر <code>Authorization: Basic …</code></td><td><code>PARSIAN_BASIC_TOKEN</code></td></tr>
|
||||||
|
<tr><td>URL پایه فرانتاند — برای ساخت تمام لینکهای دعوت + خسارت تعبیهشده در پیامهای پیامک استفاده میشود</td><td><code>URL</code></td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-yellow">
|
||||||
|
<h3>سرویس هوش مصنوعی</h3>
|
||||||
|
<table class="env-table">
|
||||||
|
<tr><th>شرح</th><th>متغیر</th></tr>
|
||||||
|
<tr><td>URL پایه درگاه هوش مصنوعی. پیشفرض: <code>https://ai-gw.ittalie.ir</code> (استفاده نشده — سرویس غیرفعال است)</td><td><code>AI_URL_V2</code></td></tr>
|
||||||
|
<tr><td>نام کاربری ورود سرویس هوش مصنوعی (استفاده نشده)</td><td><code>AI_USERNAME</code></td></tr>
|
||||||
|
<tr><td>رمز عبور ورود سرویس هوش مصنوعی (استفاده نشده)</td><td><code>AI_PASSWORD</code></td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-orange">
|
||||||
|
<h3>سرویس قیمت خودرو</h3>
|
||||||
|
<table class="env-table">
|
||||||
|
<tr><th>شرح</th><th>متغیر</th></tr>
|
||||||
|
<tr><td>URL پایه برای API قیمت بازار خودرو (مثلاً <code>https://…/</code>). در <code>.env.example</code> نیست. کاهش قیمت بهصورت خاموش رد میشود اگر تنظیم نشده باشد.</td><td><code>CW_URL</code> *</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-gray">
|
||||||
|
<h3>عمومی / برنامه</h3>
|
||||||
|
<table class="env-table">
|
||||||
|
<tr><th>شرح</th><th>متغیر</th></tr>
|
||||||
|
<tr><td>پورت HTTP (پیشفرض ۳۰۰۰). توسط fallback insurance-corp فناوران برای فراخوانی اندپوینت جستجوی محلی خودش استفاده میشود.</td><td><code>PORT</code></td></tr>
|
||||||
|
<tr><td><code>true</code> / <code>false</code> — چالش کپچای ورود را فعال/غیرفعال میکند. داخلی، بدون سرویس خارجی.</td><td><code>CAPTCHA_ENABLED</code></td></tr>
|
||||||
|
<tr><td>TTL چالش کپچا به دقیقه.</td><td><code>EXP_CAPTCHA_TIME</code></td></tr>
|
||||||
|
<tr><td>TTL کد یکبار مصرف به دقیقه.</td><td><code>EXP_OTP_TIME</code></td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer>Made by Sepehr</footer>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
593
docs/external-integrations-reference.html
Normal file
593
docs/external-integrations-reference.html
Normal file
@@ -0,0 +1,593 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>External Integrations Reference</title>
|
||||||
|
<style>
|
||||||
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, "Segoe UI", system-ui, sans-serif;
|
||||||
|
font-size: 14px; line-height: 1.6;
|
||||||
|
background: #ffffff; color: #1f2328; padding: 24px;
|
||||||
|
}
|
||||||
|
h1 { font-size: 20px; font-weight: 700; margin-bottom: 4px; }
|
||||||
|
.subtitle { font-size: 13px; color: #57606a; margin-bottom: 28px; }
|
||||||
|
h2 {
|
||||||
|
font-size: 15px; font-weight: 700;
|
||||||
|
margin-bottom: 10px; margin-top: 32px;
|
||||||
|
border-bottom: 1px solid #e5e7eb; padding-bottom: 6px;
|
||||||
|
}
|
||||||
|
h3 {
|
||||||
|
font-size: 12px; font-weight: 700;
|
||||||
|
text-transform: uppercase; letter-spacing: 0.05em;
|
||||||
|
color: #57606a; margin-bottom: 8px; margin-top: 14px;
|
||||||
|
}
|
||||||
|
.section-intro {
|
||||||
|
font-size: 13px; color: #57606a;
|
||||||
|
margin-bottom: 14px; line-height: 1.5;
|
||||||
|
}
|
||||||
|
.card {
|
||||||
|
border: 1px solid #e5e7eb; border-radius: 6px;
|
||||||
|
padding: 16px; background: #f7f8fa; margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
.card.card-blue { border-left: 4px solid #3b82f6; }
|
||||||
|
.card.card-green { border-left: 4px solid #22c55e; }
|
||||||
|
.card.card-purple { border-left: 4px solid #8b5cf6; }
|
||||||
|
.card.card-orange { border-left: 4px solid #f97316; }
|
||||||
|
.card.card-teal { border-left: 4px solid #14b8a6; }
|
||||||
|
.card.card-indigo { border-left: 4px solid #6366f1; }
|
||||||
|
.card.card-gray { border-left: 4px solid #94a3b8; }
|
||||||
|
.card.card-red { border-left: 4px solid #ef4444; }
|
||||||
|
.card.card-yellow { border-left: 4px solid #eab308; }
|
||||||
|
table {
|
||||||
|
border-collapse: collapse; width: 100%;
|
||||||
|
font-size: 12px; margin-top: 4px;
|
||||||
|
}
|
||||||
|
th {
|
||||||
|
background: #f1f5f9; font-weight: 600;
|
||||||
|
text-align: left; padding: 5px 8px; border: 1px solid #e5e7eb;
|
||||||
|
}
|
||||||
|
td { padding: 4px 8px; border: 1px solid #e5e7eb; vertical-align: top; }
|
||||||
|
tr:nth-child(even) td { background: #ffffff; }
|
||||||
|
code { font-family: monospace; font-size: 11px; color: #3b82d4; }
|
||||||
|
.method {
|
||||||
|
font-family: monospace; font-size: 11px;
|
||||||
|
font-weight: 700; white-space: nowrap;
|
||||||
|
}
|
||||||
|
.method.get { color: #059669; }
|
||||||
|
.method.post { color: #2563eb; }
|
||||||
|
.method.put { color: #d97706; }
|
||||||
|
.method.patch { color: #7c3aed; }
|
||||||
|
.note { font-size: 11px; color: #57606a; font-style: italic; margin-top: 6px; }
|
||||||
|
.warn { font-size: 11px; color: #9a3412; font-style: italic; margin-top: 6px; }
|
||||||
|
.status-badge {
|
||||||
|
display: inline-block; font-size: 11px; font-weight: 600;
|
||||||
|
padding: 1px 7px; border-radius: 10px;
|
||||||
|
}
|
||||||
|
.status-live { background: #dcfce7; color: #166534; }
|
||||||
|
.status-partial { background: #ffedd5; color: #9a3412; }
|
||||||
|
.status-disabled { background: #fee2e2; color: #991b1b; }
|
||||||
|
.status-internal { background: #f1f5f9; color: #475569; border: 1px solid #e2e8f0; }
|
||||||
|
.toc {
|
||||||
|
background: #f7f8fa; border: 1px solid #e5e7eb;
|
||||||
|
border-radius: 6px; padding: 14px 18px; margin-bottom: 28px;
|
||||||
|
}
|
||||||
|
.toc-title { font-size: 13px; font-weight: 700; margin-bottom: 8px; }
|
||||||
|
.toc ol { padding-left: 18px; }
|
||||||
|
.toc li { font-size: 13px; margin-bottom: 3px; }
|
||||||
|
.toc a { color: #3b82d4; text-decoration: none; }
|
||||||
|
.toc a:hover { text-decoration: underline; }
|
||||||
|
footer {
|
||||||
|
text-align: center; font-size: 12px; color: #57606a;
|
||||||
|
border-top: 1px solid #e5e7eb; margin-top: 40px; padding-top: 12px;
|
||||||
|
}
|
||||||
|
.max-wrap { max-width: 760px; margin: 0 auto; }
|
||||||
|
.flow-box {
|
||||||
|
background: #ffffff; border: 1px solid #e5e7eb; border-radius: 4px;
|
||||||
|
padding: 10px 14px; font-size: 12px; margin-top: 8px;
|
||||||
|
}
|
||||||
|
.flow-step {
|
||||||
|
display: flex; gap: 10px; align-items: flex-start; margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
.flow-num {
|
||||||
|
flex-shrink: 0; width: 20px; height: 20px; border-radius: 50%;
|
||||||
|
background: #3b82d4; color: #fff; font-size: 11px; font-weight: 700;
|
||||||
|
display: flex; align-items: center; justify-content: center;
|
||||||
|
}
|
||||||
|
.flow-text { flex: 1; padding-top: 2px; }
|
||||||
|
.decision-tree {
|
||||||
|
font-size: 12px; background: #ffffff;
|
||||||
|
border: 1px solid #e5e7eb; border-radius: 4px; padding: 12px 16px;
|
||||||
|
margin-top: 8px; line-height: 1.8;
|
||||||
|
}
|
||||||
|
.decision-tree ul { padding-left: 20px; }
|
||||||
|
.decision-tree li { margin-bottom: 2px; }
|
||||||
|
.env-table th:first-child { width: 220px; }
|
||||||
|
pre {
|
||||||
|
font-family: monospace; font-size: 11px;
|
||||||
|
background: #f1f5f9; border: 1px solid #e5e7eb;
|
||||||
|
border-radius: 4px; padding: 10px 12px;
|
||||||
|
white-space: pre-wrap; word-break: break-all;
|
||||||
|
margin-top: 6px; color: #1f2328;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="max-wrap">
|
||||||
|
<h1>External Integrations Reference</h1>
|
||||||
|
<p class="subtitle">
|
||||||
|
Every outbound integration: what it does, when it fires, how auth works,
|
||||||
|
retry behaviour, fallbacks, and all environment variables. Internal-only
|
||||||
|
services (captcha, offline inquiry seed) are included for completeness.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<!-- TOC -->
|
||||||
|
<div class="toc">
|
||||||
|
<div class="toc-title">Contents</div>
|
||||||
|
<ol>
|
||||||
|
<li><a href="#inquiry-routing">Inquiry routing decision tree</a></li>
|
||||||
|
<li><a href="#fanavaran">Fanavaran — insurance claims platform</a></li>
|
||||||
|
<li><a href="#sanhub">SandHub — legacy inquiry gateway</a></li>
|
||||||
|
<li><a href="#tejarat">Tejarat inquiry — block-inquiry gateway (V2+)</a></li>
|
||||||
|
<li><a href="#esg">ESG — Parsian-tenant inquiry provider</a></li>
|
||||||
|
<li><a href="#sms">SMS — Kavenegar and Parsian gateways</a></li>
|
||||||
|
<li><a href="#ai">AI service — car damage detection</a></li>
|
||||||
|
<li><a href="#car-pricing">Car pricing service — market value lookup</a></li>
|
||||||
|
<li><a href="#offline-inquiry">Offline inquiry — fallback seed data</a></li>
|
||||||
|
<li><a href="#env-ref">Environment variable reference</a></li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||||
|
<h2 id="inquiry-routing">1 — Inquiry Routing Decision Tree</h2>
|
||||||
|
<p class="section-intro">
|
||||||
|
Every blame file starts with a "run-inquiries" call that fetches the
|
||||||
|
guilty party's insurance policy from an external provider. Which provider
|
||||||
|
is actually called depends on three factors: the tenant (<code>CLIENT_ID</code>),
|
||||||
|
the file type (THIRD_PARTY vs CAR_BODY), and whether live API mode is
|
||||||
|
enabled in system settings. The offline-inquiry seed layer sits in front
|
||||||
|
of all three providers.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="card card-indigo">
|
||||||
|
<h3>Provider selection</h3>
|
||||||
|
<div class="decision-tree">
|
||||||
|
<strong>For every plate-based block inquiry:</strong>
|
||||||
|
<ul>
|
||||||
|
<li>1. Check offline-inquiry seeds (MongoDB) — if a matching seed exists, return it and skip all HTTP.</li>
|
||||||
|
<li>2. If <code>CLIENT_ID=8</code> (Parsian/ESG tenant) → route to <strong>ESG</strong> <code>/inquiry/policyByPlate</code> or <code>/inquiry/policyByChassis</code>.</li>
|
||||||
|
<li>3. Otherwise → route to <strong>Tejarat inquiry</strong> <code>/block-inquiry-tejarat</code> (THIRD_PARTY) or <code>/block-inquiry-tejarat/badane</code> (CAR_BODY).</li>
|
||||||
|
<li>4. If <code>system_settings.externalApis.sandHubUseLiveApi = false</code> (default) → return mock response instead of making HTTP calls.</li>
|
||||||
|
</ul>
|
||||||
|
<br>
|
||||||
|
<strong>For personal-identity, driving-licence, ownership, and Sheba checks:</strong>
|
||||||
|
<ul>
|
||||||
|
<li>If <code>CLIENT_ID=8</code> → ESG <code>/inquiry/person</code> and <code>/inquiry/sheba</code>.</li>
|
||||||
|
<li>Otherwise → Tejarat/SandHub <code>/personal-inquiry/tejarat-no</code>, <code>/driver-license-check</code>, <code>/ownership</code>, <code>/sheba/sheba-tejaratno</code>.</li>
|
||||||
|
</ul>
|
||||||
|
<br>
|
||||||
|
<strong>Key difference — birth date format:</strong>
|
||||||
|
SandHub/Tejarat expect a <em>Gregorian</em> birth date (converted internally from Jalali).
|
||||||
|
ESG expects the <em>Jalali</em> date directly.
|
||||||
|
</div>
|
||||||
|
<p class="note" style="margin-top:8px;">
|
||||||
|
SandHub endpoints are only used in legacy code paths. All active V2+ blame flows go through the Tejarat or ESG providers.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||||
|
<h2 id="fanavaran">2 — Fanavaran <span class="status-badge status-live">live</span></h2>
|
||||||
|
<p class="section-intro">
|
||||||
|
Fanavaran (<code>apimanager.iraneit.com</code>) is the national insurance
|
||||||
|
damage-case platform. After the damage expert submits their assessment,
|
||||||
|
the system auto-submits a structured claim to Fanavaran through a
|
||||||
|
four-step protocol. Fanavaran also serves as the lookup source for
|
||||||
|
code-lists (accident types, car components, city codes, etc.) used
|
||||||
|
across the platform.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="card card-blue">
|
||||||
|
<h3>Authentication lifecycle</h3>
|
||||||
|
<div class="flow-box">
|
||||||
|
<div class="flow-step"><div class="flow-num">1</div><div class="flow-text"><strong>GET AppToken</strong> — <code>POST /EITAuthentication/GetAppToken</code> with <code>appname</code> + <code>secret</code> headers. Returns <code>apptoken</code> header.</div></div>
|
||||||
|
<div class="flow-step"><div class="flow-num">2</div><div class="flow-text"><strong>Login</strong> — <code>POST /EITAuthentication/Login</code> with <code>appToken</code> + <code>userName</code> + <code>password</code> headers. Returns <code>authenticationToken</code> header.</div></div>
|
||||||
|
<div class="flow-step"><div class="flow-num">3</div><div class="flow-text"><strong>Cache</strong> — token is cached in memory <em>and</em> persisted to MongoDB (<code>fanavaran_auth_tokens</code>). Valid until midnight <strong>Asia/Tehran</strong> — the first call after 00:00 fetches a fresh token.</div></div>
|
||||||
|
<div class="flow-step"><div class="flow-num">4</div><div class="flow-text"><strong>All subsequent calls</strong> include four headers: <code>authenticationToken</code>, <code>CorpId</code>, <code>ContractId</code>, <code>Location</code> — tenant-specific, hardcoded per <code>FANAVARAN_CLIENT</code> key.</div></div>
|
||||||
|
</div>
|
||||||
|
<p class="note" style="margin-top:8px;">
|
||||||
|
A config fingerprint (hash of appName + secret + username + password + corpId + contractId + location)
|
||||||
|
forces a fresh login when any credential changes, even before midnight.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-blue">
|
||||||
|
<h3>Claim submission protocol (4 steps)</h3>
|
||||||
|
<div class="flow-box">
|
||||||
|
<div class="flow-step"><div class="flow-num">1</div><div class="flow-text"><strong>Base claim (GEN.03)</strong> — <code>POST /car/third-party-car-financial-claims</code>. Sends owner, driver, insurance, vehicle, and accident data. Returns a Fanavaran <code>claimId</code> and <code>claimNo</code>. SMS is sent to the owner with both identifiers.</div></div>
|
||||||
|
<div class="flow-step"><div class="flow-num">2</div><div class="flow-text"><strong>Damage cases (GEN.05)</strong> — <code>POST /car/third-party-car-financial-claims/{claimId}/dmg-cases</code>. One entry per damaged part with component ID, severity, and price. Cap: total ≤ 53 000 000 Toman.</div></div>
|
||||||
|
<div class="flow-step"><div class="flow-num">3</div><div class="flow-text"><strong>Attachments (GEN.07)</strong> — <code>POST /car/third-party-car-financial-claims/{claimId}/files</code>. Documents, car-capture images, and videos referenced by file ID.</div></div>
|
||||||
|
<div class="flow-step"><div class="flow-num">4</div><div class="flow-text"><strong>Expertise (GEN.08)</strong> — <code>POST /car/third-party-car-financial-claims/{claimId}/expertise</code>. Expert assessment metadata (expert role, date, result). Finalises the submission.</div></div>
|
||||||
|
</div>
|
||||||
|
<p class="note" style="margin-top:8px;">
|
||||||
|
All four steps are recorded in the <code>fanavaran_audit_logs</code> collection with full request/response bodies, HTTP status, duration, and tracking code for debugging.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-blue">
|
||||||
|
<h3>Lookup endpoints</h3>
|
||||||
|
<p class="note">All under <code>https://apimanager.iraneit.com/BimeApiManager/api/BimeApi/v2.0/</code>. Results are cached to disk (per client key) and in the <code>lookups</code> MongoDB collection. Parsian tenant reads DB before hitting the API; others go to the API first.</p>
|
||||||
|
<table>
|
||||||
|
<tr><th>Path</th><th>Used for</th></tr>
|
||||||
|
<tr><td><code>/car/base-info/accident-causes</code></td><td>accidentReason dropdown options (mapped to local IDs)</td></tr>
|
||||||
|
<tr><td><code>/car/code-list/accident-report-type</code></td><td>accidentWay options</td></tr>
|
||||||
|
<tr><td><code>/car/base-info/vehicle-use-types</code></td><td>vehicle usage classification</td></tr>
|
||||||
|
<tr><td><code>/car/code-list/dmg-pay-method</code></td><td>damage payment method</td></tr>
|
||||||
|
<tr><td><code>/car/base-info/driving-licence-types</code></td><td>licence type options</td></tr>
|
||||||
|
<tr><td><code>/car/code-list/accident-culprit-type</code></td><td>guilty-party classification</td></tr>
|
||||||
|
<tr><td><code>/car/code-list/inspection-place</code></td><td>inspection location options</td></tr>
|
||||||
|
<tr><td><code>/car/code-list/drop-amount-status</code></td><td>price-drop status codes</td></tr>
|
||||||
|
<tr><td><code>/car/base-info/car-components</code></td><td>component catalog (maps to outer/inner parts)</td></tr>
|
||||||
|
<tr><td><code>/car/code-list/accident-level</code></td><td>accident severity options</td></tr>
|
||||||
|
<tr><td><code>/common/code-list/insurance-corp</code></td><td>resolve <code>INSURANCE_CORP_ID</code> → Fanavaran corpId</td></tr>
|
||||||
|
<tr><td><code>/common/base-info/cities</code>, <code>/common/base-info/Provinces</code></td><td>city/province pickers</td></tr>
|
||||||
|
<tr><td><code>/car/third-party-car-policies/{policyId}</code></td><td>fetch full policy by ID after inquiry</td></tr>
|
||||||
|
<tr><td><code>/car/vehicles/inquiry-by-vin?vin=…</code></td><td>VIN-based vehicle lookup</td></tr>
|
||||||
|
<tr><td><code>/common/Policies/inquiry-my-policies</code></td><td>list policies for a national code</td></tr>
|
||||||
|
<tr><td><code>/common/customers/{customerId}</code></td><td>fetch customer record by ID</td></tr>
|
||||||
|
<tr><td><code>/common/parties/inquiry-by-unique-identifier</code></td><td>party lookup by national code + birth date</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-blue">
|
||||||
|
<h3>Error handling & resilience</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>Mechanism</th><th>Detail</th></tr>
|
||||||
|
<tr><td>Retry</td><td>3 attempts, 500 ms → 1 000 ms exponential backoff on all HTTP calls.</td></tr>
|
||||||
|
<tr><td>Transient backoff</td><td>When Fanavaran returns the Persian "try again later" message (or tracking-code 500), a tenant-wide 5-minute pause is activated. All calls during this window get <code>503 ServiceUnavailable</code> immediately — no hammering.</td></tr>
|
||||||
|
<tr><td>Token invalidation</td><td>On 401, token is cleared from memory and MongoDB; next call triggers a fresh GetAppToken + Login.</td></tr>
|
||||||
|
<tr><td>Inflight de-dup</td><td>Concurrent login requests for the same tenant are collapsed to a single in-flight Promise.</td></tr>
|
||||||
|
<tr><td>Audit log</td><td>Every step (GET_APP_TOKEN, LOGIN, and all four submission steps) is written to <code>fanavaran_audit_logs</code> with STARTED / SUCCESS / FAILURE status, full headers, body, and duration.</td></tr>
|
||||||
|
<tr><td>Timeout</td><td>20–30 s per HTTP call.</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-blue">
|
||||||
|
<h3>Tenant profiles (<code>FANAVARAN_CLIENT</code>)</h3>
|
||||||
|
<p class="section-intro" style="margin-top:6px; margin-bottom:8px;">Three pre-seeded tenant profiles exist. The active one is chosen by the <code>FANAVARAN_CLIENT</code> env var. Each profile carries its own <code>appName</code>, <code>secret</code>, <code>username</code>, <code>password</code>, <code>CorpId</code>, <code>ContractId</code>, and <code>Location</code> headers, plus payload defaults (AccidentCityId, etc.).</p>
|
||||||
|
<table>
|
||||||
|
<tr><th>Key</th><th>Insurance company</th></tr>
|
||||||
|
<tr><td><code>parsian</code></td><td>Parsian Insurance</td></tr>
|
||||||
|
<tr><td><code>tejaratno</code></td><td>Tejaratno Insurance</td></tr>
|
||||||
|
<tr><td><code>moallem</code></td><td>Moallem Insurance</td></tr>
|
||||||
|
</table>
|
||||||
|
<p class="note" style="margin-top:8px;">
|
||||||
|
<code>INSURANCE_CORP_ID</code> is a display-caption string (e.g. <em>"بیمه پارسیان"</em>) that is resolved against the live Fanavaran <code>insurance-corp</code> list to produce the numeric <code>corpId</code> used in submissions. The resolved ID is cached to disk.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||||
|
<h2 id="sanhub">3 — SandHub <span class="status-badge status-partial">legacy</span></h2>
|
||||||
|
<p class="section-intro">
|
||||||
|
SandHub is the original inquiry gateway. It is still present in the
|
||||||
|
codebase but all active blame flows (V2+) have been migrated to the
|
||||||
|
Tejarat inquiry provider. SandHub endpoints remain callable but are
|
||||||
|
only reached through legacy code paths. Its mock mode is controlled
|
||||||
|
by the same <code>sandHubUseLiveApi</code> system setting.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="card card-gray">
|
||||||
|
<h3>Auth</h3>
|
||||||
|
<p class="note">
|
||||||
|
<code>POST {SANHUB_BASE_URL}/user/login</code> with username + password JSON body.
|
||||||
|
Token cached in memory for <strong>55 minutes</strong>. On 401, token is cleared and one retry is made.
|
||||||
|
3 attempts with 1 000 ms → 2 000 ms exponential backoff.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-gray">
|
||||||
|
<h3>Endpoints</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th style="width:70px">Method</th><th>Path</th><th>What it does</th></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>/block-inquiry-tejarat</code></td><td>Plate-based insurance policy inquiry (THIRD_PARTY). Body: <code>leftTwoDigits</code>, <code>serialLetter</code>, <code>threeDigits</code>, <code>rightTwoDigits</code>, <code>nationalCode</code>.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>/block-inquiry-tejarat/badane</code></td><td>CAR_BODY policy inquiry. Timeout 50 s (longer than standard).</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>/personal-inquiry/tejarat-no</code></td><td>Personal identity check. Body: <code>nationalCode</code> + <em>Gregorian</em> <code>birthDate</code> (converted from Jalali internally).</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>/driver-license-check</code></td><td>Driving licence validation. Returns <code>IsSucceed</code> flag.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>/ownership</code></td><td>Vehicle ownership check. Returns <code>IsSuccess</code> flag.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>/sheba/sheba-tejaratno</code></td><td>Sheba / bank account validation. Returns <code>ReturnValue</code> + <code>HasError</code>.</td></tr>
|
||||||
|
</table>
|
||||||
|
<p class="note" style="margin-top:8px;">
|
||||||
|
All endpoints support full mock responses when <code>sandHubUseLiveApi=false</code> in system settings (default). Mock data is deterministic and produced locally without any HTTP calls.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||||
|
<h2 id="tejarat">4 — Tejarat Inquiry <span class="status-badge status-live">live</span></h2>
|
||||||
|
<p class="section-intro">
|
||||||
|
The active block-inquiry gateway for all non-ESG tenants. Used in every
|
||||||
|
V2+ <code>run-inquiries</code> call where <code>CLIENT_ID ≠ 8</code>.
|
||||||
|
The base URL is configurable; in production it points to the same host
|
||||||
|
as SandHub but uses separate credentials.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="card card-teal">
|
||||||
|
<h3>Auth</h3>
|
||||||
|
<p class="note">
|
||||||
|
<code>POST {TEJARAT_INQUIRY_BASE_URL}/user/login</code> with email + password JSON body.
|
||||||
|
Token cached for <strong>55 minutes</strong>. 2 attempts with 500 ms → 1 000 ms backoff.
|
||||||
|
Separate from SandHub credentials — uses <code>TEJARAT_INQUIRY_EMAIL</code> / <code>TEJARAT_INQUIRY_PASSWORD</code>.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-teal">
|
||||||
|
<h3>Endpoints</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th style="width:70px">Method</th><th>Path</th><th>What it does</th></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>/block-inquiry-tejarat</code></td><td>THIRD_PARTY plate inquiry. Body: plate fields + <code>nationalCode</code>. Offline seed checked first.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>/block-inquiry-tejarat/badane</code></td><td>CAR_BODY plate inquiry. Body: <code>part1–part4</code> (numeric) + <code>nationalCode</code>. Always goes live (no mock for badane path).</td></tr>
|
||||||
|
</table>
|
||||||
|
<p class="note" style="margin-top:8px;">
|
||||||
|
When <code>sandHubUseLiveApi=false</code>, the THIRD_PARTY path returns a mock response without HTTP. The CAR_BODY path always calls the live API regardless of this flag.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||||
|
<h2 id="esg">5 — ESG <span class="status-badge status-live">live (CLIENT_ID=8)</span></h2>
|
||||||
|
<p class="section-intro">
|
||||||
|
ESG is an internal insurance API gateway used exclusively by the Parsian
|
||||||
|
tenant (<code>CLIENT_ID=8</code>). It replaces Tejarat/SandHub for all
|
||||||
|
inquiry types when this tenant is active. It has a different response
|
||||||
|
shape, a dynamic token TTL, and expects birth dates in <strong>Jalali</strong>
|
||||||
|
format (not Gregorian, unlike SandHub/Tejarat).
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="card card-purple">
|
||||||
|
<h3>Auth</h3>
|
||||||
|
<p class="note">
|
||||||
|
<code>POST {ESG_URL}/auth/login</code> with <code>{ username, password }</code> JSON body.
|
||||||
|
Token TTL is read from the response <code>expiresIn</code> field (default 14 min).
|
||||||
|
2 attempts with 500 ms → 1 000 ms backoff. On 401, token cleared and one retry.
|
||||||
|
Default URL: <code>http://192.168.20.22:8085</code> (internal network).
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-purple">
|
||||||
|
<h3>Endpoints</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th style="width:70px">Method</th><th>Path</th><th>What it does</th></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>/inquiry/policyByPlate</code></td><td>Plate-based policy lookup (THIRD_PARTY). Body: <code>nationalCode</code>, <code>plk1–plk4</code>. Response is mapped to the old Tejarat format before being stored.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>/inquiry/policyByChassis</code></td><td>VIN/chassis-based alternative to plate inquiry. Called by <code>run-inquiries-vin</code> endpoints. Uses ESG chassis lookup (not the SandHub path). Body: <code>nationalCode</code>, <code>chassis</code>.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>/inquiry/person</code></td><td>Personal identity check. Body: <code>nationalCode</code>, <code>birthDate</code> (Jalali, NOT Gregorian).</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>/inquiry/sheba</code></td><td>Sheba / bank account validation.</td></tr>
|
||||||
|
</table>
|
||||||
|
<p class="note" style="margin-top:8px;">
|
||||||
|
ESG wraps every response as <code>{ success: boolean, data: … }</code>. A <code>success=false</code> body is translated to a Persian "استعلام در دسترس نیست" (inquiry unavailable) error.
|
||||||
|
The offline-inquiry seed check still runs first, before any ESG HTTP call.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||||
|
<h2 id="sms">6 — SMS <span class="status-badge status-live">live</span></h2>
|
||||||
|
<p class="section-intro">
|
||||||
|
Two SMS providers are supported: <strong>Kavenegar</strong> (default)
|
||||||
|
and <strong>Parsian SMS Gateway</strong>. The active provider is chosen
|
||||||
|
by the <code>SMS_PROVIDER</code> (or <code>SMS</code>) env var. Both
|
||||||
|
providers implement the same internal gateway interface so the
|
||||||
|
orchestration layer is provider-agnostic.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="card card-green">
|
||||||
|
<h3>Provider selection</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>Env var</th><th>Value</th><th>Active provider</th></tr>
|
||||||
|
<tr><td><code>SMS_PROVIDER</code> (or <code>SMS</code>)</td><td><code>kavenegar</code> (default)</td><td>Kavenegar — <code>api.kavenegar.com</code></td></tr>
|
||||||
|
<tr><td><code>SMS_PROVIDER</code> (or <code>SMS</code>)</td><td><code>parsian</code></td><td>Parsian SMS Gateway — <code>PARSIAN_SMS_URL</code></td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-green">
|
||||||
|
<h3>Kavenegar endpoints</h3>
|
||||||
|
<p class="note">Base URL: <code>https://api.kavenegar.com/v1/{SMS_API_KEY}/</code></p>
|
||||||
|
<table>
|
||||||
|
<tr><th style="width:70px">Method</th><th>Path</th><th>When used</th></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>sms/send.json</code></td><td>Plain-text messages (e.g. key-based notification texts stored in <code>sms_texts</code> collection).</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>verify/lookup.json</code></td><td>All template-based messages (OTPs, invite links, expert notifications). Params: <code>receptor</code>, <code>token</code>[, <code>token2</code>, <code>token3</code>, <code>token10</code>], <code>template</code>.</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-green">
|
||||||
|
<h3>Parsian SMS Gateway</h3>
|
||||||
|
<p class="note">Base URL from <code>PARSIAN_SMS_URL</code>. Auth: <code>X-PACKAGE-API-KEY</code> header + <code>Authorization: Basic {PARSIAN_BASIC_TOKEN}</code>. Sends as a GET with URL-encoded <code>ReceiverNumbers</code> and <code>Message</code> query params. Template messages are pre-rendered into a plain text body before sending (no verify/lookup equivalent).</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-green">
|
||||||
|
<h3>SMS templates in use</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>Template name</th><th>Trigger</th><th>Tokens</th></tr>
|
||||||
|
<tr><td><code>AUTH_SMS_TEMPLATE</code> (env)</td><td>User / actor OTP login, forget-password, party OTPs</td><td><code>token</code> = OTP code</td></tr>
|
||||||
|
<tr><td><code>yara724-invite-link</code></td><td>Second party receives blame invite link via SMS</td><td><code>token</code> = publicId, <code>token2</code> = link</td></tr>
|
||||||
|
<tr><td><code>yara-field-expert-link</code></td><td>Field expert sends link to a party</td><td><code>token</code> = file type, <code>token2</code> = expert surname, <code>token3</code> = link</td></tr>
|
||||||
|
<tr><td><code>yara-blame-agreement</code></td><td>Notify party that the other side agreed to the expert verdict</td><td><code>token</code> = publicId, <code>token2</code> = link</td></tr>
|
||||||
|
<tr><td><code>yara-claim-link</code></td><td>Damaged party notified to open claim flow after blame is complete</td><td><code>token</code> = publicId, <code>token2</code> = link</td></tr>
|
||||||
|
<tr><td><code>yara-expert-lock</code></td><td>Expert locks a blame or claim file</td><td><code>token</code> = "تصادف"/"خسارت", <code>token2</code> = publicId, <code>token3</code> = expert surname</td></tr>
|
||||||
|
<tr><td><code>yara-resend-documents</code></td><td>Expert requests document resend</td><td><code>token</code> = file kind, <code>token2</code> = publicId, <code>token3</code> = link</td></tr>
|
||||||
|
<tr><td><code>yara-signature</code></td><td>Party notified to sign the expert's damage assessment</td><td><code>token</code> = file kind, <code>token2</code> = publicId, <code>token3</code> = expert surname, <code>token10</code> = link</td></tr>
|
||||||
|
<tr><td><code>yara-fanavaran-claim</code></td><td>Fanavaran submission confirmed — sent to claim owner with Fanavaran claim number and ID</td><td><code>token</code> = publicId, <code>token2</code> = Fanavaran claimId, <code>token3</code> = Fanavaran claimNo</td></tr>
|
||||||
|
</table>
|
||||||
|
<p class="note" style="margin-top:8px;">
|
||||||
|
All SMS calls are fire-and-forget — they never throw. Failures are logged but do not block the main flow.
|
||||||
|
An <code>sms_send_logs</code> MongoDB collection records every outbound message with its kind (OTP vs TEMPLATE), provider, template name, and success/failure status.
|
||||||
|
Notification text messages (parties-disagree, one-party-signed, etc.) are seeded into the <code>sms_texts</code> collection on startup and editable at runtime.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||||
|
<h2 id="ai">7 — AI Service <span class="status-badge status-disabled">disabled (code present)</span></h2>
|
||||||
|
<p class="section-intro">
|
||||||
|
An image-based car damage detection service is integrated in the
|
||||||
|
codebase but its HTTP calls are <strong>fully commented out</strong>.
|
||||||
|
The module initialises on startup, attempts a login (silently swallowed
|
||||||
|
if it fails), and exposes an <code>aiRequestImage</code> method — but
|
||||||
|
the underlying axios calls are disabled. The service does not affect
|
||||||
|
any production flow.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="card card-yellow">
|
||||||
|
<h3>Intended interface (when re-enabled)</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th style="width:70px">Method</th><th>Path</th><th>What it does</th></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>{AI_URL_V2}/auth/login</code></td><td>Authenticate with username + password. Returns <code>accessToken</code>.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>{AI_URL_V2}/auth/profile</code></td><td>Fetch <code>apiKey.key</code> needed as the <code>gateway-api-key</code> request header.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>{AI_URL_V2}/services/car-damage/detector?version=ai-v7</code></td><td>Submit a car part image (multipart). Returns <code>downloadLink</code> with annotated result.</td></tr>
|
||||||
|
</table>
|
||||||
|
<p class="warn" style="margin-top:8px;">
|
||||||
|
Status: all three calls are wrapped in commented-out <code>axios.request(…)</code> blocks.
|
||||||
|
<code>CW_URL</code> is not in <code>.env.example</code>. To re-enable, uncomment the login, getApiKey, and aiRequestImage axios calls, and configure <code>AI_URL_V2</code>, <code>AI_USERNAME</code>, <code>AI_PASSWORD</code>.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||||
|
<h2 id="car-pricing">8 — Car Pricing Service <span class="status-badge status-partial">partially active</span></h2>
|
||||||
|
<p class="section-intro">
|
||||||
|
Used only during damage-expert price-drop calculation. When an expert
|
||||||
|
provides per-part severity values the system fetches real-time market
|
||||||
|
prices for the damaged car model, then computes the price-drop using
|
||||||
|
the formula: <strong>carPrice × yearCoefficient × sumOfPartCoefficients ÷ 400</strong>.
|
||||||
|
The service has two data sources (endpoints) that are tried in parallel.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="card card-orange">
|
||||||
|
<h3>Endpoints</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th style="width:70px">Method</th><th>Path</th><th>What it does</th></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>{CW_URL}price?akharin</code></td><td>Fetch car market prices from the "Akharin" source. Returns array of <code>{ carName, marketPrice }</code>.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>{CW_URL}price?hamrah</code></td><td>Fetch car market prices from the "Hamrah" source. Same response shape.</td></tr>
|
||||||
|
</table>
|
||||||
|
<p class="note" style="margin-top:8px;">
|
||||||
|
Both endpoints are tried; results are merged and de-duplicated. The best match for
|
||||||
|
the damaged car's name is found using <strong>Levenshtein distance</strong> (fuzzy string match).
|
||||||
|
If both endpoints fail or return empty, the price-drop calculation is skipped (marked incomplete) — it does not block claim submission.
|
||||||
|
</p>
|
||||||
|
<p class="warn" style="margin-top:6px;">
|
||||||
|
<strong><code>CW_URL</code> is not documented in <code>.env.example</code>.</strong>
|
||||||
|
This service will silently produce no price-drop if the variable is unset.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||||
|
<h2 id="offline-inquiry">9 — Offline Inquiry <span class="status-badge status-internal">internal / fallback</span></h2>
|
||||||
|
<p class="section-intro">
|
||||||
|
The offline inquiry layer intercepts plate-based inquiry calls before
|
||||||
|
any external HTTP is made. It is primarily used for development and
|
||||||
|
testing (pre-seeded known plates) but also acts as a resilience fallback
|
||||||
|
when live inquiry services are unavailable. It is controlled by a
|
||||||
|
runtime database flag, not an env var.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="card card-gray">
|
||||||
|
<h3>How it works</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>Aspect</th><th>Detail</th></tr>
|
||||||
|
<tr><td>Storage</td><td>MongoDB collection <code>offline-inquiries</code>. Documents contain <code>clientKey</code>, normalised plate fields, <code>nationalCode</code>, and the pre-built <code>raw</code> + <code>mapped</code> response to return.</td></tr>
|
||||||
|
<tr><td>Master switch</td><td><code>system_settings.offlineInquiry.enabled</code> — defaults to <code>true</code>. Toggle via <code>PATCH /super-admin/system-settings/offline-inquiry</code>.</td></tr>
|
||||||
|
<tr><td>Lookup order</td><td>Normalised plate (digits-only, Arabic→Persian) + national code + Fanavaran client key must all match. If found, returned immediately; no HTTP call is made.</td></tr>
|
||||||
|
<tr><td>Scope</td><td>Only applies to plate-based block-inquiry (THIRD_PARTY). CAR_BODY inquiry (<code>/badane</code>) always hits the live API.</td></tr>
|
||||||
|
<tr><td>Live API flag</td><td><code>system_settings.externalApis.sandHubUseLiveApi</code> — when <code>false</code> (default), even if no offline seed matches, a built-in mock response is returned rather than calling Tejarat/ESG.</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||||
|
<h2 id="env-ref">10 — Environment Variable Reference</h2>
|
||||||
|
<p class="section-intro">
|
||||||
|
All env vars across all integrations, grouped by service.
|
||||||
|
Variables marked <strong>*</strong> are not present in <code>.env.example</code>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="card card-blue">
|
||||||
|
<h3>Fanavaran</h3>
|
||||||
|
<table class="env-table">
|
||||||
|
<tr><th>Variable</th><th>Description</th></tr>
|
||||||
|
<tr><td><code>FANAVARAN_CLIENT</code></td><td>Active tenant profile key: <code>parsian</code> | <code>tejaratno</code> | <code>moallem</code></td></tr>
|
||||||
|
<tr><td><code>INSURANCE_CORP_ID</code></td><td>Display caption of the insurer company (e.g. <em>"بیمه پارسیان"</em>) — resolved to a numeric corpId at startup against the Fanavaran insurance-corp list.</td></tr>
|
||||||
|
</table>
|
||||||
|
<p class="note" style="margin-top:8px;">Per-tenant credentials (appName, secret, username, password, CorpId, ContractId, Location) are hardcoded in <code>src/core/config/fanavaran-client.config.ts</code> under <code>SEED_FANAVARAN_CLIENT_PROFILES</code>.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-gray">
|
||||||
|
<h3>SandHub (legacy)</h3>
|
||||||
|
<table class="env-table">
|
||||||
|
<tr><th>Variable</th><th>Description</th></tr>
|
||||||
|
<tr><td><code>SANHUB_BASE_URL</code></td><td>Base URL for SandHub. Default: <code>http://82.99.202.245:3027</code></td></tr>
|
||||||
|
<tr><td><code>SANHUB_URL_LOGIN</code></td><td>Full login URL (usually base + <code>/user/login</code>)</td></tr>
|
||||||
|
<tr><td><code>SANHUB_USERNAME</code></td><td>SandHub login email</td></tr>
|
||||||
|
<tr><td><code>SANHUB_PASSWORD</code></td><td>SandHub login password</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-teal">
|
||||||
|
<h3>Tejarat inquiry</h3>
|
||||||
|
<table class="env-table">
|
||||||
|
<tr><th>Variable</th><th>Description</th></tr>
|
||||||
|
<tr><td><code>TEJARAT_INQUIRY_BASE_URL</code></td><td>Base URL. Default: <code>http://82.99.202.245:3027</code></td></tr>
|
||||||
|
<tr><td><code>TEJARAT_INQUIRY_EMAIL</code></td><td>Login email</td></tr>
|
||||||
|
<tr><td><code>TEJARAT_INQUIRY_PASSWORD</code></td><td>Login password</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-purple">
|
||||||
|
<h3>ESG (CLIENT_ID=8 only)</h3>
|
||||||
|
<table class="env-table">
|
||||||
|
<tr><th>Variable</th><th>Description</th></tr>
|
||||||
|
<tr><td><code>CLIENT_ID</code></td><td>Set to <code>8</code> to activate the ESG inquiry provider for the Parsian tenant.</td></tr>
|
||||||
|
<tr><td><code>ESG_URL</code></td><td>ESG base URL. Default: <code>http://192.168.20.22:8085</code> (internal network)</td></tr>
|
||||||
|
<tr><td><code>ESG_USERNAME</code></td><td>ESG login username</td></tr>
|
||||||
|
<tr><td><code>ESG_PASSWORD</code></td><td>ESG login password</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-green">
|
||||||
|
<h3>SMS</h3>
|
||||||
|
<table class="env-table">
|
||||||
|
<tr><th>Variable</th><th>Description</th></tr>
|
||||||
|
<tr><td><code>SMS_PROVIDER</code> (or <code>SMS</code>)</td><td><code>kavenegar</code> (default) or <code>parsian</code></td></tr>
|
||||||
|
<tr><td><code>SMS_API_KEY</code></td><td>Kavenegar API key (required when provider = kavenegar)</td></tr>
|
||||||
|
<tr><td><code>AUTH_SMS_TEMPLATE</code></td><td>Kavenegar template name for OTP messages (e.g. <code>yara-otp</code>)</td></tr>
|
||||||
|
<tr><td><code>PARSIAN_SMS_URL</code></td><td>Parsian SMS Gateway base URL (required when provider = parsian)</td></tr>
|
||||||
|
<tr><td><code>PARSIAN_API_KEY</code></td><td>Parsian SMS <code>X-PACKAGE-API-KEY</code> header value</td></tr>
|
||||||
|
<tr><td><code>PARSIAN_BASIC_TOKEN</code></td><td>Base64-encoded credentials for <code>Authorization: Basic …</code> header</td></tr>
|
||||||
|
<tr><td><code>URL</code></td><td>Frontend base URL — used to build all invite + claim links embedded in SMS messages</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-yellow">
|
||||||
|
<h3>AI service</h3>
|
||||||
|
<table class="env-table">
|
||||||
|
<tr><th>Variable</th><th>Description</th></tr>
|
||||||
|
<tr><td><code>AI_URL_V2</code></td><td>AI gateway base URL. Default: <code>https://ai-gw.ittalie.ir</code> (unused — service is disabled)</td></tr>
|
||||||
|
<tr><td><code>AI_USERNAME</code></td><td>AI service login username (unused)</td></tr>
|
||||||
|
<tr><td><code>AI_PASSWORD</code></td><td>AI service login password (unused)</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-orange">
|
||||||
|
<h3>Car pricing service</h3>
|
||||||
|
<table class="env-table">
|
||||||
|
<tr><th>Variable</th><th>Description</th></tr>
|
||||||
|
<tr><td><code>CW_URL</code> *</td><td>Base URL for car market price API (e.g. <code>https://…/</code>). Not in <code>.env.example</code>. Price-drop silently skipped if unset.</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-gray">
|
||||||
|
<h3>General / app</h3>
|
||||||
|
<table class="env-table">
|
||||||
|
<tr><th>Variable</th><th>Description</th></tr>
|
||||||
|
<tr><td><code>PORT</code></td><td>HTTP port (default 3000). Used by the Fanavaran insurance-corp fallback to call its own local lookup endpoint.</td></tr>
|
||||||
|
<tr><td><code>CAPTCHA_ENABLED</code></td><td><code>true</code> / <code>false</code> — enables/disables login CAPTCHA challenge. Internal, no external service.</td></tr>
|
||||||
|
<tr><td><code>EXP_CAPTCHA_TIME</code></td><td>CAPTCHA challenge TTL in minutes.</td></tr>
|
||||||
|
<tr><td><code>EXP_OTP_TIME</code></td><td>OTP TTL in minutes.</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer>Made by Sepehr</footer>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
624
docs/panel-roles-reference.fa.html
Normal file
624
docs/panel-roles-reference.fa.html
Normal file
@@ -0,0 +1,624 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="fa" dir="rtl">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>مرجع نقشهای پنل</title>
|
||||||
|
<style>
|
||||||
|
*,
|
||||||
|
*::before,
|
||||||
|
*::after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
font-family: "Vazirmatn", "Tahoma", "Segoe UI", system-ui, sans-serif;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.8;
|
||||||
|
background: #ffffff;
|
||||||
|
color: #1f2328;
|
||||||
|
padding: 24px;
|
||||||
|
}
|
||||||
|
h1 { font-size: 20px; font-weight: 700; margin-bottom: 4px; }
|
||||||
|
.subtitle { font-size: 13px; color: #57606a; margin-bottom: 28px; }
|
||||||
|
h2 {
|
||||||
|
font-size: 15px; font-weight: 700;
|
||||||
|
margin-bottom: 10px; margin-top: 32px;
|
||||||
|
border-bottom: 1px solid #e5e7eb; padding-bottom: 6px;
|
||||||
|
}
|
||||||
|
h3 {
|
||||||
|
font-size: 12px; font-weight: 700;
|
||||||
|
text-transform: uppercase; letter-spacing: 0.03em;
|
||||||
|
color: #57606a; margin-bottom: 8px; margin-top: 14px;
|
||||||
|
}
|
||||||
|
.section-intro {
|
||||||
|
font-size: 13px; color: #57606a;
|
||||||
|
margin-bottom: 14px; line-height: 1.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Role header strip */
|
||||||
|
.role-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
.role-name {
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
.role-enum {
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 11px;
|
||||||
|
color: #3b82d4;
|
||||||
|
background: #f0f7ff;
|
||||||
|
border: 1px solid #bfdbfe;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 1px 6px;
|
||||||
|
direction: ltr;
|
||||||
|
unicode-bidi: embed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge {
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 11px; font-weight: 600;
|
||||||
|
padding: 1px 7px; border-radius: 10px;
|
||||||
|
margin-left: 4px; margin-bottom: 3px;
|
||||||
|
}
|
||||||
|
.badge-blue { background: #dbeafe; color: #1d4ed8; }
|
||||||
|
.badge-green { background: #dcfce7; color: #166534; }
|
||||||
|
.badge-purple { background: #ede9fe; color: #5b21b6; }
|
||||||
|
.badge-orange { background: #ffedd5; color: #9a3412; }
|
||||||
|
.badge-teal { background: #ccfbf1; color: #0f766e; }
|
||||||
|
.badge-indigo { background: #e0e7ff; color: #3730a3; }
|
||||||
|
.badge-gray { background: #f1f5f9; color: #475569; border: 1px solid #e2e8f0; }
|
||||||
|
.badge-red { background: #fee2e2; color: #991b1b; }
|
||||||
|
|
||||||
|
/* Cards */
|
||||||
|
.card {
|
||||||
|
border: 1px solid #e5e7eb;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 16px;
|
||||||
|
background: #f7f8fa;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
.card.card-blue { border-right: 4px solid #3b82f6; }
|
||||||
|
.card.card-green { border-right: 4px solid #22c55e; }
|
||||||
|
.card.card-purple { border-right: 4px solid #8b5cf6; }
|
||||||
|
.card.card-orange { border-right: 4px solid #f97316; }
|
||||||
|
.card.card-teal { border-right: 4px solid #14b8a6; }
|
||||||
|
.card.card-indigo { border-right: 4px solid #6366f1; }
|
||||||
|
.card.card-gray { border-right: 4px solid #94a3b8; }
|
||||||
|
.card.card-red { border-right: 4px solid #ef4444; }
|
||||||
|
|
||||||
|
/* Endpoint tables */
|
||||||
|
table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
width: 100%;
|
||||||
|
font-size: 12px;
|
||||||
|
margin-top: 4px;
|
||||||
|
direction: rtl;
|
||||||
|
}
|
||||||
|
th {
|
||||||
|
background: #f1f5f9; font-weight: 600;
|
||||||
|
text-align: right; padding: 5px 8px;
|
||||||
|
border: 1px solid #e5e7eb;
|
||||||
|
}
|
||||||
|
td {
|
||||||
|
padding: 4px 8px; border: 1px solid #e5e7eb;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
tr:nth-child(even) td { background: #ffffff; }
|
||||||
|
code {
|
||||||
|
font-family: monospace; font-size: 11px; color: #3b82d4;
|
||||||
|
direction: ltr; unicode-bidi: embed;
|
||||||
|
}
|
||||||
|
.method {
|
||||||
|
font-family: monospace; font-size: 11px;
|
||||||
|
font-weight: 700; white-space: nowrap;
|
||||||
|
direction: ltr; unicode-bidi: embed;
|
||||||
|
}
|
||||||
|
.method.get { color: #059669; }
|
||||||
|
.method.post { color: #2563eb; }
|
||||||
|
.method.put { color: #d97706; }
|
||||||
|
.method.patch { color: #7c3aed; }
|
||||||
|
.method.delete { color: #dc2626; }
|
||||||
|
.dep { color: #94a3b8; font-style: italic; font-size: 11px; }
|
||||||
|
|
||||||
|
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
|
||||||
|
@media (max-width: 860px) { .grid-2 { grid-template-columns: 1fr; } }
|
||||||
|
|
||||||
|
.note {
|
||||||
|
font-size: 11px; color: #57606a; font-style: normal;
|
||||||
|
margin-top: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc {
|
||||||
|
background: #f7f8fa; border: 1px solid #e5e7eb;
|
||||||
|
border-radius: 6px; padding: 14px 18px;
|
||||||
|
margin-bottom: 28px;
|
||||||
|
}
|
||||||
|
.toc-title { font-size: 13px; font-weight: 700; margin-bottom: 8px; }
|
||||||
|
.toc ol { padding-right: 18px; padding-left: 0; }
|
||||||
|
.toc li { font-size: 13px; margin-bottom: 3px; }
|
||||||
|
.toc a { color: #3b82d4; text-decoration: none; }
|
||||||
|
.toc a:hover { text-decoration: underline; }
|
||||||
|
|
||||||
|
footer {
|
||||||
|
text-align: center; font-size: 12px; color: #57606a;
|
||||||
|
border-top: 1px solid #e5e7eb;
|
||||||
|
margin-top: 40px; padding-top: 12px;
|
||||||
|
}
|
||||||
|
.max-wrap { max-width: 760px; margin: 0 auto; }
|
||||||
|
|
||||||
|
/* Role overview table */
|
||||||
|
.overview-table { font-size: 12px; margin-bottom: 24px; }
|
||||||
|
.overview-table th { white-space: nowrap; }
|
||||||
|
.overview-table td:last-child { font-weight: 600; white-space: nowrap; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="max-wrap">
|
||||||
|
<h1>مرجع نقشهای پنل</h1>
|
||||||
|
<p class="subtitle">
|
||||||
|
آنچه هر نقش میتواند ببیند و انجام دهد — اندپوینتها، مسئولیتها و
|
||||||
|
مراحل فرآیند. سوپر ادمین در این مستند نیست.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<!-- Table of Contents -->
|
||||||
|
<div class="toc">
|
||||||
|
<div class="toc-title">نقشهای پوششدادهشده</div>
|
||||||
|
<ol>
|
||||||
|
<li><a href="#insurer">بیمهگر (COMPANY) — ادمین تنانت شرکت بیمه</a></li>
|
||||||
|
<li><a href="#blame-expert">کارشناس تقصیر (EXPERT) — صف بررسی اختلاف</a></li>
|
||||||
|
<li><a href="#damage-expert">کارشناس خسارت (DAMAGE_EXPERT) — قیمتگذاری خسارت</a></li>
|
||||||
|
<li><a href="#field-expert">کارشناس میدانی (FIELD_EXPERT) — ثبت حضوری در صحنه</a></li>
|
||||||
|
<li><a href="#file-maker">فایلساز (FILE_MAKER) — روایت طرفین در V4/V5</a></li>
|
||||||
|
<li><a href="#file-reviewer">بازبین فایل (FILE_REVIEWER) — ارزیابی خسارت V4/V5</a></li>
|
||||||
|
<li><a href="#registrar">ثبات (REGISTRAR) — ثبت اداری حضوری</a></li>
|
||||||
|
<li><a href="#call-center">مرکز تماس (CALL_CENTER) — ثبت تلفنی V6</a></li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ── Role overview table ────────────────────────────────────── -->
|
||||||
|
<h2>نمای کلی نقشها</h2>
|
||||||
|
<table class="overview-table">
|
||||||
|
<tr>
|
||||||
|
<th>وظیفه اصلی</th>
|
||||||
|
<th>محدوده</th>
|
||||||
|
<th>پنل ورود</th>
|
||||||
|
<th>enum نقش</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>مشاهده تمام فایلها؛ مدیریت شعب و کارشناسان؛ گزارشگیری؛ امتیازدهی به کارشناسان</td>
|
||||||
|
<td>سطح تنانت</td>
|
||||||
|
<td>پورتال بیمهگر</td>
|
||||||
|
<td><code>company</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>قفلکردن پروندههای تقصیر، بررسی اسناد طرفین، صدور رأی یا درخواست ارسال مجدد</td>
|
||||||
|
<td>صف DISAGREEMENT تنانت</td>
|
||||||
|
<td>پنل کارشناس تقصیر</td>
|
||||||
|
<td><code>expert</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>قفلکردن خسارت، قیمتگذاری، اعتبارسنجی فاکتورها، درخواست ارسال مجدد/بازدید</td>
|
||||||
|
<td>صف خسارت تنانت</td>
|
||||||
|
<td>پنل خسارت</td>
|
||||||
|
<td><code>damage_expert</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>ثبت حضوری تقصیر + خسارت در V2/V3؛ دسترسی به پنلهای تقصیر/خسارت</td>
|
||||||
|
<td>فایلهای ساختهشده توسط خود</td>
|
||||||
|
<td>پنل کارشناس میدانی</td>
|
||||||
|
<td><code>field_expert</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>روایت طرفین V4/V5 (OTP، استعلام، جزئیات، امضا)؛ تأیید خسارت در V5</td>
|
||||||
|
<td>فایلهای ساختهشده توسط خود</td>
|
||||||
|
<td>پنل فایلساز</td>
|
||||||
|
<td><code>file_maker</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>ارزیابی خسارت V4/V5 (فیلدهای تصادف، قطعات، عکسها، امضای مالک)</td>
|
||||||
|
<td>فایلهای تخصیصیافته</td>
|
||||||
|
<td>پنل بازبین فایل</td>
|
||||||
|
<td><code>file_reviewer</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>ثبت حضوری اداری تقصیر + خسارت به نمایندگی از طرفین</td>
|
||||||
|
<td>فایلهای ساختهشده توسط خود</td>
|
||||||
|
<td>پنل ثبات</td>
|
||||||
|
<td><code>registrar</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>ثبت تلفنی V6: اجرای استعلام، ارسال لینک؛ کاربر بقیه را تکمیل میکند</td>
|
||||||
|
<td>فایلهای ساختهشده توسط خود</td>
|
||||||
|
<td>پنل مرکز تماس</td>
|
||||||
|
<td><code>call_center</code></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||||
|
<h2 id="insurer">۱ — بیمهگر <span class="role-enum">company</span></h2>
|
||||||
|
<p class="section-intro">
|
||||||
|
به ازای هر تنانت شرکت بیمه یک اکتور <code>company</code> وجود دارد. پورتال بیمهگر
|
||||||
|
لایه مدیریتی است: میتواند همه چیز زیر تنانت خود را ببیند، لیست کارشناسان را مدیریت
|
||||||
|
کند، شعب را اداره کند، تنظیمات رسانهای هر تنانت را پیکربندی کند و گزارشهای آماری
|
||||||
|
استخراج کند. بیمهگر هرگز مستقیماً با مراحل تقصیر/خسارت درگیر نمیشود — فقط نظارهگر
|
||||||
|
و امتیازدهنده است.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="card card-blue">
|
||||||
|
<h3>مدیریت فایل — <code>expert-insurer/</code></h3>
|
||||||
|
<table>
|
||||||
|
<tr><th style="width:70px">متد</th><th>مسیر</th><th>توضیح</th></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>expert-insurer/files</code></td><td>فهرست تمام فایلهای تقصیر + خسارت تنانت (ادغامشده بر اساس publicId). فیلترپذیر بر اساس وضعیت، نوع فایل، جستجو، مرتبسازی، صفحه.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>expert-insurer/files/:publicId</code></td><td>جزئیات کامل یک فایل بر اساس publicId.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>expert-insurer/files/:publicId/timeline</code></td><td>تایملاین فعالیت به ترتیب زمانی (تمام رویدادهای تاریخچه: منبع، نوع، اکتور، متادیتا).</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>expert-insurer/files/:publicId/report</code></td><td>دادههای ساختاریافته گزارش برای تولید PDF (بخشهای مالک، راننده، بیمه، خودرو، تصادف).</td></tr>
|
||||||
|
<tr><td><span class="method put">PUT</span></td><td><code>expert-insurer/files/:publicId/rating</code></td><td>امتیازدهی به کارشناسان یک فایل (۱–۵ در هر بُعد: روش تصادف، بهموقعبودن، دقت علت، دقت شناسایی مقصر، امتیاز ربات).</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>expert-insurer/report/unified-file-statuses</code></td><td>کاتالوگ وضعیت یکپارچه + تعداد به ازای هر وضعیت برای کل پرتفولیوی تنانت. فیلترپذیر بر اساس fileType و بازه تاریخ.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>expert-insurer/report/status-counts</code></td><td class="dep">منسوخشده — از unified-file-statuses استفاده کنید.</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-blue">
|
||||||
|
<h3>مدیریت شعب — <code>expert-insurer/branches</code></h3>
|
||||||
|
<table>
|
||||||
|
<tr><th style="width:70px">متد</th><th>مسیر</th><th>توضیح</th></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>expert-insurer/branches</code></td><td>فهرست تمام شعب این بیمهگر. پارامترها: جستجو، بازه تاریخ from/to، فیلتر isActive.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>expert-insurer/branches</code></td><td>افزودن شعبه جدید (نام، کد، آدرس، شهر، تلفن و غیره).</td></tr>
|
||||||
|
<tr><td><span class="method put">PUT</span></td><td><code>expert-insurer/branches/:branchId/status</code></td><td>فعال یا غیرفعال کردن یک شعبه.</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-blue">
|
||||||
|
<h3>مدیریت لیست کارشناسان — <code>expert-insurer/experts</code></h3>
|
||||||
|
<table>
|
||||||
|
<tr><th style="width:70px">متد</th><th>مسیر</th><th>توضیح</th></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>expert-insurer/experts/blame</code></td><td>ایجاد حساب کارشناس تقصیر جدید زیر این بیمهگر.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>expert-insurer/experts/claim</code></td><td>ایجاد حساب کارشناس خسارت جدید زیر این بیمهگر.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>expert-insurer/experts/file-maker</code></td><td>ایجاد حساب فایلساز جدید زیر این بیمهگر.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>expert-insurer/experts/file-reviewer</code></td><td>ایجاد حساب بازبین فایل جدید زیر این بیمهگر.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>expert-insurer/experts/list</code></td><td>فهرست صفحهبندیشده تمام حسابهای کارشناس در این تنانت.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>expert-insurer/experts/top</code></td><td>برترین کارشناسان تقصیر و خسارت رتبهبندیشده بر اساس میانگین امتیاز کلی (حداکثر ۱۰ نفر از هر نوع).</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>expert-insurer/top-experts</code></td><td>نام مستعار experts/top (سازگاری با فرانتاند).</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>expert-insurer/:expertId</code></td><td>فایلهای رسیدگیشده توسط یک کارشناس (ردیفهای خلاصه — تقصیر یا خسارت بسته به نوع کارشناس).</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-blue">
|
||||||
|
<h3>آمار و گزارشها</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th style="width:70px">متد</th><th>مسیر</th><th>توضیح</th></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>expert-insurer/statistics</code></td><td>کارتهای KPI: totalFilesReviewed، averageUserRating، inPersonCount، filesThisMonth، objectionPercentage و غیره. فیلترپذیر بر اساس بازه تاریخ.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>expert-insurer/top-files</code></td><td>۱۰ فایل خسارت برتر بر اساس بالاترین امتیاز (ترکیبی از امتیاز بیمهگر + کاربر).</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>expert-insurer/expert-work-log</code></td><td>لاگ کاری هر کارشناس: totalHandled، currentlyChecking، distinctFilesCheckedInPeriod. فیلترپذیر بر اساس expertKind و بازه تاریخ.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>reports/report/insurer/requests</code></td><td>تعداد خسارت + وضعیت تقصیر + تعداد فایل یکپارچه برای تنانت.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>reports/report/insurer/per-month-requests</code></td><td>همان خلاصه، تفکیکشده بر اساس ۵ ماه تقویمی اخیر.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>reports/report/insurer/checked-requests</code></td><td>همان خلاصه، فیلترشده بر اساس بازه زمانی اختیاری createdAt.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>reports/report/insurer/expert-work-log</code></td><td>لاگ کاری کارشناسان (مجموعههای کارشناس تقصیر و خسارت، نه کارشناسان میدانی).</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>reports/report/insurer/expert-work-log/per-month</code></td><td>همان لاگ کاری تفکیکشده بر اساس ماه تقویمی (۵ ماه اخیر).</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-blue">
|
||||||
|
<h3>تنظیمات تنانت — <code>client-panel/</code></h3>
|
||||||
|
<table>
|
||||||
|
<tr><th style="width:70px">متد</th><th>مسیر</th><th>توضیح</th></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>client-panel/settings</code></td><td>دریافت محدودیتهای رسانهای هر تنانت (حداکثر بایت ویدیو/تصویر/صوت) و پنجره زمانی تصادف CAR_BODY (روز).</td></tr>
|
||||||
|
<tr><td><span class="method patch">PATCH</span></td><td><code>client-panel/settings</code></td><td>بهروزرسانی جزئی این تنظیمات. نمیتواند از سقفهای سطح سیستم تجاوز کند.</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||||
|
<h2 id="blame-expert">۲ — کارشناس تقصیر <span class="role-enum">expert</span></h2>
|
||||||
|
<p class="section-intro">
|
||||||
|
فایلهای تقصیر در صف DISAGREEMENT را بررسی میکند — پروندههایی که دو طرف درباره
|
||||||
|
مقصر بودن توافق ندارند. پس از بررسی اسناد و اظهارات طرفین، کارشناس پرونده را قفل
|
||||||
|
میکند، سپس یا رأی صادر میکند، درخواست ارسال مجدد اسناد میدهد، یا نتیجه بازدید
|
||||||
|
حضوری را ثبت میکند. تمام اندپوینتها زیر <code>v2/expert-blame/</code> هستند.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="card card-orange">
|
||||||
|
<h3>فرآیند</h3>
|
||||||
|
<p class="note">
|
||||||
|
۱ مرور فهرست ← ۲ تخصیص (قفل) پرونده ← ۳ بررسی مدارک طرفین (ویدیو، صدا، اسناد) ←
|
||||||
|
۴الف صدور رأی <em>یا</em> ۴ب درخواست ارسال مجدد اسناد <em>یا</em> ۴پ ثبت بازدید حضوری.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-orange">
|
||||||
|
<h3>اندپوینتها — <code>v2/expert-blame/</code></h3>
|
||||||
|
<table>
|
||||||
|
<tr><th style="width:70px">متد</th><th>مسیر</th><th>توضیح</th></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>v2/expert-blame/</code></td><td>فهرست پروندههای تقصیر در صف DISAGREEMENT (موجود، قفلشده توسط من، یا تصمیمگرفتهشده توسط من). پارامترها: search، sortBy، sortOrder، page، limit، unifiedStatus، fileType.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>v2/expert-blame/:id</code></td><td>جزئیات کامل یک پرونده تقصیر (اظهارات، عکس، صدا، ویدیو طرفین).</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>v2/expert-blame/:id/assign</code></td><td>بررسی در دسترس بودن و قفل پرونده برای این کارشناس. بازمیگرداند: <code>assigned</code>، <code>already_assigned_to_you</code>، یا ۴۰۹ در صورتی که شخص دیگری آن را نگه داشته باشد.</td></tr>
|
||||||
|
<tr><td><span class="method put">PUT</span></td><td><code>v2/expert-blame/reply/submit/:id</code></td><td>ارسال رأی (accidentWay، accidentReason، accidentType، تصمیم طرف مقصر). پرونده را آزاد میکند و به COMPLETED منتقل میکند.</td></tr>
|
||||||
|
<tr><td><span class="method put">PUT</span></td><td><code>v2/expert-blame/reply/resend/:id</code></td><td>درخواست از طرفین برای بارگذاری مجدد اسناد. تقصیر را به WAITING_FOR_RESEND تنظیم میکند. یک درخواست ارسال مجدد در هر چرخه عمر.</td></tr>
|
||||||
|
<tr><td><span class="method put">PUT</span></td><td><code>v2/expert-blame/reply/inPerson/:id</code></td><td>ثبت اینکه بازدید حضوری انجام شده و صدور رأی.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>v2/expert-blame/report/unified-file-statuses</code></td><td>کاتالوگ وضعیت + تعداد به ازای هر وضعیت برای پرتفولیوی این کارشناس.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>v2/expert-blame/report/status-counts</code></td><td class="dep">منسوخشده — از unified-file-statuses استفاده کنید.</td></tr>
|
||||||
|
<tr><td><span class="method put">PUT</span></td><td><code>v2/expert-blame/lock/:id</code></td><td class="dep">اندپوینت قفل منسوخشده — از POST assign استفاده کنید.</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||||
|
<h2 id="damage-expert">۳ — کارشناس خسارت <span class="role-enum">damage_expert</span></h2>
|
||||||
|
<p class="section-intro">
|
||||||
|
فایلهای خسارت را پس از ارسال مدارک خسارت توسط کاربر بررسی میکند. کارشناس
|
||||||
|
هر قطعه آسیبدیده را قیمتگذاری میکند، بهصورت اختیاری کاهش قیمت (استهلاک)
|
||||||
|
محاسبه میکند، و میتواند از کاربر بخواهد مدارک را مجدداً ارسال کند، حضوری مراجعه
|
||||||
|
کند، یا فاکتورهای تعمیرگاه را هنگام نیاز به قیمتگذاری کارگاهی بارگذاری کند.
|
||||||
|
تمام اندپوینتها زیر <code>v2/expert-claim/</code> هستند.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="card card-red">
|
||||||
|
<h3>فرآیند</h3>
|
||||||
|
<p class="note">
|
||||||
|
۱ مرور فهرست ← ۲ تخصیص (قفل) خسارت ← ۳ بررسی عکسها و اسناد خسارت ←
|
||||||
|
۴ ویرایش اختیاری قطعات انتخابشده یا محاسبه کاهش قیمت ←
|
||||||
|
۵الف ارسال پاسخ قیمتگذاریشده <em>یا</em> ۵ب درخواست ارسال مجدد <em>یا</em> ۵پ درخواست بازدید حضوری ←
|
||||||
|
۶ در صورت وجود قطعات فاکتوردار: اعتبارسنجی فاکتورهای تعمیرگاه بارگذاریشده.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-red">
|
||||||
|
<h3>اندپوینتها — <code>v2/expert-claim/</code></h3>
|
||||||
|
<table>
|
||||||
|
<tr><th style="width:70px">متد</th><th>مسیر</th><th>توضیح</th></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>v2/expert-claim/requests</code></td><td>فهرست خسارتها در صف <code>WAITING_FOR_DAMAGE_EXPERT</code> + صف اعتبارسنجی فاکتور. پارامترها: search، sortBy، page، limit، unifiedStatus، fileType.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>v2/expert-claim/request/:claimRequestId</code></td><td>جزئیات کامل خسارت: قطعات آسیبدیده، تصاویر گرفتهشده، اسناد، priceDrop، داده طرف بلیم، آدرسهای ویدیو.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>v2/expert-claim/assign/:claimRequestId</code></td><td>قفل خسارت برای این کارشناس. بازمیگرداند: <code>assigned</code>، <code>already_assigned_to_you</code>، یا ۴۰۹.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>v2/expert-claim/request/:claimRequestId/price-drop</code></td><td>محتوای کاهش قیمت: برچسبهای شدت، کاتالوگ ضریب، قطعات آسیبدیده + نگاشت، سال پیشنهادی خودرو از استعلام تقصیر.</td></tr>
|
||||||
|
<tr><td><span class="method put">PUT</span></td><td><code>v2/expert-claim/request/:claimRequestId/price-drop</code></td><td>محاسبه و ذخیره کاهش قیمت: قیمت خودرو × ضریب سال × مجموع ضرایب ÷ ۴۰۰.</td></tr>
|
||||||
|
<tr><td><span class="method put">PUT</span></td><td><code>v2/expert-claim/reply/submit/:claimRequestId</code></td><td>ارسال پاسخ ارزیابی خسارت (لیست قطعات قیمتگذاریشده، داغی، branchId). سقف: کل ≤ ۵۳،۰۰۰،۰۰۰ تومان. بسته به پرچمهای factorNeeded، خسارت را به owner-sign، mixed-factors-pending، یا صف اعتبارسنجی فاکتور منتقل میکند.</td></tr>
|
||||||
|
<tr><td><span class="method put">PUT</span></td><td><code>v2/expert-claim/reply/resend/:claimRequestId</code></td><td>درخواست از کاربر برای ارسال مجدد اسناد/عکسها. یک ارسال مجدد در هر چرخه خسارت؛ در صورت تکمیل قبلی ۴۲۲ برمیگرداند.</td></tr>
|
||||||
|
<tr><td><span class="method patch">PATCH</span></td><td><code>v2/expert-claim/:claimRequestId/visit</code></td><td>درخواست از کاربر برای مراجعه حضوری. خسارت را آزاد میکند، وضعیت claimStatus را به NEEDS_REVISION تنظیم میکند.</td></tr>
|
||||||
|
<tr><td><span class="method patch">PATCH</span></td><td><code>v2/expert-claim/validate-factors/:claimRequestId</code></td><td>اعتبارسنجی فاکتورهای تعمیرگاه بارگذاریشده. تأیید یا رد هر خط فاکتور با totalPayment. سقف برای تمام خطوط اعمال میشود (≤ ۵۳،۰۰۰،۰۰۰ تومان). پس از تصمیمگیری درباره تمام خطوط، بهصورت خودکار تکمیل میشود.</td></tr>
|
||||||
|
<tr><td><span class="method patch">PATCH</span></td><td><code>v2/expert-claim/request/:claimRequestId/damaged-parts</code></td><td>ویرایش قطعات آسیبدیده انتخابشده در حالی که خسارت توسط این کارشناس قفل است (EXPERT_REVIEWING).</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>v2/expert-claim/outer-parts-catalog</code></td><td>کاتالوگ قطعات بیرونی خودرو فناوران (مشترک با جریان کاربر).</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>v2/expert-claim/inner-parts-catalog</code></td><td>JSON ثابت کاتالوگ قطعات داخلی خودرو.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>v2/expert-claim/branches</code></td><td>شعب بیمهگر برای تنانت این کارشناس (برای انتخاب داغی/شعبه در پیلود پاسخ).</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>v2/expert-claim/stream/:id/video</code></td><td>پخش ویدیوی خسارت (ویدیوی دور زدن خودرو یا ویدیوی تصادف). پارامتر: <code>query=car-capture|accident</code>.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>v2/expert-claim/report/unified-file-statuses</code></td><td>کاتالوگ وضعیت + تعداد برای پرتفولیوی خسارت این کارشناس.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>v2/expert-claim/report/status-counts</code></td><td class="dep">منسوخشده — از unified-file-statuses استفاده کنید.</td></tr>
|
||||||
|
<tr><td><span class="method put">PUT</span></td><td><code>v2/expert-claim/lock/:claimRequestId</code></td><td class="dep">اندپوینت قفل منسوخشده — از POST assign استفاده کنید.</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||||
|
<h2 id="field-expert">۴ — کارشناس میدانی <span class="role-enum">field_expert</span></h2>
|
||||||
|
<p class="section-intro">
|
||||||
|
به صحنه تصادف میرود و فرمهای هر دو طرف را حضوری پر میکند (جریان V2 mirror / V3).
|
||||||
|
کارشناس میدانی همچنین دسترسی خواندن به پنلهای expert-blame و expert-claim دارد
|
||||||
|
(محدود به فایلهای خودش). تنها نقشی است که هم <strong>ثبت تقصیر</strong> و هم
|
||||||
|
<strong>ثبت خسارت</strong> را در یک جلسه انجام میدهد.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="card card-green">
|
||||||
|
<h3>ثبت تقصیر — <code>v2/expert-initiated/blame-request-management/</code></h3>
|
||||||
|
<p class="note">آینهای از API تقصیر کاربر. فرانتاند همان صفحات را با تغییر فقط پیشوند مسیر بازاستفاده میکند.</p>
|
||||||
|
<table>
|
||||||
|
<tr><th style="width:70px">متد</th><th>مسیر</th><th>توضیح</th></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>POST /</code></td><td>ایجاد فایل تقصیر IN_PERSON.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>send-party-otp/:id</code></td><td>ارسال OTP به یک طرف از طریق شماره تلفن (بدون لینک دعوت).</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>verify-party-otp/:id</code></td><td>تأیید OTP یک طرف و اتصال حساب آنها.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>blame-confession/:id</code></td><td>ثبت اعتراف تقصیر طرف.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>car-body-form/:id</code></td><td>[فقط CAR_BODY] فرم نوع تصادف.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>run-inquiries/:id</code> / <code>run-inquiries-vin/:id</code></td><td>فرم اولیه / استعلام پلاک یا VIN برای طرف فعلی.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>upload-video/:id</code></td><td>بارگذاری ویدیوی طرف اول.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>add-detail-location/:id</code></td><td>افزودن موقعیت GPS برای طرف فعلی.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>upload-voice/:id</code></td><td>بارگذاری ضبط صوتی برای طرف فعلی.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>add-detail-description/:id</code></td><td>افزودن توضیحات برای طرف فعلی.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>add-second-party/:phone/:id/</code></td><td>پیشروی به طرف دوم (بدون ارسال لینک SMS).</td></tr>
|
||||||
|
<tr><td><span class="method put">PUT</span></td><td><code>sign/:id</code></td><td>بارگذاری امضای طرف (اول سپس دوم، پارامتر partyRole).</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>accident-fields/:id</code></td><td>ذخیره فیلدهای تصادف و تکمیل فوری تقصیر (بدون صف کارشناس).</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-green">
|
||||||
|
<h3>ثبت تقصیر + خسارت V3 — <code>v3/expert-initiated/blame-request-management/</code></h3>
|
||||||
|
<p class="note">ترتیب مراحل بازسازماندهیشده: ابتدا تمام روایت طرفین، سپس ارزیابی خسارت. هم تقصیر هم خسارت در این کنترلر واحد مدیریت میشوند.</p>
|
||||||
|
<table>
|
||||||
|
<tr><th style="width:70px">متد</th><th>مسیر</th><th>توضیح</th></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>POST /</code> ← <code>send-party-otp</code> ← <code>verify-party-otp</code> ← <code>run-inquiries</code> ← <code>add-detail-*</code> ← <code>sign</code> (×۲)</td><td>مرحله روایت طرفین (مراحل ۱–۸) — اندپوینتهای یکسان با mirror، همان قرارداد.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>accident-fields/:id</code></td><td>مرحله ۹: ذخیره فیلدهای تصادف پس از امضای هر دو طرف.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>claim-id/:requestId</code></td><td>مرحله ۱۰: دریافت شناسه خسارت ایجادشده بهصورت خودکار.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>upload-document/:claimId</code></td><td>مرحله ۱۱: بارگذاری اسناد گواهینامه / کارت خودرو.</td></tr>
|
||||||
|
<tr><td><span class="method patch">PATCH</span></td><td><code>select-outer-parts/:claimId</code> / <code>select-other-parts/:claimId</code></td><td>مراحل ۱۲–۱۳: انتخاب قطعات آسیبدیده.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>capture-part/:claimId</code></td><td>مرحله ۱۴: عکسبرداری از قطعات + زوایا.</td></tr>
|
||||||
|
<tr><td><span class="method patch">PATCH</span></td><td><code>car-capture/:claimId</code></td><td>مرحله ۱۵: ویدیوی دور زدن خودرو.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>upload-video/:requestId</code></td><td>مرحله ۱۶: ویدیوی تصادف تقصیر (نهایی) ← WAITING_FOR_EXPERT (THIRD_PARTY) یا COMPLETED (CAR_BODY).</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-green">
|
||||||
|
<h3>دسترسی به پنل expert-blame + expert-claim (خواندن + اقدام روی فایلهای خود)</h3>
|
||||||
|
<p class="note">
|
||||||
|
FIELD_EXPERT مسیر <code>v2/expert-blame/</code> را محدود به فایلهای ساختهشده توسط خودش میبیند (نه صف اختلاف).
|
||||||
|
همچنین <code>v2/expert-claim/</code> را برای خسارتهای مرتبط با فایلهای تقصیرش میبیند.
|
||||||
|
اندپوینتهای یکسان با پنلهای کارشناس تقصیر و کارشناس خسارت در بالا.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||||
|
<h2 id="file-maker">۵ — فایلساز <span class="role-enum">file_maker</span></h2>
|
||||||
|
<p class="section-intro">
|
||||||
|
اولین اکتور در تقسیم V4/V5. فایلساز روایت طرفین را در محل انجام میدهد:
|
||||||
|
OTPها، استعلامها، موقعیت/توضیحات/صدا و امضاها برای هر دو طرف.
|
||||||
|
همچنین اسناد اولیه خسارت (گواهینامهها، کارتهای خودرو) را بارگذاری میکند. پس از
|
||||||
|
امضای دوم، فایل برای تحویل به بازبین فایل «مهرومومه» میشود. در V5، فایلساز
|
||||||
|
در انتها بازمیگردد تا خسارت تکمیلشده را قبل از ارسال به فناوران تأیید یا رد کند.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="card card-purple">
|
||||||
|
<h3>ثبت تقصیر — <code>v4/file-maker/blame-request-management/</code> و <code>v5/…</code></h3>
|
||||||
|
<p class="note">اندپوینتهای V4 و V5 یکسان هستند — فقط پیشوند تغییر میکند. V5 هنگام ایجاد <code>requiresFileMakerApproval=true</code> را تنظیم میکند.</p>
|
||||||
|
<table>
|
||||||
|
<tr><th style="width:70px">متد</th><th>مسیر</th><th>توضیح</th></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>POST /</code></td><td>ایجاد فایل تقصیر IN_PERSON.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>my-files</code></td><td>فهرست تمام فایلهای تقصیر ایجادشده توسط این فایلساز.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>my-files/:requestId</code></td><td>جزئیات کامل یک فایل (طرفین، گردش کار، شناسه خسارت مرتبط).</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>claim-id/:requestId</code></td><td>دریافت شناسه خسارت ایجادشده بهصورت خودکار پس از استعلام طرف مقصر.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>send-party-otp/:id</code> / <code>verify-party-otp/:id</code></td><td>ارسال + تأیید OTP برای یک طرف در هر بار (ابتدا مقصر، سپس زیاندیده).</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>car-body-form/:id</code></td><td>[فقط CAR_BODY] فرم نوع تصادف.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>run-inquiries/:id</code> / <code>run-inquiries-vin/:id</code></td><td>اجرای استعلام پلاک یا VIN. فراخوانی اول = مقصر (+ خودکار خسارت ایجاد میکند). فراخوانی دوم = زیاندیده (فقط THIRD_PARTY).</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>add-detail-location/:id</code> / <code>add-detail-description/:id</code> / <code>upload-voice/:id</code></td><td>افزودن موقعیت، توضیحات و صدا برای طرف فعلی (پارامتر partyRole، FIRST/SECOND را انتخاب میکند).</td></tr>
|
||||||
|
<tr><td><span class="method put">PUT</span></td><td><code>sign/:id</code></td><td>بارگذاری امضای طرف (partyRole=FIRST سپس SECOND). پس از امضای دوم، فایل مهرومومه میشود.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>upload-document/:claimId</code></td><td>بارگذاری گواهینامه / کارتهای خودرو روی خسارت ایجادشده بهصورت خودکار.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>capture-requirements/:claimId</code></td><td>الزامات عکسبرداری آگاه از مرحله (فازها: اسناد پیش از عکسبرداری در مقابل قطعات آسیبدیده + شاسی/موتور).</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-purple">
|
||||||
|
<h3>تأیید خسارت V5 — <code>v5/file-maker/claim-approval/</code></h3>
|
||||||
|
<p class="note">فقط در V5 استفاده میشود. پس از بررسی کارشناس خسارت و امضای مالک، خسارت وارد <code>WAITING_FOR_FILE_MAKER_APPROVAL</code> میشود.</p>
|
||||||
|
<table>
|
||||||
|
<tr><th style="width:70px">متد</th><th>مسیر</th><th>توضیح</th></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>approve/:claimId</code></td><td>تأیید خسارت تکمیلشده ← ارسال SMS امضای مالک را فعال میکند ← پس از امضای مالک به فناوران ارسال میشود.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>reject/:claimId</code></td><td>رد به بازبین فایل ← خسارت به WAITING_FOR_DAMAGE_EXPERT برمیگردد. محدودیت: حداکثر ۲ رد در هر خسارت؛ تلاش سوم ۴۲۲ با کد <code>FILE_MAKER_REJECTION_LIMIT_EXCEEDED</code> برمیگرداند.</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||||
|
<h2 id="file-reviewer">۶ — بازبین فایل <span class="role-enum">file_reviewer</span></h2>
|
||||||
|
<p class="section-intro">
|
||||||
|
دومین اکتور در تقسیم V4/V5. بازبین فایل فایلهای مهرومومهشده (پس از اتمام کار
|
||||||
|
فایلساز) را تحویل میگیرد و مرحله کامل ارزیابی خسارت را انجام میدهد: فیلدهای
|
||||||
|
تصادف، دریافت الزامات عکسبرداری، بارگذاری اسناد (شاسی/موتور)، انتخاب قطعات،
|
||||||
|
عکسهای قطعات، ویدیوی دور زدن خودرو، امضای مالک. تقصیر با car-capture به
|
||||||
|
COMPLETED علامتگذاری میشود. بازبین فایل همچنین دسترسی خواندن به پنل expert-claim
|
||||||
|
برای خسارتهایی که بررسی میکند دارد.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="card card-teal">
|
||||||
|
<h3>ارزیابی خسارت — <code>v4/file-reviewer/blame-request-management/</code> و <code>v5/…</code></h3>
|
||||||
|
<p class="note">اندپوینتهای V4 و V5 یکسان هستند — فقط پیشوند تغییر میکند.</p>
|
||||||
|
<table>
|
||||||
|
<tr><th style="width:70px">متد</th><th>مسیر</th><th>توضیح</th></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>my-files</code></td><td>فهرست تمام فایلهای تخصیصیافته به این بازبین فایل.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>my-files/:requestId</code></td><td>جزئیات کامل یک فایل (طرفین، گردش کار، فیلدهای کارشناس، شناسه خسارت مرتبط).</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>claim-id/:requestId</code></td><td>دریافت شناسه خسارت ایجادشده بهصورت خودکار (از استعلام طرف مقصر فایلساز).</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>accident-fields/:requestId</code></td><td>مرحله ۱ (بازبین): ذخیره فیلدهای تصادف (accidentWay، accidentReason، accidentType).</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>capture-requirements/:claimId</code></td><td>الزامات عکسبرداری آگاه از مرحله (فاز اسناد پیش از عکسبرداری در مقابل فاز عکسبرداری قطعات).</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>upload-document/:claimId</code></td><td>بارگذاری اسناد شاسی / موتور / پلاک فلزی.</td></tr>
|
||||||
|
<tr><td><span class="method patch">PATCH</span></td><td><code>select-outer-parts/:claimId</code></td><td>انتخاب قطعات آسیبدیده بیرونی (بدنه).</td></tr>
|
||||||
|
<tr><td><span class="method patch">PATCH</span></td><td><code>select-other-parts/:claimId</code></td><td>انتخاب سایر قطعات آسیبدیده (غیر بدنه).</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>capture-part/:claimId</code></td><td>عکسبرداری از قطعات + زوایا برای هر قطعه آسیبدیده انتخابشده.</td></tr>
|
||||||
|
<tr><td><span class="method patch">PATCH</span></td><td><code>car-capture/:claimId</code></td><td>ویدیوی دور زدن خودرو (آخرین مرحله عکسبرداری بازبین). خسارت ← <code>WAITING_FOR_DAMAGE_EXPERT</code>، تقصیر ← <code>COMPLETED</code>.</td></tr>
|
||||||
|
<tr><td><span class="method put">PUT</span></td><td><code>claim-sign/:claimId</code></td><td>ثبت امضای مالک روی قیمتگذاری کارشناس به نمایندگی از طرف زیاندیده (موافقت + branchId + تصویر امضا).</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>upload-video/:requestId</code></td><td class="dep">در V4/V5 بیاثر است — تقصیر از قبل توسط car-capture COMPLETED شده. موفقیت idempotent برمیگرداند.</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-teal">
|
||||||
|
<h3>دسترسی به پنل expert-claim</h3>
|
||||||
|
<p class="note">
|
||||||
|
FILE_REVIEWER در نقشهای مجاز برای <code>v2/expert-claim/</code> است.
|
||||||
|
میتواند جزئیات خسارت را مشاهده کند و جریان assign/lock را برای خسارتهای
|
||||||
|
مرتبط با فایلهایش اجرا کند. نمیتواند بهطور مستقل درخواست ارسال مجدد
|
||||||
|
کارشناس خسارت را آغاز کند.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||||
|
<h2 id="registrar">۷ — ثبات <span class="role-enum">registrar</span></h2>
|
||||||
|
<p class="section-intro">
|
||||||
|
نقش اداری که تقصیر و خسارت حضوری را به نمایندگی از طرفین ثبت میکند.
|
||||||
|
از جریان OTP دستهای استفاده میکند (OTPهای هر دو طرف بهصورت همزمان ارسال و
|
||||||
|
تأیید میشوند) به جای OTP یکبهیک که توسط کارشناسان میدانی استفاده میشود.
|
||||||
|
پس از تقصیر، ثبات آینه API خسارت کاربر را دنبال میکند تا انتخاب قطعات، اسناد
|
||||||
|
و عکسبرداری را پر کند. سپس فایل وارد چرخه عادی بررسی کارشناس خسارت میشود.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="card card-gray">
|
||||||
|
<h3>ثبت تقصیر — <code>registrar-initiated-blame/</code></h3>
|
||||||
|
<p class="note">توجه: <code>@ApiExcludeController</code> — مسیرها وجود دارند اما در مستندات Swagger نمایش داده نمیشوند.</p>
|
||||||
|
<table>
|
||||||
|
<tr><th style="width:70px">متد</th><th>مسیر</th><th>توضیح</th></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>registrar-initiated-blame/create</code></td><td>ایجاد فایل تقصیر IN_PERSON.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>registrar-initiated-blame/my-files</code></td><td>فهرست تمام فایلهای تقصیر ایجادشده توسط این ثبات.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>registrar-initiated-blame/blame/:requestId</code></td><td>جزئیات کامل یک فایل تقصیر.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>registrar-initiated-blame/send-party-otps/:id</code></td><td>ارسال OTP به هر دو طرف بهصورت همزمان.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>registrar-initiated-blame/verify-party-otps/:id</code></td><td>تأیید OTPهای هر دو طرف در یک فراخوانی.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>registrar-initiated-blame/complete-blame-data/:id</code></td><td>ارسال تمام دادههای فرم تقصیر هر دو طرف در یک پیلود.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>registrar-initiated-blame/upload-video/:id</code></td><td>بارگذاری ویدیوی تقصیر.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>registrar-initiated-blame/upload-voice/:id</code></td><td>بارگذاری ضبط صوتی.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>registrar-initiated-blame/add-accident-fields/:id</code></td><td>ذخیره فیلدهای تصادف و تکمیل تقصیر.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>registrar-initiated-blame/upload-party-signature/:id</code></td><td>بارگذاری امضای یک طرف (partyRole=FIRST/SECOND).</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-gray">
|
||||||
|
<h3>ثبت خسارت — <code>v2/registrar/claim-request-management/</code></h3>
|
||||||
|
<p class="note">آینهای از API خسارت کاربر. فرانتاند همان صفحات خسارت را با تغییر فقط پیشوند بازاستفاده میکند.</p>
|
||||||
|
<table>
|
||||||
|
<tr><th style="width:70px">متد</th><th>مسیر</th><th>توضیح</th></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>create-from-blame/:blameId</code></td><td>ایجاد خسارت از یک فایل تقصیر تکمیلشده.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>outer-parts-catalog</code> / <code>car-other-part</code></td><td>کاتالوگ قطعات (قطعات بیرونی بدنه + JSON سایر قطعات).</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>branches/:insuranceId</code></td><td>فهرست شعب بیمهگر (برای انتخاب شعبه در مرحله امضای خسارت).</td></tr>
|
||||||
|
<tr><td><span class="method patch">PATCH</span></td><td><code>select-outer-parts/:claimId</code></td><td>انتخاب قطعات آسیبدیده بیرونی.</td></tr>
|
||||||
|
<tr><td><span class="method patch">PATCH</span></td><td><code>select-other-parts/:claimId</code></td><td>انتخاب سایر قطعات آسیبدیده + اطلاعات بانکی.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>upload-document/:claimId</code></td><td>بارگذاری اسناد خسارت (گواهینامه، کارت خودرو).</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>capture-part/:claimId</code></td><td>عکسبرداری از قطعات + زوایا.</td></tr>
|
||||||
|
<tr><td><span class="method patch">PATCH</span></td><td><code>car-capture/:claimId</code></td><td>ویدیوی دور زدن خودرو (مرحله نهایی) ← WAITING_FOR_DAMAGE_EXPERT.</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||||
|
<h2 id="call-center">۸ — مرکز تماس <span class="role-enum">call_center</span></h2>
|
||||||
|
<p class="section-intro">
|
||||||
|
ثبت تقصیر تلفنی V6 را مدیریت میکند. اپراتور دادههای طرف مقصر را از طریق تلفن
|
||||||
|
جمعآوری میکند، استعلام بیمه را اجرا میکند و لینک تقصیر را از طریق SMS ارسال
|
||||||
|
میکند. کاربر سپس بقیه فرم را از طریق جریان استاندارد V2 تکمیل میکند
|
||||||
|
(با رد شدن مرحله فرم اولیه/استعلام). کار اپراتور مرکز تماس پس از send-link
|
||||||
|
پایان مییابد؛ میتواند پیشرفت را از طریق اندپوینتهای خواندن پایش کند.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="card card-indigo">
|
||||||
|
<h3>اندپوینتها — <code>v6/call-center-blame/</code></h3>
|
||||||
|
<table>
|
||||||
|
<tr><th style="width:70px">متد</th><th>مسیر</th><th>توضیح</th></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>create</code></td><td>ایجاد فایل تقصیر LINK. بدنه: <code>{ type: "THIRD_PARTY" | "CAR_BODY" }</code>.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>run-inquiry/:requestId</code></td><td>اجرای استعلام بیمه پلاک + کد ملی برای طرف مقصر. نتیجه را روی سند تقصیر ذخیره میکند.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>run-inquiry-vin/:requestId</code></td><td>VIN/شاسی جایگزین برای run-inquiry. از جستجوی شاسی ESG استفاده میکند.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>send-link/:requestId</code></td><td>در صورت لزوم کاربر را ثبتنام میکند، بهعنوان طرف اول ذخیره میکند، لینک دعوت تقصیر را از طریق SMS ارسال میکند. بدنه: <code>{ phoneNumber }</code>.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>my-files</code></td><td>فهرست تمام فایلهای تقصیر شروعشده توسط این اپراتور.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>blame/:requestId</code></td><td>وضعیت فعلی و مرحله گردش کار یک فایل (برای بررسی اینکه کاربر لینک را باز کرده و پیشرفت کرده است).</td></tr>
|
||||||
|
</table>
|
||||||
|
<p class="note" style="margin-top:8px;">
|
||||||
|
پس از <code>send-link</code>، کاربر فرم را از طریق
|
||||||
|
<code>v2/blame-request-management/</code> (جریان استاندارد V2) تکمیل میکند.
|
||||||
|
مرحله فرم اولیه / استعلام بهصورت خودکار رد میشود
|
||||||
|
(<code>skipInitialFormStep=true</code>). جریان خسارت پاییندستی همان
|
||||||
|
جریان استاندارد خسارت V2 است.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||||
|
<h2>مشترک: احراز هویت اکتورها</h2>
|
||||||
|
<p class="section-intro">
|
||||||
|
تمام اکتورهای پنل (هر نقش به جز <code>user</code>) از طریق همان اندپوینت
|
||||||
|
<code>POST actor/login</code> با کپچا احراز هویت میکنند. بازنشانی رمز عبور از
|
||||||
|
طریق OTP ایمیل است. خواندن و ویرایش پروفایل نیز مشترک است.
|
||||||
|
</p>
|
||||||
|
<div class="card card-gray">
|
||||||
|
<h3>اندپوینتها — <code>actor/</code></h3>
|
||||||
|
<table>
|
||||||
|
<tr><th style="width:70px">متد</th><th>مسیر</th><th>توضیح</th></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>actor/captcha</code></td><td>صدور یک چالش کپچای ورود جدید (captchaId + تصویر SVG را برمیگرداند).</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>actor/login</code></td><td>احراز هویت هر نقش اکتور. بدنه: role، username/email/nationalCode، password، captchaId، captcha. توکنهای JWT دسترسی + رفرش را برمیگرداند.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>actor/forget-password</code></td><td>ارسال OTP بازنشانی رمز عبور به ایمیل.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>actor/forget-password-verify</code></td><td>تأیید OTP و تنظیم رمز عبور جدید.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>actor/profile</code></td><td>دریافت پروفایل اکتور فعلی.</td></tr>
|
||||||
|
<tr><td><span class="method patch">PATCH</span></td><td><code>actor/profile</code></td><td>بهروزرسانی پروفایل اکتور فعلی.</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer>Made by Sepehr</footer>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
616
docs/panel-roles-reference.html
Normal file
616
docs/panel-roles-reference.html
Normal file
@@ -0,0 +1,616 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>Panel Roles Reference</title>
|
||||||
|
<style>
|
||||||
|
*,
|
||||||
|
*::before,
|
||||||
|
*::after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, "Segoe UI", system-ui, sans-serif;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.6;
|
||||||
|
background: #ffffff;
|
||||||
|
color: #1f2328;
|
||||||
|
padding: 24px;
|
||||||
|
}
|
||||||
|
h1 { font-size: 20px; font-weight: 700; margin-bottom: 4px; }
|
||||||
|
.subtitle { font-size: 13px; color: #57606a; margin-bottom: 28px; }
|
||||||
|
h2 {
|
||||||
|
font-size: 15px; font-weight: 700;
|
||||||
|
margin-bottom: 10px; margin-top: 32px;
|
||||||
|
border-bottom: 1px solid #e5e7eb; padding-bottom: 6px;
|
||||||
|
}
|
||||||
|
h3 {
|
||||||
|
font-size: 12px; font-weight: 700;
|
||||||
|
text-transform: uppercase; letter-spacing: 0.05em;
|
||||||
|
color: #57606a; margin-bottom: 8px; margin-top: 14px;
|
||||||
|
}
|
||||||
|
.section-intro {
|
||||||
|
font-size: 13px; color: #57606a;
|
||||||
|
margin-bottom: 14px; line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Role header strip */
|
||||||
|
.role-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
.role-name {
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
.role-enum {
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 11px;
|
||||||
|
color: #3b82d4;
|
||||||
|
background: #f0f7ff;
|
||||||
|
border: 1px solid #bfdbfe;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 1px 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge {
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 11px; font-weight: 600;
|
||||||
|
padding: 1px 7px; border-radius: 10px;
|
||||||
|
margin-right: 4px; margin-bottom: 3px;
|
||||||
|
}
|
||||||
|
.badge-blue { background: #dbeafe; color: #1d4ed8; }
|
||||||
|
.badge-green { background: #dcfce7; color: #166534; }
|
||||||
|
.badge-purple { background: #ede9fe; color: #5b21b6; }
|
||||||
|
.badge-orange { background: #ffedd5; color: #9a3412; }
|
||||||
|
.badge-teal { background: #ccfbf1; color: #0f766e; }
|
||||||
|
.badge-indigo { background: #e0e7ff; color: #3730a3; }
|
||||||
|
.badge-gray { background: #f1f5f9; color: #475569; border: 1px solid #e2e8f0; }
|
||||||
|
.badge-red { background: #fee2e2; color: #991b1b; }
|
||||||
|
|
||||||
|
/* Cards */
|
||||||
|
.card {
|
||||||
|
border: 1px solid #e5e7eb;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 16px;
|
||||||
|
background: #f7f8fa;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
.card.card-blue { border-left: 4px solid #3b82f6; }
|
||||||
|
.card.card-green { border-left: 4px solid #22c55e; }
|
||||||
|
.card.card-purple { border-left: 4px solid #8b5cf6; }
|
||||||
|
.card.card-orange { border-left: 4px solid #f97316; }
|
||||||
|
.card.card-teal { border-left: 4px solid #14b8a6; }
|
||||||
|
.card.card-indigo { border-left: 4px solid #6366f1; }
|
||||||
|
.card.card-gray { border-left: 4px solid #94a3b8; }
|
||||||
|
|
||||||
|
/* Endpoint tables */
|
||||||
|
table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
width: 100%;
|
||||||
|
font-size: 12px;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
th {
|
||||||
|
background: #f1f5f9; font-weight: 600;
|
||||||
|
text-align: left; padding: 5px 8px;
|
||||||
|
border: 1px solid #e5e7eb;
|
||||||
|
}
|
||||||
|
td {
|
||||||
|
padding: 4px 8px; border: 1px solid #e5e7eb;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
tr:nth-child(even) td { background: #ffffff; }
|
||||||
|
code {
|
||||||
|
font-family: monospace; font-size: 11px; color: #3b82d4;
|
||||||
|
}
|
||||||
|
.method {
|
||||||
|
font-family: monospace; font-size: 11px;
|
||||||
|
font-weight: 700; white-space: nowrap;
|
||||||
|
}
|
||||||
|
.method.get { color: #059669; }
|
||||||
|
.method.post { color: #2563eb; }
|
||||||
|
.method.put { color: #d97706; }
|
||||||
|
.method.patch { color: #7c3aed; }
|
||||||
|
.method.delete { color: #dc2626; }
|
||||||
|
.dep { color: #94a3b8; font-style: italic; font-size: 11px; }
|
||||||
|
|
||||||
|
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
|
||||||
|
@media (max-width: 860px) { .grid-2 { grid-template-columns: 1fr; } }
|
||||||
|
|
||||||
|
.note {
|
||||||
|
font-size: 11px; color: #57606a; font-style: italic;
|
||||||
|
margin-top: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc {
|
||||||
|
background: #f7f8fa; border: 1px solid #e5e7eb;
|
||||||
|
border-radius: 6px; padding: 14px 18px;
|
||||||
|
margin-bottom: 28px;
|
||||||
|
}
|
||||||
|
.toc-title { font-size: 13px; font-weight: 700; margin-bottom: 8px; }
|
||||||
|
.toc ol { padding-left: 18px; }
|
||||||
|
.toc li { font-size: 13px; margin-bottom: 3px; }
|
||||||
|
.toc a { color: #3b82d4; text-decoration: none; }
|
||||||
|
.toc a:hover { text-decoration: underline; }
|
||||||
|
|
||||||
|
footer {
|
||||||
|
text-align: center; font-size: 12px; color: #57606a;
|
||||||
|
border-top: 1px solid #e5e7eb;
|
||||||
|
margin-top: 40px; padding-top: 12px;
|
||||||
|
}
|
||||||
|
.max-wrap { max-width: 760px; margin: 0 auto; }
|
||||||
|
|
||||||
|
/* Role overview table */
|
||||||
|
.overview-table { font-size: 12px; margin-bottom: 24px; }
|
||||||
|
.overview-table th { white-space: nowrap; }
|
||||||
|
.overview-table td:first-child { font-weight: 600; white-space: nowrap; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="max-wrap">
|
||||||
|
<h1>Panel Roles Reference</h1>
|
||||||
|
<p class="subtitle">
|
||||||
|
What every actor role can see and do — endpoints, responsibilities, and
|
||||||
|
process steps. Super-admin excluded.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<!-- Table of Contents -->
|
||||||
|
<div class="toc">
|
||||||
|
<div class="toc-title">Roles covered</div>
|
||||||
|
<ol>
|
||||||
|
<li><a href="#insurer">Insurer (COMPANY) — the insurance-company tenant admin</a></li>
|
||||||
|
<li><a href="#blame-expert">Blame Expert (EXPERT) — disagreement review queue</a></li>
|
||||||
|
<li><a href="#damage-expert">Damage Expert (DAMAGE_EXPERT) — claim pricing</a></li>
|
||||||
|
<li><a href="#field-expert">Field Expert (FIELD_EXPERT) — on-scene in-person filing</a></li>
|
||||||
|
<li><a href="#file-maker">File Maker (FILE_MAKER) — V4/V5 party narrative</a></li>
|
||||||
|
<li><a href="#file-reviewer">File Reviewer (FILE_REVIEWER) — V4/V5 damage assessment</a></li>
|
||||||
|
<li><a href="#registrar">Registrar (REGISTRAR) — office-based filing</a></li>
|
||||||
|
<li><a href="#call-center">Call Center (CALL_CENTER) — V6 phone-initiated filing</a></li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ── Role overview table ────────────────────────────────────── -->
|
||||||
|
<h2>Role Overview</h2>
|
||||||
|
<table class="overview-table">
|
||||||
|
<tr>
|
||||||
|
<th>Role enum</th>
|
||||||
|
<th>Login panel</th>
|
||||||
|
<th>Scope</th>
|
||||||
|
<th>Primary job</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>company</code></td>
|
||||||
|
<td>Insurer portal</td>
|
||||||
|
<td>Tenant-wide</td>
|
||||||
|
<td>View all files; manage branches, experts; run reports; rate experts</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>expert</code></td>
|
||||||
|
<td>Blame expert panel</td>
|
||||||
|
<td>Tenant DISAGREEMENT queue</td>
|
||||||
|
<td>Lock blame cases, review party submissions, submit verdict or request resend</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>damage_expert</code></td>
|
||||||
|
<td>Claim/damage panel</td>
|
||||||
|
<td>Tenant claim queue</td>
|
||||||
|
<td>Lock claims, price damage, validate repair factors, request resend/visit</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>field_expert</code></td>
|
||||||
|
<td>Field expert panel</td>
|
||||||
|
<td>Own created files</td>
|
||||||
|
<td>V2/V3 in-person blame + claim filing; also sees blame/claim review panels</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>file_maker</code></td>
|
||||||
|
<td>FileMaker panel</td>
|
||||||
|
<td>Own created files</td>
|
||||||
|
<td>V4/V5 party narrative (OTPs, inquiries, details, signatures); V5 claim approval</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>file_reviewer</code></td>
|
||||||
|
<td>FileReviewer panel</td>
|
||||||
|
<td>Assigned files</td>
|
||||||
|
<td>V4/V5 damage assessment (accident fields, parts, captures, owner sign)</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>registrar</code></td>
|
||||||
|
<td>Registrar panel</td>
|
||||||
|
<td>Own created files</td>
|
||||||
|
<td>Office-based in-person blame + claim filing on behalf of parties</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>call_center</code></td>
|
||||||
|
<td>Call-center panel</td>
|
||||||
|
<td>Own created files</td>
|
||||||
|
<td>V6 phone-initiated blame: run inquiry, send link; user completes the rest</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||||
|
<h2 id="insurer">1 — Insurer <span class="role-enum">company</span></h2>
|
||||||
|
<p class="section-intro">
|
||||||
|
One <code>company</code> actor per insurance-company tenant. The insurer portal is the
|
||||||
|
management layer: it can see everything under its tenant, manage the expert roster,
|
||||||
|
manage branches, configure per-tenant media settings, and pull statistical reports.
|
||||||
|
The insurer never touches blame/claim steps directly — it only observes and rates.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="card card-blue">
|
||||||
|
<h3>File management — <code>expert-insurer/</code></h3>
|
||||||
|
<table>
|
||||||
|
<tr><th style="width:70px">Method</th><th>Route</th><th>What it does</th></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>expert-insurer/files</code></td><td>List all blame + claim files for the tenant (merged by publicId). Filterable by status, file type, search, sort, page.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>expert-insurer/files/:publicId</code></td><td>Full detail for one file by publicId.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>expert-insurer/files/:publicId/timeline</code></td><td>Chronological activity timeline (all history events: source, type, actor, metadata).</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>expert-insurer/files/:publicId/report</code></td><td>Structured report data for PDF generation (owner, driver, insurance, vehicle, accident sections).</td></tr>
|
||||||
|
<tr><td><span class="method put">PUT</span></td><td><code>expert-insurer/files/:publicId/rating</code></td><td>Rate the experts on a file (1–5 per dimension: collision method, timeliness, cause accuracy, guilty-ID accuracy, bot rating).</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>expert-insurer/report/unified-file-statuses</code></td><td>Unified status catalog + per-status counts for the whole tenant portfolio. Filterable by fileType and date range.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>expert-insurer/report/status-counts</code></td><td class="dep">Deprecated — prefer unified-file-statuses.</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-blue">
|
||||||
|
<h3>Branch management — <code>expert-insurer/branches</code></h3>
|
||||||
|
<table>
|
||||||
|
<tr><th style="width:70px">Method</th><th>Route</th><th>What it does</th></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>expert-insurer/branches</code></td><td>List all branches for this insurer. Query: search, from/to date, isActive filter.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>expert-insurer/branches</code></td><td>Add a new branch (name, code, address, city, phone, etc.).</td></tr>
|
||||||
|
<tr><td><span class="method put">PUT</span></td><td><code>expert-insurer/branches/:branchId/status</code></td><td>Activate or deactivate a branch.</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-blue">
|
||||||
|
<h3>Expert roster management — <code>expert-insurer/experts</code></h3>
|
||||||
|
<table>
|
||||||
|
<tr><th style="width:70px">Method</th><th>Route</th><th>What it does</th></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>expert-insurer/experts/blame</code></td><td>Create a new blame-expert account under this insurer.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>expert-insurer/experts/claim</code></td><td>Create a new damage-expert (claim) account under this insurer.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>expert-insurer/experts/file-maker</code></td><td>Create a new FileMaker account under this insurer.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>expert-insurer/experts/file-reviewer</code></td><td>Create a new FileReviewer account under this insurer.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>expert-insurer/experts/list</code></td><td>Paginated list of all expert accounts on this tenant.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>expert-insurer/experts/top</code></td><td>Top blame vs claim experts ranked by overall average rating (up to 10 each).</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>expert-insurer/top-experts</code></td><td>Alias for experts/top (frontend compat).</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>expert-insurer/:expertId</code></td><td>Files handled by one expert (slim summary rows — blame or claim depending on expert type).</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-blue">
|
||||||
|
<h3>Statistics & reports</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th style="width:70px">Method</th><th>Route</th><th>What it does</th></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>expert-insurer/statistics</code></td><td>KPI cards: totalFilesReviewed, averageUserRating, inPersonCount, filesThisMonth, objectionPercentage, etc. Filterable by date range.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>expert-insurer/top-files</code></td><td>Top 10 highest-rated claim files (combined insurer + user score).</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>expert-insurer/expert-work-log</code></td><td>Per-expert work log: totalHandled, currentlyChecking, distinctFilesCheckedInPeriod. Filterable by expertKind and date range.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>reports/report/insurer/requests</code></td><td>Claim + blame status bucket counts + unified file count for the tenant.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>reports/report/insurer/per-month-requests</code></td><td>Same summary, broken down by the last 5 calendar months.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>reports/report/insurer/checked-requests</code></td><td>Same summary filtered by optional createdAt date range.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>reports/report/insurer/expert-work-log</code></td><td>Expert work log (blame + damage expert collections, not field experts).</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>reports/report/insurer/expert-work-log/per-month</code></td><td>Same work log per calendar month (last 5).</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-blue">
|
||||||
|
<h3>Tenant settings — <code>client-panel/</code></h3>
|
||||||
|
<table>
|
||||||
|
<tr><th style="width:70px">Method</th><th>Route</th><th>What it does</th></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>client-panel/settings</code></td><td>Get per-tenant media limits (video/image/voice maxBytes) and CAR_BODY accident window (days).</td></tr>
|
||||||
|
<tr><td><span class="method patch">PATCH</span></td><td><code>client-panel/settings</code></td><td>Update those settings (partial). Cannot exceed system-level route ceilings.</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||||
|
<h2 id="blame-expert">2 — Blame Expert <span class="role-enum">expert</span></h2>
|
||||||
|
<p class="section-intro">
|
||||||
|
Reviews blame files in the DISAGREEMENT queue — cases where the two parties do not
|
||||||
|
agree on who is at fault. After reviewing submitted documents and party statements
|
||||||
|
the expert locks the case, then either submits a verdict, asks the parties to resend
|
||||||
|
documents, or records an in-person visit outcome. All endpoints are under
|
||||||
|
<code>v2/expert-blame/</code>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="card card-orange">
|
||||||
|
<h3>Process</h3>
|
||||||
|
<p class="note">
|
||||||
|
1 Browse list → 2 Assign (lock) the case → 3 Review party evidence (videos, voices, documents) →
|
||||||
|
4a Submit verdict <em>or</em> 4b Request document resend <em>or</em> 4c Record in-person visit.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-orange">
|
||||||
|
<h3>Endpoints — <code>v2/expert-blame/</code></h3>
|
||||||
|
<table>
|
||||||
|
<tr><th style="width:70px">Method</th><th>Route</th><th>What it does</th></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>v2/expert-blame/</code></td><td>List blame cases in the DISAGREEMENT queue (available, locked by me, or decided by me). Query: search, sortBy, sortOrder, page, limit, unifiedStatus, fileType.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>v2/expert-blame/:id</code></td><td>Full detail for one blame case (party statements, photos, voices, videos).</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>v2/expert-blame/:id/assign</code></td><td>Check availability and lock the case to this expert. Returns <code>assigned</code>, <code>already_assigned_to_you</code>, or 409 if someone else holds it.</td></tr>
|
||||||
|
<tr><td><span class="method put">PUT</span></td><td><code>v2/expert-blame/reply/submit/:id</code></td><td>Submit verdict (accidentWay, accidentReason, accidentType, guilty party decision). Unlocks the case and moves it to COMPLETED.</td></tr>
|
||||||
|
<tr><td><span class="method put">PUT</span></td><td><code>v2/expert-blame/reply/resend/:id</code></td><td>Request parties to re-upload documents. Sets blame to WAITING_FOR_RESEND. One resend request per lifecycle.</td></tr>
|
||||||
|
<tr><td><span class="method put">PUT</span></td><td><code>v2/expert-blame/reply/inPerson/:id</code></td><td>Record that an in-person visit was made and submit verdict.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>v2/expert-blame/report/unified-file-statuses</code></td><td>Status catalog + per-status counts for this expert's portfolio.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>v2/expert-blame/report/status-counts</code></td><td class="dep">Deprecated — prefer unified-file-statuses.</td></tr>
|
||||||
|
<tr><td><span class="method put">PUT</span></td><td><code>v2/expert-blame/lock/:id</code></td><td class="dep">Deprecated lock endpoint — use POST assign.</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||||
|
<h2 id="damage-expert">3 — Damage Expert <span class="role-enum">damage_expert</span></h2>
|
||||||
|
<p class="section-intro">
|
||||||
|
Reviews claim files after the user has submitted their damage evidence. The expert
|
||||||
|
prices each damaged part, optionally calculates a price-drop (depreciation), and
|
||||||
|
can ask the user to resend documents, come in person, or upload repair factor invoices
|
||||||
|
when workshop pricing is needed. All endpoints are under <code>v2/expert-claim/</code>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="card card-red">
|
||||||
|
<h3>Process</h3>
|
||||||
|
<p class="note">
|
||||||
|
1 Browse list → 2 Assign (lock) the claim → 3 Review damage photos and documents →
|
||||||
|
4 Optionally edit selected parts or calculate price-drop →
|
||||||
|
5a Submit priced reply <em>or</em> 5b Request resend <em>or</em> 5c Request in-person visit →
|
||||||
|
6 If factor parts present: validate uploaded factor invoices.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-red">
|
||||||
|
<h3>Endpoints — <code>v2/expert-claim/</code></h3>
|
||||||
|
<table>
|
||||||
|
<tr><th style="width:70px">Method</th><th>Route</th><th>What it does</th></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>v2/expert-claim/requests</code></td><td>List claims in <code>WAITING_FOR_DAMAGE_EXPERT</code> queue + factor-validation queue. Query: search, sortBy, page, limit, unifiedStatus, fileType.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>v2/expert-claim/request/:claimRequestId</code></td><td>Full claim detail: damaged parts, captured images, documents, priceDrop, blameCase party data, video URLs.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>v2/expert-claim/assign/:claimRequestId</code></td><td>Lock claim to this expert. Returns <code>assigned</code>, <code>already_assigned_to_you</code>, or 409.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>v2/expert-claim/request/:claimRequestId/price-drop</code></td><td>Price-drop context: severity labels, coefficient catalog, damaged parts + mapping, suggested car year from blame inquiry.</td></tr>
|
||||||
|
<tr><td><span class="method put">PUT</span></td><td><code>v2/expert-claim/request/:claimRequestId/price-drop</code></td><td>Calculate and persist price-drop: carPrice × yearCoeff × sumOfCoeffs ÷ 400.</td></tr>
|
||||||
|
<tr><td><span class="method put">PUT</span></td><td><code>v2/expert-claim/reply/submit/:claimRequestId</code></td><td>Submit damage assessment reply (priced parts list, daghi, branchId). Cap: total ≤ 53 000 000 Toman. Depending on factorNeeded flags moves claim to owner-sign, mixed-factors-pending, or factor-validation queue.</td></tr>
|
||||||
|
<tr><td><span class="method put">PUT</span></td><td><code>v2/expert-claim/reply/resend/:claimRequestId</code></td><td>Request user to resend documents/photos. One resend per claim lifecycle; returns 422 if already fulfilled.</td></tr>
|
||||||
|
<tr><td><span class="method patch">PATCH</span></td><td><code>v2/expert-claim/:claimRequestId/visit</code></td><td>Ask user to come in person. Unlocks claim, sets claimStatus to NEEDS_REVISION.</td></tr>
|
||||||
|
<tr><td><span class="method patch">PATCH</span></td><td><code>v2/expert-claim/validate-factors/:claimRequestId</code></td><td>Validate uploaded repair factor invoices. Approve or reject each factor line with totalPayment. Cap applies across all lines (≤ 53 000 000 Toman). Auto-completes when all lines are decided.</td></tr>
|
||||||
|
<tr><td><span class="method patch">PATCH</span></td><td><code>v2/expert-claim/request/:claimRequestId/damaged-parts</code></td><td>Edit selected damaged parts while the claim is locked by this expert (EXPERT_REVIEWING).</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>v2/expert-claim/outer-parts-catalog</code></td><td>Fanavaran outer car-components catalog (shared with user flow).</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>v2/expert-claim/inner-parts-catalog</code></td><td>Static inner car-parts catalog JSON.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>v2/expert-claim/branches</code></td><td>Insurer branches for this expert's tenant (for daghi/branch selection in reply payload).</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>v2/expert-claim/stream/:id/video</code></td><td>Stream claim video (car-capture walk-around or accident video). Query: <code>query=car-capture|accident</code>.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>v2/expert-claim/report/unified-file-statuses</code></td><td>Status catalog + counts for this expert's claim portfolio.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>v2/expert-claim/report/status-counts</code></td><td class="dep">Deprecated — prefer unified-file-statuses.</td></tr>
|
||||||
|
<tr><td><span class="method put">PUT</span></td><td><code>v2/expert-claim/lock/:claimRequestId</code></td><td class="dep">Deprecated lock endpoint — use POST assign.</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||||
|
<h2 id="field-expert">4 — Field Expert <span class="role-enum">field_expert</span></h2>
|
||||||
|
<p class="section-intro">
|
||||||
|
Goes to the accident scene and fills both parties' forms in-person (V2 mirror / V3 flows).
|
||||||
|
The field expert also has read access to the expert-blame and expert-claim panels
|
||||||
|
(scoped to their own files). They are the only role that spans both
|
||||||
|
<strong>blame filing</strong> and <strong>claim filing</strong> in the same session.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="card card-green">
|
||||||
|
<h3>Blame filing — <code>v2/expert-initiated/blame-request-management/</code></h3>
|
||||||
|
<p class="note">Mirror of the user blame API. Frontend reuses same pages by swapping prefix only.</p>
|
||||||
|
<table>
|
||||||
|
<tr><th style="width:70px">Method</th><th>Route</th><th>What it does</th></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>POST /</code></td><td>Create IN_PERSON blame file.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>send-party-otp/:id</code></td><td>Send OTP to one party by phone number (no invite link).</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>verify-party-otp/:id</code></td><td>Verify one party's OTP and bind their account.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>blame-confession/:id</code></td><td>Record party's blame confession.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>car-body-form/:id</code></td><td>[CAR_BODY only] Accident type form.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>run-inquiries/:id</code> / <code>run-inquiries-vin/:id</code></td><td>Initial form / plate or VIN inquiry for current party.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>upload-video/:id</code></td><td>Upload first-party video.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>add-detail-location/:id</code></td><td>Add GPS location for current party.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>upload-voice/:id</code></td><td>Upload voice recording for current party.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>add-detail-description/:id</code></td><td>Add description for current party.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>add-second-party/:phone/:id/</code></td><td>Advance to second party (no SMS link sent).</td></tr>
|
||||||
|
<tr><td><span class="method put">PUT</span></td><td><code>sign/:id</code></td><td>Upload party signature (FIRST then SECOND, partyRole param).</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>accident-fields/:id</code></td><td>Save accident fields and complete blame immediately (no expert queue).</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-green">
|
||||||
|
<h3>V3 blame + claim filing — <code>v3/expert-initiated/blame-request-management/</code></h3>
|
||||||
|
<p class="note">Reorganised step order: all party narrative first, then damage assessment. Blame and claim both handled in this single controller.</p>
|
||||||
|
<table>
|
||||||
|
<tr><th style="width:70px">Method</th><th>Route</th><th>What it does</th></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>POST /</code> → <code>send-party-otp</code> → <code>verify-party-otp</code> → <code>run-inquiries</code> → <code>add-detail-*</code> → <code>sign</code> (×2)</td><td>Party narrative phase (steps 1–8) — identical endpoints to mirror, same contract.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>accident-fields/:id</code></td><td>Step 9: save accident fields after both parties have signed.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>claim-id/:requestId</code></td><td>Step 10: get auto-created claim ID.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>upload-document/:claimId</code></td><td>Step 11: upload licence / car card documents.</td></tr>
|
||||||
|
<tr><td><span class="method patch">PATCH</span></td><td><code>select-outer-parts/:claimId</code> / <code>select-other-parts/:claimId</code></td><td>Steps 12–13: select damaged parts.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>capture-part/:claimId</code></td><td>Step 14: capture part photos + angles.</td></tr>
|
||||||
|
<tr><td><span class="method patch">PATCH</span></td><td><code>car-capture/:claimId</code></td><td>Step 15: walk-around video.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>upload-video/:requestId</code></td><td>Step 16: blame accident video (final) → WAITING_FOR_EXPERT (THIRD_PARTY) or COMPLETED (CAR_BODY).</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-green">
|
||||||
|
<h3>Expert-blame + expert-claim panel (read + action on own files)</h3>
|
||||||
|
<p class="note">
|
||||||
|
FIELD_EXPERT sees <code>v2/expert-blame/</code> scoped to their own created files (not the disagreement queue).
|
||||||
|
They also see <code>v2/expert-claim/</code> for claims linked to their blame files.
|
||||||
|
Same endpoints as blame-expert and damage-expert panels above.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||||
|
<h2 id="file-maker">5 — File Maker <span class="role-enum">file_maker</span></h2>
|
||||||
|
<p class="section-intro">
|
||||||
|
The first actor in the V4/V5 split. FileMaker handles the party narrative on-site:
|
||||||
|
OTPs, inquiries, location/description/voice, and signatures for both parties.
|
||||||
|
They also upload the initial claim documents (licences, car cards). After the second
|
||||||
|
signature the file is "sealed" for FileReviewer pickup. In V5, FileMaker comes
|
||||||
|
back at the end to approve or reject the completed claim before fanavaran submission.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="card card-purple">
|
||||||
|
<h3>Blame filing — <code>v4/file-maker/blame-request-management/</code> and <code>v5/…</code></h3>
|
||||||
|
<p class="note">V4 and V5 endpoints are identical — only the prefix changes. V5 sets <code>requiresFileMakerApproval=true</code> at creation.</p>
|
||||||
|
<table>
|
||||||
|
<tr><th style="width:70px">Method</th><th>Route</th><th>What it does</th></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>POST /</code></td><td>Create IN_PERSON blame file.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>my-files</code></td><td>List all blame files created by this FileMaker.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>my-files/:requestId</code></td><td>Full detail for one file (parties, workflow, linked claim ID).</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>claim-id/:requestId</code></td><td>Get the auto-created claim ID after guilty-party run-inquiries.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>send-party-otp/:id</code> / <code>verify-party-otp/:id</code></td><td>Send + verify OTP for one party at a time (guilty first, then damaged).</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>car-body-form/:id</code></td><td>[CAR_BODY only] Accident type form.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>run-inquiries/:id</code> / <code>run-inquiries-vin/:id</code></td><td>Run plate or VIN inquiry. First call = guilty (+ auto-creates claim). Second call = damaged (THIRD_PARTY only).</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>add-detail-location/:id</code> / <code>add-detail-description/:id</code> / <code>upload-voice/:id</code></td><td>Add location, description, and voice for current party (partyRole param selects FIRST/SECOND).</td></tr>
|
||||||
|
<tr><td><span class="method put">PUT</span></td><td><code>sign/:id</code></td><td>Upload party signature (partyRole=FIRST then SECOND). After second signature, file is sealed.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>upload-document/:claimId</code></td><td>Upload licences / car cards against the auto-created claim.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>capture-requirements/:claimId</code></td><td>Step-aware capture requirements (phases: pre-capture docs vs damaged parts + chassis/engine).</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-purple">
|
||||||
|
<h3>V5 claim approval — <code>v5/file-maker/claim-approval/</code></h3>
|
||||||
|
<p class="note">Used only in V5. After damage expert review and owner sign, claim enters <code>WAITING_FOR_FILE_MAKER_APPROVAL</code>.</p>
|
||||||
|
<table>
|
||||||
|
<tr><th style="width:70px">Method</th><th>Route</th><th>What it does</th></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>approve/:claimId</code></td><td>Approve the completed claim → triggers owner sign SMS → fanavaran submission after owner signs.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>reject/:claimId</code></td><td>Reject back to FileReviewer → claim returns to WAITING_FOR_DAMAGE_EXPERT. Limit: max 2 rejections per claim; 3rd attempt returns 422 <code>FILE_MAKER_REJECTION_LIMIT_EXCEEDED</code>.</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||||
|
<h2 id="file-reviewer">6 — File Reviewer <span class="role-enum">file_reviewer</span></h2>
|
||||||
|
<p class="section-intro">
|
||||||
|
The second actor in the V4/V5 split. FileReviewer picks up sealed files (after
|
||||||
|
FileMaker is done) and performs the full damage assessment pass: accident fields,
|
||||||
|
capture requirements lookup, document upload (chassis/engine), part selection,
|
||||||
|
part photos, walk-around video, owner signature. The blame is marked COMPLETED
|
||||||
|
by car-capture. FileReviewer also has read access to the expert-claim panel for
|
||||||
|
claims they are reviewing.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="card card-teal">
|
||||||
|
<h3>Damage assessment — <code>v4/file-reviewer/blame-request-management/</code> and <code>v5/…</code></h3>
|
||||||
|
<p class="note">V4 and V5 endpoints are identical — only the prefix changes.</p>
|
||||||
|
<table>
|
||||||
|
<tr><th style="width:70px">Method</th><th>Route</th><th>What it does</th></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>my-files</code></td><td>List all files assigned to this FileReviewer.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>my-files/:requestId</code></td><td>Full detail for one file (parties, workflow, expert fields, linked claim ID).</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>claim-id/:requestId</code></td><td>Get the auto-created claim ID (from FileMaker's guilty-party inquiry).</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>accident-fields/:requestId</code></td><td>Step 1 (FileReviewer): save accident fields (accidentWay, accidentReason, accidentType).</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>capture-requirements/:claimId</code></td><td>Step-aware capture requirements (pre-capture docs phase vs capture-parts phase).</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>upload-document/:claimId</code></td><td>Upload chassis / engine / metal-plate documents.</td></tr>
|
||||||
|
<tr><td><span class="method patch">PATCH</span></td><td><code>select-outer-parts/:claimId</code></td><td>Select outer (body) damaged parts.</td></tr>
|
||||||
|
<tr><td><span class="method patch">PATCH</span></td><td><code>select-other-parts/:claimId</code></td><td>Select other (non-body) damaged parts.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>capture-part/:claimId</code></td><td>Capture part photos + angles for each selected damaged part.</td></tr>
|
||||||
|
<tr><td><span class="method patch">PATCH</span></td><td><code>car-capture/:claimId</code></td><td>Walk-around video (final FileReviewer capture step). Claim → <code>WAITING_FOR_DAMAGE_EXPERT</code>, blame → <code>COMPLETED</code>.</td></tr>
|
||||||
|
<tr><td><span class="method put">PUT</span></td><td><code>claim-sign/:claimId</code></td><td>Submit owner signature on expert pricing on behalf of the damaged party (agree + branchId + signature image).</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>upload-video/:requestId</code></td><td class="dep">No-op in V4/V5 — blame already COMPLETED by car-capture. Returns idempotent success.</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-teal">
|
||||||
|
<h3>Expert-claim panel access</h3>
|
||||||
|
<p class="note">
|
||||||
|
FILE_REVIEWER is in the allowed roles for <code>v2/expert-claim/</code>.
|
||||||
|
They can view claim details and run the assign/lock flow for claims associated
|
||||||
|
with their files. They cannot initiate a damage-expert resend independently.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||||
|
<h2 id="registrar">7 — Registrar <span class="role-enum">registrar</span></h2>
|
||||||
|
<p class="section-intro">
|
||||||
|
Office-based role that files in-person blame and claim on behalf of parties.
|
||||||
|
Uses a bulk-OTP flow (both parties' OTPs sent and verified in one call each)
|
||||||
|
rather than the one-at-a-time OTP used by field experts. After blame, the
|
||||||
|
registrar mirrors the user claim API to fill part selection, documents, and
|
||||||
|
captures. The file then enters the normal damage-expert review lifecycle.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="card card-gray">
|
||||||
|
<h3>Blame filing — <code>registrar-initiated-blame/</code></h3>
|
||||||
|
<p class="note">Note: <code>@ApiExcludeController</code> — routes exist but not surfaced in Swagger docs.</p>
|
||||||
|
<table>
|
||||||
|
<tr><th style="width:70px">Method</th><th>Route</th><th>What it does</th></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>registrar-initiated-blame/create</code></td><td>Create IN_PERSON blame file.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>registrar-initiated-blame/my-files</code></td><td>List all blame files created by this registrar.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>registrar-initiated-blame/blame/:requestId</code></td><td>Full detail for one blame file.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>registrar-initiated-blame/send-party-otps/:id</code></td><td>Send OTPs to both parties simultaneously.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>registrar-initiated-blame/verify-party-otps/:id</code></td><td>Verify both parties' OTPs in one call.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>registrar-initiated-blame/complete-blame-data/:id</code></td><td>Submit all blame form data for both parties in one payload.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>registrar-initiated-blame/upload-video/:id</code></td><td>Upload blame video.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>registrar-initiated-blame/upload-voice/:id</code></td><td>Upload voice recording.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>registrar-initiated-blame/add-accident-fields/:id</code></td><td>Save accident fields and complete blame.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>registrar-initiated-blame/upload-party-signature/:id</code></td><td>Upload a party's signature (partyRole=FIRST/SECOND).</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-gray">
|
||||||
|
<h3>Claim filing — <code>v2/registrar/claim-request-management/</code></h3>
|
||||||
|
<p class="note">Mirror of the user claim API. Frontend reuses same claim pages by swapping prefix only.</p>
|
||||||
|
<table>
|
||||||
|
<tr><th style="width:70px">Method</th><th>Route</th><th>What it does</th></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>create-from-blame/:blameId</code></td><td>Create claim from a completed blame file.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>outer-parts-catalog</code> / <code>car-other-part</code></td><td>Parts catalogs (outer body parts + other parts JSON).</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>branches/:insuranceId</code></td><td>Insurer branch list (for branch selection in claim sign step).</td></tr>
|
||||||
|
<tr><td><span class="method patch">PATCH</span></td><td><code>select-outer-parts/:claimId</code></td><td>Select outer damaged parts.</td></tr>
|
||||||
|
<tr><td><span class="method patch">PATCH</span></td><td><code>select-other-parts/:claimId</code></td><td>Select other damaged parts + bank info.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>upload-document/:claimId</code></td><td>Upload claim documents (licences, car card).</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>capture-part/:claimId</code></td><td>Capture part photos + angles.</td></tr>
|
||||||
|
<tr><td><span class="method patch">PATCH</span></td><td><code>car-capture/:claimId</code></td><td>Walk-around video (final step) → WAITING_FOR_DAMAGE_EXPERT.</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||||
|
<h2 id="call-center">8 — Call Center <span class="role-enum">call_center</span></h2>
|
||||||
|
<p class="section-intro">
|
||||||
|
Handles V6 phone-initiated blame filing. The agent collects the guilty party's
|
||||||
|
data over the phone, runs the insurance inquiry, and sends the blame link via
|
||||||
|
SMS. The user then completes the rest of the form through the standard V2 flow
|
||||||
|
(with the initial-form/inquiry step skipped). The call-center agent's job ends
|
||||||
|
after send-link; they can monitor progress via the read endpoints.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="card card-indigo">
|
||||||
|
<h3>Endpoints — <code>v6/call-center-blame/</code></h3>
|
||||||
|
<table>
|
||||||
|
<tr><th style="width:70px">Method</th><th>Route</th><th>What it does</th></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>create</code></td><td>Create a LINK blame file. Body: <code>{ type: "THIRD_PARTY" | "CAR_BODY" }</code>.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>run-inquiry/:requestId</code></td><td>Run plate + national-code insurance inquiry for the guilty party. Stores result on blame document.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>run-inquiry-vin/:requestId</code></td><td>VIN/chassis alternative to run-inquiry. Uses ESG chassis lookup.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>send-link/:requestId</code></td><td>Register user if needed, store as first party, send blame invite link via SMS. Body: <code>{ phoneNumber }</code>.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>my-files</code></td><td>List all blame files started by this agent.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>blame/:requestId</code></td><td>Current status and workflow step for one file (to check if user has opened the link and progressed).</td></tr>
|
||||||
|
</table>
|
||||||
|
<p class="note" style="margin-top:8px;">
|
||||||
|
After <code>send-link</code> the user completes the form via
|
||||||
|
<code>v2/blame-request-management/</code> (standard V2 flow).
|
||||||
|
The initial-form / inquiry step is automatically skipped
|
||||||
|
(<code>skipInitialFormStep=true</code>). Downstream claim flow is the
|
||||||
|
standard V2 claim flow.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ═══════════════════════════════════════════════════════════ -->
|
||||||
|
<h2>Shared: Actor Authentication</h2>
|
||||||
|
<p class="section-intro">
|
||||||
|
All panel actors (every role except <code>user</code>) authenticate through the same
|
||||||
|
<code>POST actor/login</code> endpoint with captcha. Password reset is via email OTP.
|
||||||
|
Profile reads and edits are also shared.
|
||||||
|
</p>
|
||||||
|
<div class="card card-gray">
|
||||||
|
<h3>Endpoints — <code>actor/</code></h3>
|
||||||
|
<table>
|
||||||
|
<tr><th style="width:70px">Method</th><th>Route</th><th>What it does</th></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>actor/captcha</code></td><td>Issue a new login captcha challenge (returns captchaId + SVG image).</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>actor/login</code></td><td>Authenticate any actor role. Body: role, username/email/nationalCode, password, captchaId, captcha. Returns JWT access + refresh tokens.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>actor/forget-password</code></td><td>Send password-reset OTP to email.</td></tr>
|
||||||
|
<tr><td><span class="method post">POST</span></td><td><code>actor/forget-password-verify</code></td><td>Verify OTP and set new password.</td></tr>
|
||||||
|
<tr><td><span class="method get">GET</span></td><td><code>actor/profile</code></td><td>Get current actor's profile.</td></tr>
|
||||||
|
<tr><td><span class="method patch">PATCH</span></td><td><code>actor/profile</code></td><td>Update current actor's profile.</td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer>Made by Sepehr</footer>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -10797,16 +10797,41 @@ export class ClaimRequestManagementService {
|
|||||||
}
|
}
|
||||||
claims = [...byId.values()];
|
claims = [...byId.values()];
|
||||||
}
|
}
|
||||||
const list = (claims as any[]).map((c) => ({
|
const blameIdsForList = [
|
||||||
claimRequestId: c._id.toString(),
|
...new Set(
|
||||||
publicId: c.publicId,
|
(claims as any[])
|
||||||
requestNo: c.requestNo,
|
.map((c) => c.blameRequestId?.toString())
|
||||||
status: c.status,
|
.filter((id): id is string => !!id),
|
||||||
claimStatus: c.claimStatus || "PENDING",
|
),
|
||||||
currentStep: c.workflow?.currentStep || "",
|
];
|
||||||
createdAt: c.createdAt,
|
const blamesForList =
|
||||||
blameRequestId: c.blameRequestId?.toString(),
|
blameIdsForList.length > 0
|
||||||
})) as ClaimListItemV2Dto[];
|
? ((await this.blameRequestDbService.find(
|
||||||
|
{ _id: { $in: blameIdsForList.map((id) => new Types.ObjectId(id)) } },
|
||||||
|
{ lean: true, select: "type creationMethod" },
|
||||||
|
)) as any[])
|
||||||
|
: [];
|
||||||
|
const blameByIdForList = new Map<string, any>(
|
||||||
|
blamesForList.map((b) => [String(b._id), b]),
|
||||||
|
);
|
||||||
|
|
||||||
|
const list = (claims as any[]).map((c) => {
|
||||||
|
const blameForItem = c.blameRequestId
|
||||||
|
? blameByIdForList.get(c.blameRequestId.toString())
|
||||||
|
: undefined;
|
||||||
|
return {
|
||||||
|
claimRequestId: c._id.toString(),
|
||||||
|
publicId: c.publicId,
|
||||||
|
requestNo: c.requestNo,
|
||||||
|
status: c.status,
|
||||||
|
claimStatus: c.claimStatus || "PENDING",
|
||||||
|
currentStep: c.workflow?.currentStep || "",
|
||||||
|
createdAt: c.createdAt,
|
||||||
|
blameRequestId: c.blameRequestId?.toString(),
|
||||||
|
blameType: blameForItem?.type ?? undefined,
|
||||||
|
creationMethod: blameForItem?.creationMethod ?? undefined,
|
||||||
|
};
|
||||||
|
}) as ClaimListItemV2Dto[];
|
||||||
|
|
||||||
const paged = applyListQueryV2(
|
const paged = applyListQueryV2(
|
||||||
list,
|
list,
|
||||||
@@ -10856,6 +10881,15 @@ export class ClaimRequestManagementService {
|
|||||||
}
|
}
|
||||||
await this.assertActorCanViewClaimV2(claim, currentUserId, actor);
|
await this.assertActorCanViewClaimV2(claim, currentUserId, actor);
|
||||||
|
|
||||||
|
const blameForDetail = claim.blameRequestId
|
||||||
|
? ((
|
||||||
|
await this.blameRequestDbService.find(
|
||||||
|
{ _id: new Types.ObjectId(claim.blameRequestId.toString()) },
|
||||||
|
{ lean: true, select: "type creationMethod" },
|
||||||
|
)
|
||||||
|
)[0] ?? null)
|
||||||
|
: null;
|
||||||
|
|
||||||
const hasCapture = (data: any, key: string) =>
|
const hasCapture = (data: any, key: string) =>
|
||||||
data && (data instanceof Map ? data.get(key) : data[key]);
|
data && (data instanceof Map ? data.get(key) : data[key]);
|
||||||
|
|
||||||
@@ -11011,6 +11045,8 @@ export class ClaimRequestManagementService {
|
|||||||
nextStep: claim.workflow?.nextStep,
|
nextStep: claim.workflow?.nextStep,
|
||||||
blameRequestId: claim.blameRequestId?.toString(),
|
blameRequestId: claim.blameRequestId?.toString(),
|
||||||
blameRequestNo: claim.blameRequestNo,
|
blameRequestNo: claim.blameRequestNo,
|
||||||
|
blameType: (blameForDetail as any)?.type ?? undefined,
|
||||||
|
creationMethod: (blameForDetail as any)?.creationMethod ?? undefined,
|
||||||
...(ownerData ? { owner: ownerData } : {}),
|
...(ownerData ? { owner: ownerData } : {}),
|
||||||
vehicle: claim.vehicle,
|
vehicle: claim.vehicle,
|
||||||
selectedParts: selectedNormDetails,
|
selectedParts: selectedNormDetails,
|
||||||
|
|||||||
@@ -122,6 +122,18 @@ export class ClaimDetailsV2ResponseDto {
|
|||||||
@ApiPropertyOptional({ description: 'Blame request number' })
|
@ApiPropertyOptional({ description: 'Blame request number' })
|
||||||
blameRequestNo?: string;
|
blameRequestNo?: string;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
description: 'Blame file type: THIRD_PARTY or CAR_BODY',
|
||||||
|
example: 'THIRD_PARTY',
|
||||||
|
})
|
||||||
|
blameType?: string;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
description: 'How the blame file was initiated: IN_PERSON or LINK',
|
||||||
|
example: 'IN_PERSON',
|
||||||
|
})
|
||||||
|
creationMethod?: string;
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
@ApiPropertyOptional({
|
||||||
description: 'Claim owner (damaged party): ids for the user and their insurer client scope',
|
description: 'Claim owner (damaged party): ids for the user and their insurer client scope',
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -28,6 +28,18 @@ export class ClaimListItemV2Dto {
|
|||||||
|
|
||||||
@ApiProperty({ description: 'Blame request ID this claim originated from' })
|
@ApiProperty({ description: 'Blame request ID this claim originated from' })
|
||||||
blameRequestId?: string;
|
blameRequestId?: string;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
description: 'Blame file type: THIRD_PARTY or CAR_BODY',
|
||||||
|
example: 'THIRD_PARTY',
|
||||||
|
})
|
||||||
|
blameType?: string;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
description: 'How the blame file was initiated: IN_PERSON or LINK',
|
||||||
|
example: 'IN_PERSON',
|
||||||
|
})
|
||||||
|
creationMethod?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class GetMyClaimsV2ResponseDto {
|
export class GetMyClaimsV2ResponseDto {
|
||||||
|
|||||||
@@ -116,6 +116,8 @@ export class AllRequestDtoV2 {
|
|||||||
lockFile: boolean;
|
lockFile: boolean;
|
||||||
lockTime: string | null;
|
lockTime: string | null;
|
||||||
type: string;
|
type: string;
|
||||||
|
/** IN_PERSON or LINK — how the blame file was created */
|
||||||
|
creationMethod?: string;
|
||||||
blameStatus: string;
|
blameStatus: string;
|
||||||
/** Calculated blame + linked claim lifecycle status */
|
/** Calculated blame + linked claim lifecycle status */
|
||||||
unifiedFileStatus?: string;
|
unifiedFileStatus?: string;
|
||||||
|
|||||||
@@ -762,6 +762,7 @@ export class ExpertBlameService {
|
|||||||
? new Date(workflow.lockedAt as string).toISOString()
|
? new Date(workflow.lockedAt as string).toISOString()
|
||||||
: null,
|
: null,
|
||||||
type: String(doc.type ?? "THIRD_PARTY"),
|
type: String(doc.type ?? "THIRD_PARTY"),
|
||||||
|
creationMethod: doc.creationMethod ? String(doc.creationMethod) : undefined,
|
||||||
blameStatus: String(doc.blameStatus ?? ""),
|
blameStatus: String(doc.blameStatus ?? ""),
|
||||||
partiesInitialForms: {
|
partiesInitialForms: {
|
||||||
firstParty: statementToFormKey(firstParty?.statement) ?? "",
|
firstParty: statementToFormKey(firstParty?.statement) ?? "",
|
||||||
|
|||||||
@@ -52,6 +52,12 @@ export class ClaimDetailV2ResponseDto {
|
|||||||
})
|
})
|
||||||
blameRequestType?: BlameRequestType;
|
blameRequestType?: BlameRequestType;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
description: "How the blame file was initiated: IN_PERSON or LINK",
|
||||||
|
example: "IN_PERSON",
|
||||||
|
})
|
||||||
|
creationMethod?: string;
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
@ApiPropertyOptional({
|
||||||
description:
|
description:
|
||||||
"CAR_BODY only: first-step flags — another car (`car`) and/or object (`object`)",
|
"CAR_BODY only: first-step flags — another car (`car`) and/or object (`object`)",
|
||||||
|
|||||||
@@ -49,6 +49,12 @@ export class ClaimListItemV2Dto {
|
|||||||
})
|
})
|
||||||
blameRequestType?: BlameRequestType;
|
blameRequestType?: BlameRequestType;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
description: 'How the blame file was initiated: IN_PERSON or LINK',
|
||||||
|
example: 'IN_PERSON',
|
||||||
|
})
|
||||||
|
creationMethod?: string;
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
@ApiPropertyOptional({
|
||||||
description:
|
description:
|
||||||
'CAR_BODY only: first-step flags — accident involved another car (`car`) and/or fixed object (`object`)',
|
'CAR_BODY only: first-step flags — accident involved another car (`car`) and/or fixed object (`object`)',
|
||||||
|
|||||||
@@ -629,6 +629,7 @@ export class ExpertClaimService {
|
|||||||
*/
|
*/
|
||||||
private blameFileContextForExpert(blame: any): {
|
private blameFileContextForExpert(blame: any): {
|
||||||
blameRequestType?: BlameRequestType;
|
blameRequestType?: BlameRequestType;
|
||||||
|
creationMethod?: string;
|
||||||
carBodyFirstForm?: { car?: boolean; object?: boolean };
|
carBodyFirstForm?: { car?: boolean; object?: boolean };
|
||||||
blameStatus?: string;
|
blameStatus?: string;
|
||||||
} {
|
} {
|
||||||
@@ -636,9 +637,11 @@ export class ExpertClaimService {
|
|||||||
const blameRequestType = blame.type as BlameRequestType;
|
const blameRequestType = blame.type as BlameRequestType;
|
||||||
const out: {
|
const out: {
|
||||||
blameRequestType?: BlameRequestType;
|
blameRequestType?: BlameRequestType;
|
||||||
|
creationMethod?: string;
|
||||||
carBodyFirstForm?: { car?: boolean; object?: boolean };
|
carBodyFirstForm?: { car?: boolean; object?: boolean };
|
||||||
blameStatus?: string;
|
blameStatus?: string;
|
||||||
} = { blameRequestType };
|
} = { blameRequestType };
|
||||||
|
if (blame.creationMethod) out.creationMethod = blame.creationMethod;
|
||||||
out.blameStatus = blame.blameStatus;
|
out.blameStatus = blame.blameStatus;
|
||||||
if (blameRequestType !== BlameRequestType.CAR_BODY) return out;
|
if (blameRequestType !== BlameRequestType.CAR_BODY) return out;
|
||||||
|
|
||||||
@@ -4017,7 +4020,7 @@ export class ExpertClaimService {
|
|||||||
blameIds.length > 0
|
blameIds.length > 0
|
||||||
? ((await this.blameRequestDbService.find(
|
? ((await this.blameRequestDbService.find(
|
||||||
{ _id: { $in: blameIds.map((id) => new Types.ObjectId(id)) } },
|
{ _id: { $in: blameIds.map((id) => new Types.ObjectId(id)) } },
|
||||||
{ lean: true, select: "type parties expert.decision blameStatus status isMadeByFileMaker" },
|
{ lean: true, select: "type creationMethod parties expert.decision blameStatus status isMadeByFileMaker" },
|
||||||
)) as any[])
|
)) as any[])
|
||||||
: [];
|
: [];
|
||||||
const blameById = new Map<string, any>(
|
const blameById = new Map<string, any>(
|
||||||
@@ -4117,7 +4120,7 @@ export class ExpertClaimService {
|
|||||||
blameIds.length > 0
|
blameIds.length > 0
|
||||||
? ((await this.blameRequestDbService.find(
|
? ((await this.blameRequestDbService.find(
|
||||||
{ _id: { $in: blameIds.map((id) => new Types.ObjectId(id)) } },
|
{ _id: { $in: blameIds.map((id) => new Types.ObjectId(id)) } },
|
||||||
{ lean: true, select: "type parties expert.decision blameStatus status" },
|
{ lean: true, select: "type creationMethod parties expert.decision blameStatus status" },
|
||||||
)) as any[])
|
)) as any[])
|
||||||
: [];
|
: [];
|
||||||
const blameById = new Map<string, any>(
|
const blameById = new Map<string, any>(
|
||||||
@@ -4212,7 +4215,7 @@ export class ExpertClaimService {
|
|||||||
{
|
{
|
||||||
lean: true,
|
lean: true,
|
||||||
select:
|
select:
|
||||||
"_id type parties blameStatus status expert.decision assignedFileReviewerId requiresFileMakerApproval",
|
"_id type creationMethod parties blameStatus status expert.decision assignedFileReviewerId requiresFileMakerApproval",
|
||||||
},
|
},
|
||||||
)) as any[];
|
)) as any[];
|
||||||
|
|
||||||
@@ -4303,7 +4306,7 @@ export class ExpertClaimService {
|
|||||||
|
|
||||||
const makerBlames = (await this.blameRequestDbService.find(
|
const makerBlames = (await this.blameRequestDbService.find(
|
||||||
{ isMadeByFileMaker: true, initiatedByFieldExpertId: makerOid },
|
{ isMadeByFileMaker: true, initiatedByFieldExpertId: makerOid },
|
||||||
{ lean: true, select: "_id type parties blameStatus status expert.decision assignedFileReviewerId requiresFileMakerApproval" },
|
{ lean: true, select: "_id type creationMethod parties blameStatus status expert.decision assignedFileReviewerId requiresFileMakerApproval" },
|
||||||
)) as any[];
|
)) as any[];
|
||||||
|
|
||||||
if (makerBlames.length === 0) {
|
if (makerBlames.length === 0) {
|
||||||
|
|||||||
@@ -1498,6 +1498,8 @@ export class ExpertInsurerService {
|
|||||||
(blame as any)?.requestNumber ||
|
(blame as any)?.requestNumber ||
|
||||||
(claim as any)?.requestNumber,
|
(claim as any)?.requestNumber,
|
||||||
type: (blame as any)?.type ?? (blameFull as any)?.type,
|
type: (blame as any)?.type ?? (blameFull as any)?.type,
|
||||||
|
creationMethod:
|
||||||
|
(blame as any)?.creationMethod ?? (blameFull as any)?.creationMethod,
|
||||||
hasClaim: !!claim,
|
hasClaim: !!claim,
|
||||||
blameStatus: (blame as any)?.status ?? (blameFull as any)?.status,
|
blameStatus: (blame as any)?.status ?? (blameFull as any)?.status,
|
||||||
claimStatus: (claim as any)?.status,
|
claimStatus: (claim as any)?.status,
|
||||||
|
|||||||
@@ -6116,6 +6116,7 @@ export class RequestManagementService {
|
|||||||
requestNo: plain.requestNo,
|
requestNo: plain.requestNo,
|
||||||
publicId: plain.publicId,
|
publicId: plain.publicId,
|
||||||
type: plain.type,
|
type: plain.type,
|
||||||
|
creationMethod: plain.creationMethod,
|
||||||
status: plain.status,
|
status: plain.status,
|
||||||
blameStatus: plain.blameStatus,
|
blameStatus: plain.blameStatus,
|
||||||
workflow: plain.workflow,
|
workflow: plain.workflow,
|
||||||
|
|||||||
Reference in New Issue
Block a user