forked from Yara724/api
229 lines
11 KiB
Markdown
229 lines
11 KiB
Markdown
# راهنمای فرانتاند: ثبت پاسخ کارشناس خسارت (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` ردیفها را نیز پیش از ارسال محاسبه و نمایش دهید.
|