Added Docs

This commit is contained in:
SepehrYahyaee
2026-09-12 17:02:12 +03:30
parent ed9ef3bac3
commit 401ad6a143
7 changed files with 979 additions and 2 deletions

103
docs/architecture.md Normal file
View File

@@ -0,0 +1,103 @@
docs/
│
├── README.md
│
├── architecture/
│ ├── overview.md
│ ├── principles.md
│ ├── layers.md
│ ├── modules.md
│ ├── workflow-engine.md
│ ├── event-driven.md
│ ├── database.md
│ ├── caching.md
│ ├── authentication.md
│ ├── authorization.md
│ ├── file-storage.md
│ ├── id-generation.md
│ ├── error-handling.md
│ ├── logging.md
│ └── diagrams/
│
├── adr/
│ ├── 0001-use-mongodb.md
│ ├── 0002-use-workflow-engine.md
│ ├── ...
│
├── engineering/
│ ├── structure.md
│ ├── coding-style.md
│ ├── naming.md
│ ├── comments.md
│ ├── exceptions.md
│ ├── validation.md
│ ├── dto-guidelines.md
│ ├── repositories.md
│ ├── services.md
│ ├── controllers.md
│ ├── testing.md
│ ├── code-review.md
│ ├── gitflow.md
│ ├── commit-convention.md
│ ├── branching.md
│ ├── dependency-rules.md
│ ├── security.md
│ └── performance.md
│
├── domain/
│ ├── glossary.md
│ ├── insurance-concepts.md
│ ├── entities.md
│ ├── events.md
│ ├── workflows.md
│ ├── business-rules.md
│ └── state-transitions.md
│
├── flows/
│ ├── company-a/
│ ├── company-b/
│ ├── company-c/
│ └── common/
│
├── api/
│ ├── rest.md
│ ├── versioning.md
│ ├── pagination.md
│ ├── errors.md
│ └── examples/
│
├── deployment/
│ ├── docker.md
│ ├── environments.md
│ ├── ci.md
│ ├── cd.md
│ ├── backups.md
│ └── monitoring.md
│
├── onboarding/
│ ├── setup.md
│ ├── first-day.md
│ ├── debugging.md
│ ├── faq.md
│ └── common-mistakes.md
│
├── operations/
│ ├── runbooks.md
│ ├── incident-response.md
│ ├── rca/
│ ├── postmortems/
│ └── troubleshooting.md
│
├── backlog/
│ ├── ideas.md
│ ├── technical-debt.md
│ ├── future-features.md
│ └── experiments.md
│
├── decisions/
│ ├── rejected-ideas.md
│ ├── deprecated.md
│ └── migration-plans.md
│
├── changelog.md
└── roadmap.md

View File

