forked from Yara724/api
Merge pull request 'YARA-985' (#199) from s.yahyaee/yara724-api:main into main
Reviewed-on: Yara724/api#199
This commit is contained in:
@@ -240,6 +240,23 @@ export class ExpertInsurerController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Get("files/:publicId/timeline")
|
||||||
|
@ApiParam({ name: "publicId" })
|
||||||
|
@ApiOperation({
|
||||||
|
summary: "Activity timeline for a case",
|
||||||
|
description:
|
||||||
|
"Returns a chronological list of all history events for the blame and/or claim associated with the given publicId. Each event has: source, type, timestamp, actor, metadata.",
|
||||||
|
})
|
||||||
|
async getFileTimeline(
|
||||||
|
@CurrentUser() insurer,
|
||||||
|
@Param("publicId") publicId: string,
|
||||||
|
) {
|
||||||
|
return await this.expertInsurerService.getFileTimeline(
|
||||||
|
insurer.clientKey,
|
||||||
|
publicId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@Get("files/:publicId")
|
@Get("files/:publicId")
|
||||||
@ApiParam({ name: "publicId" })
|
@ApiParam({ name: "publicId" })
|
||||||
async getFileDetailsByPublicId(
|
async getFileDetailsByPublicId(
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ import {
|
|||||||
extractExpertNamesFromBlame,
|
extractExpertNamesFromBlame,
|
||||||
extractExpertNamesFromClaim,
|
extractExpertNamesFromClaim,
|
||||||
} from "./helper/insurer.helper";
|
} from "./helper/insurer.helper";
|
||||||
|
import { getEventFaLabel } from "./helper/timeline-fa-labels";
|
||||||
import { buildEnrichedDamagedParts } from "src/expert-claim/dto/claim-damaged-part.enricher";
|
import { buildEnrichedDamagedParts } from "src/expert-claim/dto/claim-damaged-part.enricher";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@@ -2088,4 +2089,77 @@ export class ExpertInsurerService {
|
|||||||
waiting_for_documents_resend: counts.WAITING_FOR_DOCUMENT_RESEND ?? 0,
|
waiting_for_documents_resend: counts.WAITING_FOR_DOCUMENT_RESEND ?? 0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getFileTimeline(
|
||||||
|
insurerId: string,
|
||||||
|
publicId: string,
|
||||||
|
): Promise<{ publicId: string; events: object[] }> {
|
||||||
|
if (!publicId?.trim()) {
|
||||||
|
throw new BadRequestException("publicId is required");
|
||||||
|
}
|
||||||
|
|
||||||
|
const id = this.getClientId(insurerId);
|
||||||
|
const [blameFiles, claimFiles] = await Promise.all([
|
||||||
|
this.getClientBlameFiles(id),
|
||||||
|
this.getClientClaimFiles(id),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const blame = blameFiles.find(
|
||||||
|
(b) => String((b as any).publicId) === publicId,
|
||||||
|
);
|
||||||
|
const claim = claimFiles.find(
|
||||||
|
(c) => String((c as any).publicId) === publicId,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!blame && !claim) {
|
||||||
|
throw new NotFoundException("File not found for this publicId");
|
||||||
|
}
|
||||||
|
|
||||||
|
const events: object[] = [];
|
||||||
|
|
||||||
|
if (blame) {
|
||||||
|
const blameDoc = await this.blameRequestDbService.findById(
|
||||||
|
String((blame as any)._id),
|
||||||
|
);
|
||||||
|
const blameHistory: any[] = (blameDoc as any)?.history ?? [];
|
||||||
|
for (const ev of blameHistory) {
|
||||||
|
events.push({
|
||||||
|
source: "blame",
|
||||||
|
type: ev.type,
|
||||||
|
faLabel: getEventFaLabel(ev),
|
||||||
|
timestamp: ev.timestamp,
|
||||||
|
actor: ev.actor ?? null,
|
||||||
|
metadata: ev.metadata ?? null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (claim) {
|
||||||
|
const claimId = String((claim as any)._id);
|
||||||
|
const rows = (await this.claimCaseDbService.find(
|
||||||
|
{ _id: new Types.ObjectId(claimId) },
|
||||||
|
{ lean: true, select: "history createdAt" },
|
||||||
|
)) as Record<string, unknown>[];
|
||||||
|
const claimHistory: any[] = (rows[0] as any)?.history ?? [];
|
||||||
|
for (const ev of claimHistory) {
|
||||||
|
events.push({
|
||||||
|
source: "claim",
|
||||||
|
type: ev.type,
|
||||||
|
faLabel: getEventFaLabel(ev),
|
||||||
|
timestamp: ev.timestamp,
|
||||||
|
actor: ev.actor ?? null,
|
||||||
|
metadata: ev.metadata ?? null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
events.sort((a: any, b: any) => {
|
||||||
|
const ta = a.timestamp ? new Date(a.timestamp).getTime() : 0;
|
||||||
|
const tb = b.timestamp ? new Date(b.timestamp).getTime() : 0;
|
||||||
|
return ta - tb;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Resolve insurer's client name for context if needed — just return raw events
|
||||||
|
return { publicId, events };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
196
src/expert-insurer/helper/timeline-fa-labels.ts
Normal file
196
src/expert-insurer/helper/timeline-fa-labels.ts
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
/**
|
||||||
|
* Persian (Farsi) display labels for every timeline event `type` that can
|
||||||
|
* appear in a blame or claim history array.
|
||||||
|
*
|
||||||
|
* For generic `STEP_COMPLETED` / `V3_STEP_COMPLETED` events the label is
|
||||||
|
* derived from the `metadata.stepKey` value; those keys are listed at the
|
||||||
|
* bottom under STEP_KEY_FA_LABELS.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* const label = EVENT_TYPE_FA_LABELS[event.type]
|
||||||
|
* ?? resolveStepCompletedFaLabel(event)
|
||||||
|
* ?? event.type;
|
||||||
|
*/
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Blame-phase events (recorded on BlameRequest.history)
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
export const EVENT_TYPE_FA_LABELS: Record<string, string> = {
|
||||||
|
// File creation
|
||||||
|
FILE_CREATED_BY_REGISTRAR: "پرونده توسط ثبتکننده ایجاد شد",
|
||||||
|
FILE_CREATED_BY_FIELD_EXPERT: "پرونده توسط کارشناس میدانی ایجاد شد",
|
||||||
|
|
||||||
|
// Expert link / OTP flow
|
||||||
|
LINK_SENT: "لینک ارسال شد",
|
||||||
|
PARTY_OTP_SENT: "کد تأیید ارسال شد",
|
||||||
|
PARTY_OTP_VERIFIED: "کد تأیید تأیید شد",
|
||||||
|
PARTY_OTPS_VERIFIED: "کدهای تأیید هر دو طرف تأیید شدند",
|
||||||
|
SECOND_PARTY_OTP_SENT: "کد تأیید طرف دوم ارسال شد",
|
||||||
|
SECOND_PARTY_OTP_VERIFIED_ADVANCED: "کد طرف دوم تأیید و پیشرفت انجام شد",
|
||||||
|
|
||||||
|
// Confession / accident type
|
||||||
|
FIRST_BLAME_CONFESSION_SUBMITTED: "اقرار اولیه طرف اول ثبت شد",
|
||||||
|
CAR_BODY_ACCIDENT_TYPE_SUBMITTED: "نوع تصادف بدنه خودرو ثبت شد",
|
||||||
|
AUTO_ADVANCED_TO_CAR_BODY_FORM: "پیشرفت خودکار به فرم بدنه خودرو",
|
||||||
|
AUTO_CONFESSION_SKIPPED: "مرحله اقرار بهصورت خودکار رد شد",
|
||||||
|
|
||||||
|
// First video
|
||||||
|
FIRST_VIDEO_UPLOADED: "ویدیوی اول بارگذاری شد",
|
||||||
|
|
||||||
|
// Second party invitation
|
||||||
|
SECOND_PARTY_INVITED: "طرف دوم دعوت شد",
|
||||||
|
|
||||||
|
// Accident fields / expert in-person completion
|
||||||
|
ACCIDENT_FIELDS_SAVED_ADVANCED_TO_SIGNATURES:
|
||||||
|
"اطلاعات تصادف ذخیره و به مرحله امضاها پیشرفت شد",
|
||||||
|
EXPERT_COMPLETED_CAR_BODY_FORM_V2: "کارشناس فرم بدنه خودرو را تکمیل کرد",
|
||||||
|
EXPERT_COMPLETED_THIRD_PARTY_FORM_V2: "کارشناس فرم شخص ثالث را تکمیل کرد",
|
||||||
|
EXPERT_ADDED_LOCATIONS_V2: "کارشناس موقعیت مکانی را ثبت کرد",
|
||||||
|
EXPERT_UPLOADED_VIDEO_V2: "کارشناس ویدیو را بارگذاری کرد",
|
||||||
|
EXPERT_UPLOADED_VOICE_V2: "کارشناس صدا را بارگذاری کرد",
|
||||||
|
|
||||||
|
// Party rejection / disagreement
|
||||||
|
PARTY_REJECTED_EXPERT_DECISION: "طرف حساب با نظر کارشناس مخالفت کرد",
|
||||||
|
PARTIES_DISAGREED_ON_EXPERT_DECISION: "طرفین با نظر کارشناس توافق نکردند",
|
||||||
|
|
||||||
|
// Document resend (blame)
|
||||||
|
BLAME_DOCUMENT_RESEND_STARTED: "درخواست ارسال مجدد مدارک پرونده شروع شد",
|
||||||
|
BLAME_DOCUMENT_RESEND_CLEARED: "ارسال مجدد مدارک پرونده پاکسازی شد",
|
||||||
|
|
||||||
|
// Expert assignment (blame)
|
||||||
|
BLAME_ASSIGNED: "پرونده به کارشناس تخصیص داده شد",
|
||||||
|
AUTO_ASSIGNED_TO_EXPERT: "پرونده بهصورت خودکار به کارشناس تخصیص یافت",
|
||||||
|
|
||||||
|
// Auto-assignment helpers
|
||||||
|
READY_FOR_EXPERT_EVALUATION: "پرونده آماده ارزیابی کارشناس شد",
|
||||||
|
SECOND_PARTY_DESCRIPTION_COMPLETED: "توضیحات طرف دوم تکمیل شد",
|
||||||
|
|
||||||
|
// V3 blame steps
|
||||||
|
V3_PARTY_SIGNATURE_RECORDED: "امضای طرف ثبت شد",
|
||||||
|
V3_ACCIDENT_FIELDS_SAVED: "اطلاعات تصادف ثبت شد",
|
||||||
|
V3_BLAME_ACCIDENT_VIDEO_UPLOADED: "ویدیوی تصادف بارگذاری شد",
|
||||||
|
V3_PARTY_VOICE_UPLOADED: "صدای طرف بارگذاری شد",
|
||||||
|
V3_PARTY_LOCATION_SAVED: "موقعیت مکانی طرف ذخیره شد",
|
||||||
|
V3_PARTY_DESCRIPTION_SAVED: "توضیحات طرف ذخیره شد",
|
||||||
|
V3_CAR_BODY_ACCIDENT_TYPE_SAVED: "نوع تصادف بدنه خودرو ذخیره شد",
|
||||||
|
|
||||||
|
// V5 blame steps
|
||||||
|
V5_BLAME_ACCIDENT_VIDEO_UPLOADED: "ویدیوی تصادف (نسخه ۵) بارگذاری شد",
|
||||||
|
V5_FILE_MAKER_APPROVED: "پروندهساز پرونده را تأیید کرد",
|
||||||
|
V5_FILE_MAKER_REJECTED: "پروندهساز پرونده را رد کرد",
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Claim-phase events (recorded on ClaimCase.history)
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Claim creation
|
||||||
|
CLAIM_CREATED: "پرونده خسارت ایجاد شد",
|
||||||
|
|
||||||
|
// Expert assignment (claim)
|
||||||
|
CLAIM_ASSIGNED: "پرونده خسارت به کارشناس ارزیابی تخصیص داده شد",
|
||||||
|
|
||||||
|
// Expert actions on claim
|
||||||
|
EXPERT_RESEND_REQUESTED: "کارشناس درخواست ارسال مجدد مدارک داد",
|
||||||
|
EXPERT_RESEND_FULFILLED: "مدارک درخواستی ارسال شد",
|
||||||
|
IN_PERSON_VISIT_REQUESTED: "کارشناس بازدید حضوری درخواست کرد",
|
||||||
|
EXPERT_DAMAGED_PARTS_UPDATED: "کارشناس قطعات آسیبدیده را بهروزرسانی کرد",
|
||||||
|
|
||||||
|
// Workflow steps (generic)
|
||||||
|
STEP_COMPLETED: "مرحله تکمیل شد",
|
||||||
|
V3_STEP_COMPLETED: "مرحله تکمیل شد",
|
||||||
|
|
||||||
|
// Documents & media
|
||||||
|
DOCUMENT_UPLOADED: "مدرک بارگذاری شد",
|
||||||
|
VIDEO_CAPTURE_UPLOADED: "تصویر ویدیویی بارگذاری شد",
|
||||||
|
ALL_FACTORS_UPLOADED_PENDING_VALIDATION:
|
||||||
|
"تمام فاکتورها بارگذاری شدند و در انتظار تأیید هستند",
|
||||||
|
|
||||||
|
// User responses
|
||||||
|
USER_OBJECTION_SUBMITTED: "اعتراض کاربر ثبت شد",
|
||||||
|
USER_RATING_SUBMITTED: "امتیاز کاربر ثبت شد",
|
||||||
|
|
||||||
|
// Owner insurer approval
|
||||||
|
OWNER_SIGNED_INSURER_APPROVAL: "صاحب خودرو قرارداد بیمه را امضا کرد",
|
||||||
|
OWNER_REJECTED_INSURER_APPROVAL_PRICING: "صاحب خودرو قیمتگذاری بیمه را رد کرد",
|
||||||
|
OWNER_SIGNED_PRICED_PARTS_PENDING_FACTOR_UPLOAD:
|
||||||
|
"صاحب خودرو قطعات قیمتگذاریشده را امضا کرد و در انتظار بارگذاری فاکتور",
|
||||||
|
OWNER_REJECTED_PRICED_PARTS_BEFORE_FACTORS:
|
||||||
|
"صاحب خودرو قطعات قیمتگذاریشده را پیش از فاکتور رد کرد",
|
||||||
|
|
||||||
|
// Fanavaran sync
|
||||||
|
FANAVARAN_AUTO_SUBMIT_SUCCEEDED: "ارسال خودکار به فناوران موفق بود",
|
||||||
|
FANAVARAN_AUTO_SUBMIT_FAILED: "ارسال خودکار به فناوران ناموفق بود",
|
||||||
|
FANAVARAN_EARLY_AUTO_SUBMIT_SUCCEEDED: "ارسال زودهنگام خودکار به فناوران موفق بود",
|
||||||
|
FANAVARAN_EARLY_AUTO_SUBMIT_FAILED: "ارسال زودهنگام خودکار به فناوران ناموفق بود",
|
||||||
|
FANAVARAN_EXPERTISE_AUTO_SUBMIT_SUCCEEDED:
|
||||||
|
"ارسال خودکار کارشناسی به فناوران موفق بود",
|
||||||
|
FANAVARAN_EXPERTISE_AUTO_SUBMIT_FAILED:
|
||||||
|
"ارسال خودکار کارشناسی به فناوران ناموفق بود",
|
||||||
|
FANAVARAN_ATTACHMENT_AUTO_SUBMIT_SUCCEEDED:
|
||||||
|
"ارسال خودکار پیوست به فناوران موفق بود",
|
||||||
|
FANAVARAN_ATTACHMENT_AUTO_SUBMIT_FAILED:
|
||||||
|
"ارسال خودکار پیوست به فناوران ناموفق بود",
|
||||||
|
FANAVARAN_DAMAGE_CASE_AUTO_SUBMIT_SUCCEEDED:
|
||||||
|
"ارسال خودکار پرونده خسارت به فناوران موفق بود",
|
||||||
|
FANAVARAN_DAMAGE_CASE_AUTO_SUBMIT_FAILED:
|
||||||
|
"ارسال خودکار پرونده خسارت به فناوران ناموفق بود",
|
||||||
|
|
||||||
|
// V5 claim
|
||||||
|
V5_HELD_FOR_FILE_MAKER_APPROVAL: "پرونده در انتظار تأیید پروندهساز متوقف شد",
|
||||||
|
};
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Persian labels for `metadata.stepKey` inside STEP_COMPLETED events
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
export const STEP_KEY_FA_LABELS: Record<string, string> = {
|
||||||
|
// Claim workflow steps
|
||||||
|
CLAIM_CREATED: "ایجاد پرونده خسارت",
|
||||||
|
SELECT_OUTER_PARTS: "انتخاب قطعات بیرونی آسیبدیده",
|
||||||
|
SELECT_OTHER_PARTS: "انتخاب سایر اطلاعات و قطعات",
|
||||||
|
CAPTURE_PART_DAMAGES: "عکسبرداری از آسیبهای قطعات",
|
||||||
|
UPLOAD_REQUIRED_DOCUMENTS: "بارگذاری مدارک مورد نیاز",
|
||||||
|
USER_SUBMISSION_COMPLETE: "ارسال اطلاعات توسط کاربر",
|
||||||
|
USER_EXPERT_RESEND: "ارسال مجدد مدارک توسط کاربر",
|
||||||
|
EXPERT_DAMAGE_ASSESSMENT: "ارزیابی خسارت توسط کارشناس",
|
||||||
|
EXPERT_FINAL_REPLY: "پاسخ نهایی کارشناس",
|
||||||
|
EXPERT_COST_EVALUATION: "ارزیابی هزینه توسط کارشناس",
|
||||||
|
OWNER_UPLOAD_FACTOR_DOCUMENTS: "بارگذاری فاکتورهای تعمیر توسط مالک",
|
||||||
|
INSURER_REVIEW: "بررسی توسط بیمهگر",
|
||||||
|
CLAIM_COMPLETED: "پرونده خسارت تکمیل شد",
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For `STEP_COMPLETED` and `V3_STEP_COMPLETED` events whose final label
|
||||||
|
* depends on the `metadata.stepKey` value, this function returns the
|
||||||
|
* most specific Persian label available.
|
||||||
|
*/
|
||||||
|
export function resolveStepCompletedFaLabel(event: {
|
||||||
|
type: string;
|
||||||
|
metadata?: any;
|
||||||
|
}): string | undefined {
|
||||||
|
if (
|
||||||
|
event.type !== "STEP_COMPLETED" &&
|
||||||
|
event.type !== "V3_STEP_COMPLETED"
|
||||||
|
) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const stepKey: string | undefined = event.metadata?.stepKey;
|
||||||
|
if (stepKey && STEP_KEY_FA_LABELS[stepKey]) {
|
||||||
|
return STEP_KEY_FA_LABELS[stepKey];
|
||||||
|
}
|
||||||
|
return "مرحله تکمیل شد";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the best Persian label for any timeline event.
|
||||||
|
*/
|
||||||
|
export function getEventFaLabel(event: {
|
||||||
|
type: string;
|
||||||
|
metadata?: any;
|
||||||
|
}): string {
|
||||||
|
return (
|
||||||
|
resolveStepCompletedFaLabel(event) ??
|
||||||
|
EVENT_TYPE_FA_LABELS[event.type] ??
|
||||||
|
event.type
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user