# راهنمای فرانت‌اند: ثبت پاسخ کارشناس خسارت (V2) این مستند قرارداد API زیر را توضیح می‌دهد، به‌ویژه اعتبارسنجی مبلغ‌ها و ساختار خطاهایی که باید در فرم نمایش داده شوند. ```http PUT /v2/expert-claim/reply/submit/:claimRequestId Authorization: Bearer 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` | بله | مبلغ بین 1,000,000 تا 100,000,000,000 ریال. فقط در جریان V1، مجموع این فیلدها در کل پرونده نباید از 530,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 تومان است. - محدودیت هر فیلد مبلغ مستقل است؛ سقف 530 میلیون ریال فقط برای **مجموع `totalPayment` تمام ردیف‌های پرونده V1** اعمال می‌شود. جریان‌های V2 تا V6 سقف مجموع ندارند. - فیلدهای ناشناخته در 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": 540000000, "priceCap": 530000000 } ``` این خطا فقط برای پرونده‌های V1 رخ می‌دهد و به یک ردیف مشخص وصل نیست. آن را در بالای جدول قیمت‌ها نمایش دهید و از `priceCap` جزئیات پرونده/خطا برای پیام UI استفاده کنید. مقدار `priceCap: null` یعنی پرونده سقف مجموع ندارد. ### 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[].daghi.` است. ## الگوی پیشنهادی هندل کردن خطا در فرانت‌اند ```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` را فقط وقتی `priceCap` جزئیات پرونده عدد است با سقف مقایسه کنید؛ `null` یعنی V2 تا V6 و بدون سقف مجموع.