@@ -0,0 +1,228 @@
# راهنمای فرانت‌اند: ثبت پاسخ کارشناس خسارت (V2)
این مستند قرارداد API زیر را توضیح می‌دهد، به‌ویژه اعتبارسنجی مبلغ‌ها و ساختار خطاهایی که باید در فرم نمایش داده شوند.
```http
PUT /v2/expert-claim/reply/submit/:claimRequestId
Authorization: Bearer <actor-token>
Content-Type: application/json
```
اگر محیط شما API را زیر پیشوند `/api` منتشر می‌کند، مسیر نهایی به‌صورت `/api/v2/expert-claim/reply/submit/:claimRequestId` است.
## پیش‌نیازها
- کاربر باید نقش کارشناس مجاز داشته باشد.
- پرونده باید پیش از ارسال توسط همین کارشناس قفل شده باشد.
- `partId` هر ردیف باید از `damagedParts[].partId` در جزئیات پرونده انتخاب شود؛ شناسه دلخواه ارسال نکنید.
- دست‌کم یک ردیف در `parts` لازم است.
## بدنه درخواست
تمام مبلغ‌ها باید **string** باشند. ارقام فارسی/انگلیسی و جداکننده هزارگان پذیرفته می‌شوند؛ برای نمونه هر دو مقدار `"100000"` و `"۱۰۰,۰۰۰"` معتبرند.
```json
{
"description": "تعویض درب جلو لازم است",
"parts": [
{
"partId": 201,
"typeOfDamage": "تعویض",
"price": "2500000",
"salary": "400000",
"totalPayment": "2900000",
"factorNeeded": false,
"daghi": {
"option": "ارزش لوازم بازیافتی",
"price": "300000"
}
}
]
}
```
### فیلدهای سطح بالا
| فیلد | الزامی | توضیح |
| --- | --- | --- |
| `description` | خیر | یادداشت متنی کارشناس. می‌تواند ارسال نشود. |
| `parts` | بله | آرایه‌ای با حداقل یک ردیف قیمت‌گذاری. |
### فیلدهای هر ردیف `parts[i]`
| فیلد | الزامی | مقدار و قاعده |
| --- | --- | --- |
| `partId` | بله | عدد صحیح؛ باید متعلق به قطعات آسیب‌دیده همان پرونده باشد. |
| `typeOfDamage` | بله | دقیقاً یکی از `"تعمیر"` یا `"تعویض"`. |
| `price` | برای `"تعویض"` بله؛ برای `"تعمیر"` اختیاری | در صورت ارسال، مبلغ بین 100,000 تا 10,000,000,000 تومان. |
| `salary` | بله | مبلغ بین 100,000 تا 10,000,000,000 تومان. |
| `totalPayment` | بله | مبلغ بین 100,000 تا 10,000,000,000 تومان. مجموع این فیلدها در کل پرونده نباید از 53,000,000 تومان بیشتر شود. |
| `factorNeeded` | بله | مقدار Boolean واقعی (`true` یا `false`)؛ رشته ارسال نکنید. |
| `daghi` | برای `"تعویض"` بله | برای `"تعمیر"` لازم نیست و در ثبت نهایی حذف می‌شود. |
| `daghi.option` | در صورت وجود `daghi` بله | یکی از `"ارزش لوازم بازیافتی"`، `"تحویل داغی"`، `"فاقد ارزش"` یا `"با احتساب داغی"`. |
| `daghi.price` | فقط وقتی `option` برابر `"ارزش لوازم بازیافتی"` است | مبلغ بین 100,000 تا 10,000,000,000 تومان. |
| `daghi.branchId` | فقط وقتی `option` برابر `"تحویل داغی"` است | Mongo ObjectId معتبرِ شعبه. |
نکته‌ها:
- مقدار `0` برای هیچ مبلغ ارسالی این endpoint معتبر نیست؛ حداقل مبلغ 100,000 تومان است.
- محدودیت 10 میلیارد مربوط به **هر فیلد مبلغ** است؛ سقف 53 میلیون مربوط به **مجموع `totalPayment` تمام ردیف‌ها** است و همچنان اعمال می‌شود.
- فیلدهای ناشناخته در body حذف می‌شوند. فرانت‌اند نباید برای انتقال داده به آن‌ها تکیه کند.
## پاسخ موفق
پاسخ `200` شامل مسیر بعدی workflow است. نمونه:
```json
{
"claimRequestId": "66c...",
"status": "COMPLETED",
"claimStatus": "APPROVED",
"currentStep": "CLAIM_COMPLETED",
"workflowNextStep": "CLAIM_COMPLETED",
"factorNeeded": false,
"mixedPricingAndFactors": false,
"allPartsFactorNeeded": false,
"isFinalReplyAfterObjection": false
}
```
مقادیر `status` و step به این وابسته‌اند که ردیف factor داشته باشید یا نه؛ فرانت‌اند باید از مقادیر پاسخ استفاده کند، نه اینکه مسیر بعدی را فقط از payload حدس بزند.
## قرارداد خطا
همه خطاهای این endpoint JSON هستند و NestJS مقدار `statusCode` را نیز به پاسخ اضافه می‌کند. برای نمایش پیام به کاربر از `message` استفاده کنید و برای منطق برنامه از `code` استفاده کنید؛ متن فارسی را با متن ثابت در فرانت‌اند جایگزین نکنید.
### 1. خطای ساختار DTO — `400`
برای نبودن فیلد الزامی، نوع اشتباه، enum نامعتبر، آرایه خالی، یا فرمت مبلغ نامعتبر، پاسخ زیر برمی‌گردد:
```json
{
"statusCode": 400,
"message": "اطلاعات ارسالی پاسخ کارشناسی معتبر نیست.",
"error": "EXPERT_REPLY_VALIDATION_ERROR",
"code": "EXPERT_REPLY_VALIDATION_ERROR",
"validationErrors": [
{
"field": "parts[0].salary",
"message": "باید مبلغ صحیح و غیرمنفی به تومان باشد."
},
{
"field": "parts[0].factorNeeded",
"message": "باید درست یا نادرست باشد."
}
]
}
```
`validationErrors` ممکن است چند خطا داشته باشد. کلید `field` دقیقاً برای اتصال به کنترل فرم است؛ مانند `parts[0].daghi.price` یا `parts[2].partId`.
### 2. خطای قواعد مبلغ و قیمت‌گذاری — `400`
این خطاها پس از اعتبارسنجی ساختار و پیش از تغییر وضعیت پرونده بررسی می‌شوند. برای مثال، مبلغ کمتر از حداقل:
```json
{
"statusCode": 400,
"message": "قیمت قطعه 201 باید حداقل ۱۰۰٬۰۰۰ و حداکثر ۱۰٬۰۰۰٬۰۰۰٬۰۰۰ تومان باشد.",
"error": "EXPERT_REPLY_VALIDATION_ERROR",
"code": "EXPERT_REPLY_VALIDATION_ERROR",
"field": "parts[0].price",
"partId": "201",
"rule": "amount_out_of_range",
"minAmount": 100000,
"maxAmount": 10000000000
}
```
`rule` یکی از این مقادیر است:
| مقدار | معنی |
| --- | --- |
| `required` | فیلد لازم ارسال نشده است. |
| `invalid_value` | مقدار enum یا شناسه معتبر نیست. |
| `invalid_amount` | مبلغ، عدد صحیح غیرمنفی به تومان نیست. |
| `amount_out_of_range` | مبلغ خارج از بازه `minAmount` و `maxAmount` است. |
در صورت `amount_out_of_range`، برای نمایش محدوده از اعداد `minAmount` و `maxAmount` استفاده کنید، نه از parse کردن متن `message`.
### 3. سقف مجموع مبلغ‌ها — `400`
```json
{
"statusCode": 400,
"message": "مجموع مبلغ قطعات (۵۴٬۰۰۰٬۰۰۰) از سقف مجاز (۵۳٬۰۰۰٬۰۰۰) تومان بیشتر است.",
"error": "PRICE_CAP_ERROR",
"code": "PRICE_CAP_ERROR",
"totalPrice": 54000000,
"priceCap": 53000000
}
```
این خطا به یک ردیف مشخص وصل نیست. آن را در بالای جدول قیمت‌ها نمایش دهید و در صورت نیاز از `priceCap` برای پیام UI استفاده کنید.
### 4. خطاهای قواعد پرونده و workflow
این خطاها ساختار مشترک زیر را دارند:
```json
{
"statusCode": 400,
"message": "قطعه با شناسه 201 در فهرست قطعات آسیب‌دیده پرونده وجود ندارد.",
"error": "EXPERT_REPLY_SUBMISSION_ERROR",
"code": "PART_NOT_ON_CLAIM",
"field": "partId",
"partId": 201
}
```
کدهای مهم:
| HTTP | `code` | رفتار پیشنهادی فرانت‌اند |
| --- | --- | --- |
| 404 | `CLAIM_NOT_FOUND` | پیام خطا و بازگشت به فهرست پرونده‌ها. |
| 400 | `CLAIM_NOT_REVIEWABLE` | جزئیات پرونده را refresh کنید؛ ثبت در وضعیت فعلی مجاز نیست. |
| 403 | `CLAIM_NOT_LOCKED` | کاربر باید ابتدا پرونده را قفل کند. |
| 403 | `CLAIM_LOCKED_BY_ANOTHER_EXPERT` | فرم را read-only کنید و پیام مناسب نمایش دهید. |
| 400 | `PART_NOT_ON_CLAIM` | داده قطعات را refresh کنید و ردیف خطادار را اصلاح/حذف کنید. |
| 400 | `PART_INVALID` / `PART_ID_INVALID` | ردیف دارای `partId` را اصلاح کنید. |
| 400 | `DUPLICATE_PART` | ردیف تکراری را حذف کنید. |
| 400 | `DAGHI_OPTION_REQUIRED` | فیلد `daghi.option` همان ردیف را نشان‌دار کنید. |
| 400 | `DAGHI_PRICE_REQUIRED` | فیلد `daghi.price` همان ردیف را نشان‌دار کنید. |
| 400 | `DAGHI_BRANCH_REQUIRED` / `DAGHI_BRANCH_INVALID` | فیلد `daghi.branchId` همان ردیف را نشان‌دار کنید. |
| 409 | `FINAL_REPLY_ALREADY_SUBMITTED` | ثبت مجدد ممنوع است؛ داده پرونده را refresh کنید. |
برای خطاهای داغی که از سرویس برمی‌گردند، `field` به شکل `parts[<index>].daghi.<property>` است.
## الگوی پیشنهادی هندل کردن خطا در فرانت‌اند
```ts
type ApiErrorBody = {
message?: string;
code?: string;
field?: string;
validationErrors?: Array<{ field: string; message: string }>;
minAmount?: number;
maxAmount?: number;
priceCap?: number;
};
function applySubmitError(body: ApiErrorBody) {
if (body.validationErrors?.length) {
for (const issue of body.validationErrors) {
setFieldError(issue.field, issue.message);
}
return;
}
if (body.field) {
setFieldError(body.field, body.message ?? "مقدار واردشده معتبر نیست.");
return;
}
setFormError(body.message ?? "ثبت پاسخ کارشناسی انجام نشد.");
}
```
قبل از ارسال، فرانت‌اند می‌تواند همین بازه مبلغ را برای تجربه کاربری بهتر بررسی کند؛ با این حال اعتبار نهایی همیشه پاسخ API است. برای جلوگیری از خطای سقف مجموع، جمع `totalPayment` ردیف‌ها را نیز پیش از ارسال محاسبه و نمایش دهید.

View File

@@ -0,0 +1,109 @@
# پیشنهاد مدل اشخاص در مرحله استعلام
وضعیت: پیشنهادی
دامنه: تمام جریان‌های استعلام کاربر، کارشناس، پرونده‌ساز و مرکز تماس
نسخه انگلیسی: [inquiry-participants-proposal.md](./inquiry-participants-proposal.md)
## مسئله
قرارداد فعلی استعلام عمدتاً فقط راننده و شخصی با عنوان `insurer` را نگه می‌دارد. این مدل کامل نیست و نام‌گذاری نیز دقیق نیست: شخص، **بیمه‌گذار** است و **بیمه‌گر** شرکت بیمه است.
برای هر وسیله نقلیه در یک `Party` نقش‌های هویتی زیر وجود دارد:
| نوع پرونده | نقش‌های الزامی |
| --- | --- |
| `THIRD_PARTY` | راننده، مالک وسیله نقلیه، بیمه‌گذار شخص ثالث |
| `CAR_BODY` | راننده، مالک وسیله نقلیه، بیمه‌گذار شخص ثالث، بیمه‌گذار بدنه |
ممکن است یک شخص چند نقش را داشته باشد، اما سیستم نباید یکسان بودن آن‌ها را فرض کند.
## پیشنهاد
پیش از اجرای استعلام، یک **مرحله کوتاه تعیین اشخاص** اضافه شود. ابتدا نسبت اشخاص پرسیده شود و اطلاعات فقط برای افراد متفاوت دریافت شود. دریافت بدون شرط اطلاعات کامل همه اشخاص مناسب نیست. همچنین افزودن فلگ‌های دوتایی متعدد مانند `driverIsOwner` و `ownerIsPolicyholder` باعث ابهام، تناقض و رشد سریع حالت‌ها می‌شود.
به‌جای آن، هر نقش یا اطلاعات یک شخص جدید را داشته باشد یا به نقش قبلی ارجاع دهد:
```json
{
"driver": {
"nationalCode": "0012345678",
"birthday": "1370/01/01",
"licenseNumber": "123456789",
"licenseType": "1"
},
"vehicleOwner": { "sameAs": "DRIVER" },
"thirdPartyPolicyholder": { "sameAs": "VEHICLE_OWNER" },
"carBodyPolicyholder": {
"nationalCode": "0098765432",
"birthday": "1365/02/03"
}
}
```
فیلد `carBodyPolicyholder` برای `THIRD_PARTY` مجاز نیست و برای `CAR_BODY` الزامی است. هر نقش باید فقط یکی از دو حالت «اطلاعات شخص» یا `sameAs` را داشته باشد. بک‌اند این ورودی را به فهرست اشخاص یکتا و اتصال نقش‌ها به آن‌ها تبدیل می‌کند.
## انتقال مالکیت اخیر و پلاک قبلی
نقش اشخاص و شناسه‌های خودرو دو موضوع جدا هستند. اگر خودرو به‌تازگی فروخته یا خریداری شده باشد، ممکن است اطلاعات رسمی یا بیمه‌نامه هنوز به پلاک قبلی متصل باشد. این وضعیت باید صریح ثبت شود و پلاک قبلی نباید جایگزین پلاک فعلی شود:
```json
{
"vehicle": {
"registrationState": "RECENTLY_TRANSFERRED",
"currentPlate": {
"leftDigits": "44",
"centerAlphabet": "ب",
"centerDigits": "111",
"ir": "22"
},
"previousPlate": {
"leftDigits": "55",
"centerAlphabet": "ج",
"centerDigits": "222",
"ir": "33"
},
"vin": "NAAM01E15HK123456"
}
}
```
مقدار پیش‌فرض `registrationState` برابر `CURRENT` است و برای این مسیر استثنایی مقدار `RECENTLY_TRANSFERRED` استفاده می‌شود. `previousPlate` فقط در حالت انتقال اخیر الزامی است. پلاک فعلی همچنان شناسه اصلی خودرو است. هماهنگ‌کننده استعلام باید ابتدا پلاک فعلی را بررسی کند و اگر نتیجه ناموجود، قدیمی یا فاقد بیمه‌نامه مرتبط بود، پلاک قبلی را به‌صورت خودکار استعلام کند.
پیش از پذیرش نتیجه پلاک قبلی، بک‌اند باید یکسان بودن VIN/شماره شاسی را بررسی کند. در صورت مغایرت، انتخاب خودکار متوقف و اصلاح اطلاعات یا بررسی دستی الزامی شود. هر دو پلاک و تمام تلاش‌های استعلام برای ممیزی نگه‌داری شوند، اما پلاک قبلی هیچ‌گاه نباید روی پلاک فعلی نوشته شود.
## ترتیب پیشنهادی فرم
1. پلاک فعلی دریافت و درباره انتقال مالکیت اخیر پرسیده شود. در صورت انتقال اخیر، پلاک قبلی و VIN/شماره شاسی نیز دریافت شوند.
2. اطلاعات هویتی و گواهینامه راننده دریافت شود.
3. پرسیده شود آیا مالک خودرو همان راننده است؛ فقط در صورت تفاوت، اطلاعات مالک دریافت شود.
4. برای بیمه‌گذار شخص ثالث یکی از «راننده»، «مالک» یا «شخص دیگر» انتخاب شود؛ فقط برای شخص دیگر فرم جدید نمایش داده شود.
5. در `CAR_BODY` همین انتخاب برای بیمه‌گذار بدنه انجام شود و امکان انتخاب هر شخص ثبت‌شده یا شخص دیگر وجود داشته باشد.
6. خلاصه اشخاص نمایش داده شود و سپس استعلام‌ها اجرا شوند.
به این ترتیب مسیر رایج کوتاه می‌ماند و همه ترکیب‌های معتبر نیز پشتیبانی می‌شوند.
## محل منطق در بک‌اند
یک resolver مشترک برای اشخاص ساخته شود و تمام routeهای استعلام از آن استفاده کنند. interface این ماژول باید:
- نقش‌های لازم را بر اساس نوع پرونده اعتبارسنجی و ارجاع‌های نامعتبر یا حلقوی `sameAs` را رد کند؛
- شخص نهایی هر نقش را برگرداند؛
- هویت درست را به استعلام مرتبط بدهد: گواهینامه ← راننده، مالکیت ← مالک خودرو، بیمه شخص ثالث ← بیمه‌گذار شخص ثالث، بیمه بدنه ← بیمه‌گذار بدنه؛
- بر اساس یک قاعده مشخص، پلاک فعلی یا قبلی را انتخاب و نتیجه پلاک قبلی را با VIN/شماره شاسی تطبیق دهد؛
- استعلام هویت را برای هر شخص یکتا فقط یک بار اجرا کند؛
- اشخاص نرمال‌شده و نقش‌های آن‌ها را در `Party` مربوط ذخیره کند.
جریان‌های V2 کاربر/کارشناس، V3، V4، V5 و V6 باید adapter همین قوانین مشترک باشند و منطق نسبت اشخاص را جداگانه پیاده‌سازی نکنند.
## سازگاری و انتشار تدریجی
1. در دوره گذار، ساختار جدید در کنار فیلدهای قدیمی پذیرفته شود.
2. `nationalCodeOfDriver` به راننده و `nationalCodeOfInsurer` به بیمه‌گذار شخص ثالث نگاشت شود. اگر `driverIsInsurer=true` است، هر دو نقش به یک شخص متصل شوند.
3. `plate` یا `plateId` قدیمی به پلاک فعلی نگاشت شود؛ پلاک قبلی فقط وقتی ثبت شود که صریحاً از کاربر دریافت شده باشد.
4. برای داده‌های قدیمی، مالک خودرو یا بیمه‌گذار بدنه حدس زده نشود؛ مگر اینکه نتیجه استعلام ذخیره‌شده آن را قطعی کند، نقش «نامشخص» باقی بماند.
5. پاسخ جزئیات و گزارش، اشخاص را بر اساس نقش نمایش دهد و در زمان مهاجرت فیلدهای قدیمی را نیز حفظ کند.
6. پس از مهاجرت همه فرانت‌اندها، قرارداد جدید برای پرونده‌های تازه الزامی و فیلدهای قدیمی deprecated شوند.
## تصمیم پیشنهادی
راه‌حل مناسب، **دریافت شرطی اطلاعات همراه با اتصال صریح نقش‌ها** است. این روش بدون طولانی کردن مسیر اکثر کاربران، اطلاعات کامل فراهم می‌کند، از تناقض فلگ‌ها جلوگیری می‌کند و یک مدل یکسان برای همه جریان‌های استعلام می‌سازد.

View File

@@ -0,0 +1,109 @@
# Inquiry participant identity proposal
Status: proposed
Scope: every user, expert, FileMaker, and call-center inquiry flow
Persian version: [inquiry-participants-proposal.fa.md](./inquiry-participants-proposal.fa.md)
## Problem
The current inquiry contract mainly models a driver and a value named `insurer`. That is incomplete and the name is misleading: a person is the **policyholder**; the **insurer** is the insurance company.
Each vehicle-side `Party` can have these identity roles:
| Case type | Required roles |
| --- | --- |
| `THIRD_PARTY` | Driver, vehicle owner, third-party policyholder |
| `CAR_BODY` | Driver, vehicle owner, third-party policyholder, car-body policyholder |
One person may hold several roles, but the system must not assume that they do.
## Recommendation
Add a short **participant-identification step before inquiry**. Ask relationship questions and collect details only for distinct people. Do not ask for every person's complete data unconditionally, and do not add pairwise flags such as `driverIsOwner`, `ownerIsPolicyholder`, and `driverIsBodyPolicyholder`; that becomes ambiguous and grows combinatorially.
Use explicit role references instead:
```json
{
"driver": {
"nationalCode": "0012345678",
"birthday": "1370/01/01",
"licenseNumber": "123456789",
"licenseType": "1"
},
"vehicleOwner": { "sameAs": "DRIVER" },
"thirdPartyPolicyholder": { "sameAs": "VEHICLE_OWNER" },
"carBodyPolicyholder": {
"nationalCode": "0098765432",
"birthday": "1365/02/03"
}
}
```
`carBodyPolicyholder` is forbidden for `THIRD_PARTY` and required for `CAR_BODY`. A role is either a new person's identity or a `sameAs` reference, never both. The backend should normalize this input into unique participants plus role assignments.
## Recent ownership transfer and previous plate
Participant roles and vehicle identifiers are separate concerns. When a vehicle has recently been sold or purchased, the current official record or policy may still be connected to its previous plate. Model this explicitly instead of replacing the current plate:
```json
{
"vehicle": {
"registrationState": "RECENTLY_TRANSFERRED",
"currentPlate": {
"leftDigits": "44",
"centerAlphabet": "ب",
"centerDigits": "111",
"ir": "22"
},
"previousPlate": {
"leftDigits": "55",
"centerAlphabet": "ج",
"centerDigits": "222",
"ir": "33"
},
"vin": "NAAM01E15HK123456"
}
}
```
`registrationState` is `CURRENT` by default or `RECENTLY_TRANSFERRED` for this exceptional path. `previousPlate` is required only when `registrationState=RECENTLY_TRANSFERRED`. The current plate remains the vehicle's primary identifier. The inquiry orchestrator should query the current plate first and automatically try the previous plate when the current result is missing, stale, or does not find the relevant policy.
Before accepting a previous-plate result, the backend must correlate it to the same VIN/chassis. A mismatch must stop automatic selection and require correction or manual review. Both identifiers and every attempted inquiry should be retained for audit, but a previous plate must never overwrite the current plate.
## Suggested UI sequence
1. Collect the current plate and ask whether the vehicle was recently transferred. If yes, collect the previous plate and VIN/chassis.
2. Collect driver identity and licence details.
3. Ask whether the vehicle owner is the driver; collect owner identity only when different.
4. Ask whether the third-party policyholder is the driver, the owner, or another person; collect identity only for “another person”.
5. For `CAR_BODY`, ask the same question for the car-body policyholder, allowing any already entered person or another person.
6. Show a short review, then run the inquiries.
This keeps the common case fast while representing all valid combinations.
## Backend seam
Create one shared participant resolver used by every inquiry route. Its interface should:
- validate required roles by case type and reject circular/invalid `sameAs` references;
- return the resolved person for each role;
- route the correct identity to each inquiry: driver licence → Driver, ownership → Vehicle Owner, third-party policy → Third-party Policyholder, car-body policy → Car-body Policyholder;
- choose the current or previous plate deterministically and verify previous-plate results against VIN/chassis;
- run personal identity inquiry once per distinct person;
- persist normalized participants and role assignments on the relevant `Party`.
V2 user/expert routes, V3, V4, V5, and V6 should be adapters over this shared rule set rather than implementing their own relationship logic.
## Compatibility and rollout
1. Accept the new participant object alongside the legacy fields temporarily.
2. Map legacy `nationalCodeOfDriver` to Driver and `nationalCodeOfInsurer` to Third-party Policyholder. When `driverIsInsurer=true`, bind both roles to the same participant.
3. Map the legacy `plate`/`plateId` to Current Plate; leave Previous Plate absent unless it was explicitly collected.
4. Do not guess Vehicle Owner or Car-body Policyholder for old records. Mark unresolved roles as unknown unless stored inquiry evidence identifies them.
5. Update report/detail responses to expose people by role while retaining legacy fields during migration.
6. After all frontends use the new step, require the new contract for newly created cases and deprecate the legacy fields.
## Decision
Prefer **conditional collection plus explicit role assignments**. It provides complete data without burdening most users, prevents contradictory booleans, and gives all inquiry flows one consistent domain model.

View File

@@ -0,0 +1,26 @@
# اطلاعات پنل‌ها و PDF
## مواردی که نبود و اضافه شد
- نام شخص در پنل‌های فلو ۴ و ۵
- کد ملی بیمه‌گذار و راننده در پنل‌های فلو ۴ و ۵
- مشخص‌بودن یکسان‌بودن راننده و بیمه‌گذار در فلو ۴ و ۵
- نوع، شماره و تاریخ گواهینامه در پنل‌های فلو ۴ و ۵
- تاریخ تولد بیمه‌گذار و راننده در پنل‌های فلو ۴ و ۵
- پلاک و VIN در پنل‌های فلو ۴ و ۵
- شماره شبا در پنل‌های فلو ۴ و ۵
- کدهای تکمیلی فناوران در پنل‌ها: بیمه‌نامه، راننده، نوع خودرو، نوع/نمونه پلاک، کاربری خودرو و شرکت بیمه
- نمایش نوع واقعی گواهینامه، مثل «پایه یک»، در PDF
## مواردی که از قبل وجود داشت
- نام و کد ملی افراد در پنل‌های کارشناس و بیمه‌گر
- کد ملی راننده، در صورت متفاوت‌بودن با بیمه‌گذار
- شماره شبا در پنل بیمه‌گر و PDF
- اطلاعات راننده در PDF، وقتی راننده با بیمه‌گذار متفاوت است
- پلاک خودرو در API؛ فرانت باید از `vehicle.plateId` بخواند
- کدهای اصلی فناوران: شماره خسارت، شماره پرونده، شناسه پرونده خسارت و شناسه کارشناسی
## موردی که هنوز نداریم
- `expedited` → در بک‌اند فیلد و قرارداد API ندارد.

View File

@@ -367,7 +367,7 @@
<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. A priced-only claim completes immediately; factor claims continue through factor collection/validation. No final owner signature or automatic Fanavaran submission.</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 and daghi). <code>daghi.branchId</code> is used only with the <code>تحویل داغی</code> option. Cap: total ≤ 53 000 000 Toman. A priced-only claim completes immediately; factor claims continue through factor collection/validation. No final owner signature or automatic Fanavaran submission.</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>
@@ -552,7 +552,7 @@
<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 get">GET</span></td><td><code>branches/:insuranceId</code></td><td>Insurer branch list for <code>daghi.branchId</code> when the expert selects the <code>تحویل داغی</code> option.</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>

View File

@@ -0,0 +1,402 @@
# مستند فرانت‌اند API گزارش PDF پرونده
## هدف API
این API داده‌ی ساخت‌یافته‌ی لازم برای تولید PDF پرونده در پنل بیمه‌گر را برمی‌گرداند.
خروجی آن ترکیبی از اطلاعات پرونده‌ی تقصیر (`blame`) و پرونده‌ی خسارت (`claim`) است و طوری طراحی شده که فرانت‌اند بدون وابستگی به مدل‌های داخلی بک‌اند، فقط با `sections` و `fields` بتواند PDF را رندر کند.
---
## آدرس API
```http
GET /expert-insurer/files/:publicId/report
```
### پارامتر مسیر
- `publicId`: شناسه عمومی پرونده
---
## ساختار کلی پاسخ
```json
{
"title": "گزارش پرونده بیمه گر",
"publicId": "RPT832-00406",
"requestNo": "BL-RPT832-000406",
"sections": []
}
```
### فیلدهای سطح بالا
#### `title`
عنوان کلی گزارش.
#### `publicId`
شناسه عمومی پرونده.
#### `requestNo`
شماره درخواست، اگر در داده‌های پرونده موجود باشد.
#### `sections`
آرایه‌ای از سکشن‌های گزارش.
هر سکشن یک عنوان دارد و شامل تعدادی ردیف اطلاعات (`fields`) است.
---
## ساختار هر سکشن
```json
{
"title": "زمان‌بندی پرونده",
"fields": [
{
"label": "تاریخ و ساعت ثبت پرونده",
"value": "1405/06/02 07:02"
}
]
}
```
### `title`
عنوان فارسی سکشن، آماده‌ی نمایش در PDF.
### `fields`
لیست ردیف‌های اطلاعاتی همان سکشن.
---
## ساختار هر فیلد
```json
{
"label": "شماره بیمه‌نامه",
"value": "POL-12345"
}
```
### `label`
عنوان فارسی فیلد.
### `value`
مقدار فیلد.
برای نمایش مستقیم در PDF استفاده می‌شود.
---
## سکشن‌های ممکن در پاسخ
سکشن‌ها معمولاً با ترتیب زیر برمی‌گردند، ولی فرانت‌اند بهتر است به‌جای تکیه بر ایندکس آرایه، سکشن را با `title` پیدا کند:
1. `زمان‌بندی پرونده`
2. `مالک خودروی زیان دیده`
3. `مالک خودروی مقصر`
4. `راننده خودروی زیان دیده`
5. `بیمه شخص ثالث زیان‌دیده`
6. `بیمه بدنه زیان‌دیده`
7. `بیمه شخص ثالث مقصر`
8. `بیمه بدنه مقصر`
9. `اطلاعات خودروی زیان‌دیده`
10. `اطلاعات خودروی مقصر`
11. `اظهارات و اقرار زیان‌دیده`
12. `اظهارات و اقرار مقصر`
13. `کدهای فناوران`
14. `نتیجه ارزیابی`
15. `گزارش حادثه`
نکته:
- بعضی سکشن‌ها بسته به نوع پرونده ممکن است وجود نداشته باشند.
- در پرونده‌های بدنه (`CAR_BODY`) اگر طرفین عملاً یک نفر باشند، سکشن‌های مربوط به مقصر ممکن است حذف شوند.
- در پرونده‌های شخص ثالث (`THIRD_PARTY`) انتظار می‌رود اطلاعات هر دو طرف به‌صورت تفکیک‌شده برگردد.
---
# توضیح سکشن‌ها
## 1) `زمان‌بندی پرونده`
برای نمایش زمان‌های مهم پرونده.
فیلدهای مهم:
- `تاریخ و ساعت ثبت پرونده`
- `تاریخ و ساعت ثبت نتیجه ارزیابی`
نکته:
- این تاریخ‌ها در بک‌اند فرمت شده‌اند و آماده‌ی نمایش هستند.
---
## 2) `مالک خودروی زیان دیده`
اطلاعات مالک یا صاحب خودروی زیان‌دیده.
فیلدهای رایج:
- `نام`
- `شماره تلفن`
- `کد ملی`
- `تاریخ تولد`
- `شماره شبا`
نکته:
- `شماره شبا` معمولاً برای زیان‌دیده مهم است و ممکن است فقط در همین سکشن وجود داشته باشد.
---
## 3) `مالک خودروی مقصر`
اطلاعات مالک خودروی مقصر.
فیلدهای رایج:
- `نام`
- `شماره تلفن`
- `کد ملی`
- `تاریخ تولد`
نکته:
- این سکشن مخصوص پرونده‌های شخص ثالث اهمیت دارد تا اطلاعات مالک هر دو طرف در PDF موجود باشد.
---
## 4) `راننده خودروی زیان دیده`
اگر راننده با مالک/بیمه‌گذار متفاوت باشد، این سکشن برمی‌گردد.
فیلدهای رایج:
- `نام`
- `نوع گواهینامه`
- `تاریخ گواهینامه`
- `شماره تلفن`
- `کد ملی`
- `تاریخ تولد`
- `شماره گواهینامه`
نکته:
- اگر راننده و مالک یکی باشند، این سکشن ممکن است وجود نداشته باشد.
---
## 5) `بیمه شخص ثالث زیان‌دیده`
اطلاعات بیمه شخص ثالث طرف زیان‌دیده.
فیلدهای رایج:
- `شماره بیمه‌نامه`
- `شرکت بیمه`
- `تاریخ شروع بیمه‌نامه`
- `تاریخ پایان بیمه‌نامه`
- `سقف تعهد مالی`
- `پوشش‌ها`
---
## 6) `بیمه بدنه زیان‌دیده`
اطلاعات بیمه بدنه‌ی طرف زیان‌دیده.
فیلدهای رایج:
- `شماره بیمه‌نامه`
- `شرکت بیمه`
- `تاریخ شروع بیمه‌نامه`
- `تاریخ پایان بیمه‌نامه`
- `پوشش‌ها`
---
## 7) `بیمه شخص ثالث مقصر`
اطلاعات بیمه شخص ثالث طرف مقصر.
فیلدهای رایج:
- `شماره بیمه‌نامه`
- `شرکت بیمه`
- `تاریخ شروع بیمه‌نامه`
- `تاریخ پایان بیمه‌نامه`
- `سقف تعهد مالی`
- `پوشش‌ها`
---
## 8) `بیمه بدنه مقصر`
اطلاعات بیمه بدنه‌ی طرف مقصر.
فیلدهای رایج:
- `شماره بیمه‌نامه`
- `شرکت بیمه`
- `تاریخ شروع بیمه‌نامه`
- `تاریخ پایان بیمه‌نامه`
- `پوشش‌ها`
---
## 9) `اطلاعات خودروی زیان‌دیده`
جزئیات خودروی زیان‌دیده.
فیلدها می‌توانند شامل موارد زیر باشند:
- `خودرو / پلاک`
- `خودرو / نام خودرو`
- `خودرو / مدل خودرو`
- `خودرو / نوع خودرو`
- `VIN`
- `شماره موتور`
- `شماره شاسی`
- `رنگ اصلی`
- `رنگ فرعی`
- `سیستم`
- `تیپ`
- `کاربری`
- `ظرفیت`
- `تعداد سیلندر`
نکته:
- بسته به منبع داده، ممکن است بعضی فیلدها با برچسب‌های نزدیک به هم ولی از دو منبع مختلف برگردند.
---
## 10) `اطلاعات خودروی مقصر`
جزئیات خودروی طرف مقصر.
فیلدها مشابه سکشن خودروی زیان‌دیده هستند.
---
## 11) `اظهارات و اقرار زیان‌دیده`
اطلاعات مربوط به اظهارات طرف زیان‌دیده.
فیلدهای رایج:
- `نقش طرف`
- `نام`
- `ادعای خسارت`
- `پذیرش نظر کارشناس`
- `توضیحات طرف`
نکته:
- معمولاً `اقرار به تقصیر` برای زیان‌دیده نمایش داده نمی‌شود.
---
## 12) `اظهارات و اقرار مقصر`
اطلاعات مربوط به اظهارات طرف مقصر.
فیلدهای رایج:
- `نقش طرف`
- `نام`
- `اقرار به تقصیر`
- `پذیرش نظر کارشناس`
- `توضیحات طرف`
نکته:
- معمولاً `ادعای خسارت` برای مقصر نمایش داده نمی‌شود.
---
## 13) `کدهای فناوران`
کدها و شناسه‌های فنی مرتبط با پرونده در فناوران.
فیلدهای ممکن:
- `شماره پرونده فناوران`
- `کد پرونده فناوران`
- `کد کیس خسارت فناوران`
- `کد کارشناسی فناوران`
- `کد بیمه‌نامه فناوران`
- `کد راننده فناوران`
- `کد نوع خودرو فناوران`
- `کد شرکت بیمه فناوران`
---
## 14) `نتیجه ارزیابی`
اطلاعات نتیجه‌ی ارزیابی کارشناس خسارت.
فیلدهای مهم:
- `نتیجه ارزیابی`
- `کارشناس ارزیاب`
- `تاریخ و ساعت ثبت ارزیابی`
- `پاسخ / توضیحات کارشناس`
---
## 15) `گزارش حادثه`
خلاصه‌ی اطلاعات حادثه، وضعیت پرونده و برخی خروجی‌های کارشناسی.
فیلدهای رایج:
- `تاریخ حادثه`
- `ساعت حادثه`
- `کارشناس(ان)`
- `موقعیت (عرض و طول جغرافیایی)`
- `وضعیت آب و هوا`
- `وضعیت جاده`
- `وضعیت نور`
- `وضعیت مقصر`
- `وضعیت خسارت`
- `نظر کارشناس مقصر`
- `نحوه برخورد`
- `علت حادثه`
- `نوع حادثه`
- `توضیحات طرف`
---
## نکات مهم برای فرانت‌اند
### 1) فقط بر اساس `sections` و `fields` رندر کنید
ساختار اصلی خروجی این است:
```ts
response.sections[].title
response.sections[].fields[].label
response.sections[].fields[].value
```
---
### 2) به ایندکس سکشن‌ها وابسته نشوید
ممکن است بعضی سکشن‌ها در بعضی پرونده‌ها وجود نداشته باشند.
بهتر است سکشن را با `title` پیدا کنید.
---
### 3) نبودن بعضی سکشن‌ها طبیعی است
مثلاً:
- `راننده خودروی زیان دیده`
- سکشن‌های مربوط به مقصر در بعضی پرونده‌های بدنه
- بعضی داده‌های فناوران
---
### 4) مقدار `-` یعنی داده‌ای برای نمایش وجود نداشته
اگر سکشنی داده‌ی واقعی نداشته باشد، ممکن است فقط این مقدار را داشته باشد:
```json
{
"label": "اطلاعات",
"value": "-"
}
```
---
### 5) برچسب‌ها فارسی و آماده‌ی نمایش هستند
فیلدهای `title` و `label` نیازی به ترجمه‌ی مجدد در فرانت‌اند ندارند.
---
## نمونه‌ی ساده‌ی رندر در فرانت‌اند
```ts
for (const section of response.sections) {
renderSectionTitle(section.title)
for (const field of section.fields) {
renderRow(field.label, field.value ?? "-")
}
}
```
---
## خلاصه
این API برای تولید PDF پرونده، داده‌ها را به‌صورت کامل و تفکیک‌شده برمی‌گرداند، از جمله:
- زمان‌بندی پرونده
- اطلاعات مالک زیان‌دیده
- اطلاعات مالک مقصر
- اطلاعات راننده در صورت متفاوت بودن
- بیمه‌نامه‌های تفکیک‌شده‌ی ثالث و بدنه برای هر طرف
- اطلاعات خودرو برای هر دو طرف
- اظهارات و اقرار هر دو طرف
- کدهای فناوران
- نتیجه ارزیابی و توضیحات کارشناس
- گزارش حادثه