forked from Yara724/api
YARA-985
This commit is contained in:
@@ -309,7 +309,7 @@ export class ExpertInsurerController {
|
||||
@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.",
|
||||
"Returns a chronological list of all history events for the blame and/or claim associated with the given publicId. Each event includes localized `faLabel`, enriched `actor`/`actorName`, the specific `performedBy` role, plus `performedByCategory` and `performedByFaLabel` for UI rendering.",
|
||||
})
|
||||
async getFileTimeline(@CurrentUser() insurer, @Param("publicId") publicId: string) {
|
||||
return await this.expertInsurerService.getFileTimeline(insurer.clientKey, publicId);
|
||||
|
||||
@@ -29,6 +29,9 @@ import { ExpertDbService } from "src/users/entities/db-service/expert.db.service
|
||||
import { FieldExpertDbService } from "src/users/entities/db-service/field-expert.db.service";
|
||||
import { FileMakerDbService } from "src/users/entities/db-service/file-maker.db.service";
|
||||
import { FileReviewerDbService } from "src/users/entities/db-service/file-reviewer.db.service";
|
||||
import { RegistrarDbService } from "src/users/entities/db-service/registrar.db.service";
|
||||
import { CallCenterAgentDbService } from "src/users/entities/db-service/call-center-agent.db.service";
|
||||
import { UserDbService } from "src/users/entities/db-service/user.db.service";
|
||||
import { ExpertFileActivityDbService } from "src/users/entities/db-service/expert-file-activity.db.service";
|
||||
import { ExpertFileActivityType } from "src/users/entities/schema/expert-file-activity.schema";
|
||||
import { HashService } from "src/utils/hash/hash.service";
|
||||
@@ -96,6 +99,9 @@ export class ExpertInsurerService {
|
||||
private readonly claimSignDbService: ClaimSignDbService,
|
||||
private readonly fileMakerDbService: FileMakerDbService,
|
||||
private readonly fileReviewerDbService: FileReviewerDbService,
|
||||
private readonly registrarDbService: RegistrarDbService,
|
||||
private readonly callCenterAgentDbService: CallCenterAgentDbService,
|
||||
private readonly userDbService: UserDbService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -2150,19 +2156,309 @@ export class ExpertInsurerService {
|
||||
};
|
||||
}
|
||||
|
||||
private static readonly TIMELINE_PERFORMER_FA_LABELS: Record<string, string> = {
|
||||
user: "کاربر",
|
||||
field_expert: "کارشناس میدانی",
|
||||
damage_expert: "کارشناس خسارت",
|
||||
file_maker: "پروندهساز",
|
||||
file_reviewer: "بازبین پرونده",
|
||||
registrar: "ثبتکننده",
|
||||
call_center: "اپراتور کالسنتر",
|
||||
system: "سیستم",
|
||||
expert: "کارشناس",
|
||||
};
|
||||
|
||||
/**
|
||||
* Maps a raw history `actorType` string to either `"user"` or `"expert"`.
|
||||
* - `"user"` → the action was taken by the car-owner / damaged party
|
||||
* - `"expert"` → taken by any expert, system, or back-office actor
|
||||
* - `null` → actorType was absent (unknown)
|
||||
* Returns the most specific actor role recorded in history so the frontend can
|
||||
* distinguish between user/system/back-office/expert actions.
|
||||
*/
|
||||
private static resolveTimelinePerformedBy(
|
||||
actorType: string | undefined,
|
||||
): "user" | "expert" | null {
|
||||
if (!actorType) return null;
|
||||
if (actorType === "user") return "user";
|
||||
): string | null {
|
||||
if (!actorType?.trim()) return null;
|
||||
return actorType.trim().toLowerCase();
|
||||
}
|
||||
|
||||
private static resolveTimelinePerformedByCategory(
|
||||
actorType: string | undefined,
|
||||
): "user" | "expert" | "staff" | "system" | null {
|
||||
const performedBy = ExpertInsurerService.resolveTimelinePerformedBy(actorType);
|
||||
if (!performedBy) return null;
|
||||
if (performedBy === "user") return "user";
|
||||
if (performedBy === "system") return "system";
|
||||
if (
|
||||
performedBy === "field_expert" ||
|
||||
performedBy === "damage_expert" ||
|
||||
performedBy === "expert"
|
||||
) {
|
||||
return "expert";
|
||||
}
|
||||
return "staff";
|
||||
}
|
||||
|
||||
private static resolveTimelinePerformedByFaLabel(
|
||||
actorType: string | undefined,
|
||||
): string | null {
|
||||
const performedBy = ExpertInsurerService.resolveTimelinePerformedBy(actorType);
|
||||
if (!performedBy) return null;
|
||||
return (
|
||||
ExpertInsurerService.TIMELINE_PERFORMER_FA_LABELS[performedBy] ??
|
||||
performedBy
|
||||
);
|
||||
}
|
||||
|
||||
private static readonly TIMELINE_PLACEHOLDER_ACTOR_NAMES = new Set([
|
||||
"user",
|
||||
"actor",
|
||||
"expert",
|
||||
"unknown expert",
|
||||
"field_expert",
|
||||
"damage_expert",
|
||||
"file_maker",
|
||||
"file_reviewer",
|
||||
"registrar",
|
||||
"call_center",
|
||||
"system",
|
||||
]);
|
||||
|
||||
private static getDisplayNameFromRecord(record: any): string | null {
|
||||
if (!record) return null;
|
||||
|
||||
const fullName =
|
||||
typeof record.fullName === "string" ? record.fullName.trim() : "";
|
||||
if (fullName) return fullName;
|
||||
|
||||
const firstName =
|
||||
typeof record.firstName === "string" ? record.firstName.trim() : "";
|
||||
const lastName =
|
||||
typeof record.lastName === "string" ? record.lastName.trim() : "";
|
||||
const combined = [firstName, lastName].filter(Boolean).join(" ").trim();
|
||||
return combined || null;
|
||||
}
|
||||
|
||||
private static isMeaningfulTimelineActorName(name: string | undefined): boolean {
|
||||
const trimmed = typeof name === "string" ? name.trim() : "";
|
||||
if (!trimmed) return false;
|
||||
return !ExpertInsurerService.TIMELINE_PLACEHOLDER_ACTOR_NAMES.has(
|
||||
trimmed.toLowerCase(),
|
||||
);
|
||||
}
|
||||
|
||||
private static resolveBlamePartyName(sourceDoc: any, partyRole?: string): string | null {
|
||||
const parties = Array.isArray(sourceDoc?.parties) ? sourceDoc.parties : [];
|
||||
const normalizedPartyRole =
|
||||
typeof partyRole === "string" ? partyRole.trim().toUpperCase() : "";
|
||||
|
||||
const matchedParty = normalizedPartyRole
|
||||
? parties.find(
|
||||
(party: any) =>
|
||||
String(party?.role ?? "").trim().toUpperCase() === normalizedPartyRole,
|
||||
)
|
||||
: null;
|
||||
|
||||
const matchedName = matchedParty?.person?.fullName?.trim?.();
|
||||
if (matchedName) return matchedName;
|
||||
|
||||
if (parties.length === 1) {
|
||||
return parties[0]?.person?.fullName?.trim?.() || null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private async resolveTimelineActualActorType(
|
||||
actor: any,
|
||||
cache: Map<string, string | null> = new Map(),
|
||||
): Promise<string | null> {
|
||||
const rawActorType = ExpertInsurerService.resolveTimelinePerformedBy(
|
||||
actor?.actorType,
|
||||
);
|
||||
const actorId = actor?.actorId ? String(actor.actorId).trim() : "";
|
||||
|
||||
if (!actorId || !Types.ObjectId.isValid(actorId)) {
|
||||
return rawActorType;
|
||||
}
|
||||
|
||||
if (cache.has(actorId)) {
|
||||
return cache.get(actorId) ?? rawActorType ?? null;
|
||||
}
|
||||
|
||||
const objectId = new Types.ObjectId(actorId);
|
||||
const [fileMaker, fileReviewer, registrar, callCenter, fieldExpert, damageExpert, expert, user] = await Promise.all([
|
||||
this.fileMakerDbService.findById(actorId),
|
||||
this.fileReviewerDbService.findById(actorId),
|
||||
this.registrarDbService.findById(actorId),
|
||||
this.callCenterAgentDbService.findById(actorId),
|
||||
this.fieldExpertDbService.findById(actorId),
|
||||
this.damageExpertDbService.findById(actorId),
|
||||
this.expertDbService.findOne({ _id: objectId }),
|
||||
this.userDbService.findOne({ _id: objectId }),
|
||||
]);
|
||||
|
||||
const resolvedActorType = fileMaker
|
||||
? "file_maker"
|
||||
: fileReviewer
|
||||
? "file_reviewer"
|
||||
: registrar
|
||||
? "registrar"
|
||||
: callCenter
|
||||
? "call_center"
|
||||
: fieldExpert
|
||||
? "field_expert"
|
||||
: damageExpert
|
||||
? "damage_expert"
|
||||
: expert
|
||||
? "expert"
|
||||
: user
|
||||
? "user"
|
||||
: rawActorType;
|
||||
|
||||
cache.set(actorId, resolvedActorType ?? null);
|
||||
return resolvedActorType ?? null;
|
||||
}
|
||||
|
||||
private async resolveTimelineActorNameFromId(
|
||||
actorType: string,
|
||||
actorId: string,
|
||||
): Promise<string | null> {
|
||||
if (!Types.ObjectId.isValid(actorId)) return null;
|
||||
|
||||
const objectId = new Types.ObjectId(actorId);
|
||||
|
||||
switch (actorType) {
|
||||
case "user": {
|
||||
const user = await this.userDbService.findOne({ _id: objectId });
|
||||
return ExpertInsurerService.getDisplayNameFromRecord(user);
|
||||
}
|
||||
case "field_expert": {
|
||||
const [fieldExpert, expert] = await Promise.all([
|
||||
this.fieldExpertDbService.findById(actorId),
|
||||
this.expertDbService.findOne({ _id: objectId }),
|
||||
]);
|
||||
return (
|
||||
ExpertInsurerService.getDisplayNameFromRecord(fieldExpert) ??
|
||||
ExpertInsurerService.getDisplayNameFromRecord(expert)
|
||||
);
|
||||
}
|
||||
case "damage_expert": {
|
||||
const [damageExpert, expert] = await Promise.all([
|
||||
this.damageExpertDbService.findById(actorId),
|
||||
this.expertDbService.findOne({ _id: objectId }),
|
||||
]);
|
||||
return (
|
||||
ExpertInsurerService.getDisplayNameFromRecord(damageExpert) ??
|
||||
ExpertInsurerService.getDisplayNameFromRecord(expert)
|
||||
);
|
||||
}
|
||||
case "file_maker": {
|
||||
const fileMaker = await this.fileMakerDbService.findById(actorId);
|
||||
return ExpertInsurerService.getDisplayNameFromRecord(fileMaker);
|
||||
}
|
||||
case "file_reviewer": {
|
||||
const [fileReviewer, expert] = await Promise.all([
|
||||
this.fileReviewerDbService.findById(actorId),
|
||||
this.expertDbService.findOne({ _id: objectId }),
|
||||
]);
|
||||
return (
|
||||
ExpertInsurerService.getDisplayNameFromRecord(fileReviewer) ??
|
||||
ExpertInsurerService.getDisplayNameFromRecord(expert)
|
||||
);
|
||||
}
|
||||
case "registrar": {
|
||||
const registrar = await this.registrarDbService.findById(actorId);
|
||||
return ExpertInsurerService.getDisplayNameFromRecord(registrar);
|
||||
}
|
||||
case "call_center": {
|
||||
const agent = await this.callCenterAgentDbService.findById(actorId);
|
||||
return ExpertInsurerService.getDisplayNameFromRecord(agent);
|
||||
}
|
||||
case "expert": {
|
||||
const expert = await this.expertDbService.findOne({ _id: objectId });
|
||||
return ExpertInsurerService.getDisplayNameFromRecord(expert);
|
||||
}
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private buildTimelineActor(
|
||||
actor: any,
|
||||
actorName: string | null,
|
||||
actorType: string | null,
|
||||
): { actorId?: string; actorName?: string; actorType?: string } | null {
|
||||
const actorId = actor?.actorId ? String(actor.actorId).trim() : "";
|
||||
|
||||
if (!actorType && !actorId && !actorName) return null;
|
||||
|
||||
return {
|
||||
...(actorId ? { actorId } : {}),
|
||||
...(actorName ? { actorName } : {}),
|
||||
...(actorType ? { actorType } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
private async resolveTimelineActorName(
|
||||
actor: any,
|
||||
actorType: string | null,
|
||||
sourceDoc?: any,
|
||||
event?: any,
|
||||
cache: Map<string, string | null> = new Map(),
|
||||
): Promise<string | null> {
|
||||
const explicitName =
|
||||
typeof actor?.actorName === "string" ? actor.actorName.trim() : "";
|
||||
if (ExpertInsurerService.isMeaningfulTimelineActorName(explicitName)) {
|
||||
return explicitName;
|
||||
}
|
||||
|
||||
if (!actorType) return null;
|
||||
|
||||
if (actorType === "system") {
|
||||
return ExpertInsurerService.resolveTimelinePerformedByFaLabel(actorType);
|
||||
}
|
||||
|
||||
if (actorType === "user") {
|
||||
const ownerName = sourceDoc?.owner?.fullName?.trim?.();
|
||||
if (ownerName) return ownerName;
|
||||
|
||||
const blamePartyName = ExpertInsurerService.resolveBlamePartyName(
|
||||
sourceDoc,
|
||||
event?.metadata?.partyRole,
|
||||
);
|
||||
if (blamePartyName) return blamePartyName;
|
||||
}
|
||||
|
||||
const actorId = actor?.actorId ? String(actor.actorId).trim() : "";
|
||||
if (actorId) {
|
||||
const cacheKey = `${actorType}:${actorId}`;
|
||||
if (cache.has(cacheKey)) {
|
||||
return cache.get(cacheKey) ?? null;
|
||||
}
|
||||
|
||||
let resolvedName = await this.resolveTimelineActorNameFromId(
|
||||
actorType,
|
||||
actorId,
|
||||
);
|
||||
|
||||
if (!resolvedName && actorType === "user") {
|
||||
resolvedName =
|
||||
sourceDoc?.owner?.fullName?.trim?.() ??
|
||||
ExpertInsurerService.resolveBlamePartyName(
|
||||
sourceDoc,
|
||||
event?.metadata?.partyRole,
|
||||
);
|
||||
}
|
||||
|
||||
if (!resolvedName) {
|
||||
resolvedName =
|
||||
ExpertInsurerService.resolveTimelinePerformedByFaLabel(actorType);
|
||||
}
|
||||
|
||||
cache.set(cacheKey, resolvedName ?? null);
|
||||
return resolvedName ?? null;
|
||||
}
|
||||
|
||||
return ExpertInsurerService.resolveTimelinePerformedByFaLabel(actorType);
|
||||
}
|
||||
|
||||
async getFileTimeline(
|
||||
insurerId: string,
|
||||
@@ -2190,6 +2486,8 @@ export class ExpertInsurerService {
|
||||
}
|
||||
|
||||
const events: object[] = [];
|
||||
const actorNameCache = new Map<string, string | null>();
|
||||
const actorTypeCache = new Map<string, string | null>();
|
||||
|
||||
if (blame) {
|
||||
const blameDoc = await this.blameRequestDbService.findById(
|
||||
@@ -2197,13 +2495,29 @@ export class ExpertInsurerService {
|
||||
);
|
||||
const blameHistory: any[] = (blameDoc as any)?.history ?? [];
|
||||
for (const ev of blameHistory) {
|
||||
const performedBy = await this.resolveTimelineActualActorType(
|
||||
ev.actor,
|
||||
actorTypeCache,
|
||||
);
|
||||
const actorName = await this.resolveTimelineActorName(
|
||||
ev.actor,
|
||||
performedBy,
|
||||
blameDoc,
|
||||
ev,
|
||||
actorNameCache,
|
||||
);
|
||||
events.push({
|
||||
source: "blame",
|
||||
type: ev.type,
|
||||
faLabel: getEventFaLabel(ev),
|
||||
timestamp: ev.timestamp,
|
||||
actor: ev.actor ?? null,
|
||||
performedBy: ExpertInsurerService.resolveTimelinePerformedBy(ev.actor?.actorType),
|
||||
actor: this.buildTimelineActor(ev.actor, actorName, performedBy),
|
||||
actorName,
|
||||
performedBy,
|
||||
performedByCategory:
|
||||
ExpertInsurerService.resolveTimelinePerformedByCategory(performedBy),
|
||||
performedByFaLabel:
|
||||
ExpertInsurerService.resolveTimelinePerformedByFaLabel(performedBy),
|
||||
metadata: ev.metadata ?? null,
|
||||
});
|
||||
}
|
||||
@@ -2213,17 +2527,34 @@ export class ExpertInsurerService {
|
||||
const claimId = String((claim as any)._id);
|
||||
const rows = (await this.claimCaseDbService.find(
|
||||
{ _id: new Types.ObjectId(claimId) },
|
||||
{ lean: true, select: "history createdAt" },
|
||||
{ lean: true, select: "history createdAt owner" },
|
||||
)) as Record<string, unknown>[];
|
||||
const claimHistory: any[] = (rows[0] as any)?.history ?? [];
|
||||
const claimDoc = rows[0] as any;
|
||||
const claimHistory: any[] = claimDoc?.history ?? [];
|
||||
for (const ev of claimHistory) {
|
||||
const performedBy = await this.resolveTimelineActualActorType(
|
||||
ev.actor,
|
||||
actorTypeCache,
|
||||
);
|
||||
const actorName = await this.resolveTimelineActorName(
|
||||
ev.actor,
|
||||
performedBy,
|
||||
claimDoc,
|
||||
ev,
|
||||
actorNameCache,
|
||||
);
|
||||
events.push({
|
||||
source: "claim",
|
||||
type: ev.type,
|
||||
faLabel: getEventFaLabel(ev),
|
||||
timestamp: ev.timestamp,
|
||||
actor: ev.actor ?? null,
|
||||
performedBy: ExpertInsurerService.resolveTimelinePerformedBy(ev.actor?.actorType),
|
||||
actor: this.buildTimelineActor(ev.actor, actorName, performedBy),
|
||||
actorName,
|
||||
performedBy,
|
||||
performedByCategory:
|
||||
ExpertInsurerService.resolveTimelinePerformedByCategory(performedBy),
|
||||
performedByFaLabel:
|
||||
ExpertInsurerService.resolveTimelinePerformedByFaLabel(performedBy),
|
||||
metadata: ev.metadata ?? null,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -19,9 +19,11 @@ export const EVENT_TYPE_FA_LABELS: Record<string, string> = {
|
||||
// File creation
|
||||
FILE_CREATED_BY_REGISTRAR: "پرونده توسط ثبتکننده ایجاد شد",
|
||||
FILE_CREATED_BY_FIELD_EXPERT: "پرونده توسط کارشناس میدانی ایجاد شد",
|
||||
FILE_CREATED_BY_CALL_CENTER: "پرونده توسط اپراتور کالسنتر ایجاد شد",
|
||||
|
||||
// Expert link / OTP flow
|
||||
LINK_SENT: "لینک ارسال شد",
|
||||
CALL_CENTER_LINK_SENT: "لینک توسط اپراتور کالسنتر ارسال شد",
|
||||
PARTY_OTP_SENT: "کد تأیید ارسال شد",
|
||||
PARTY_OTP_VERIFIED: "کد تأیید تأیید شد",
|
||||
PARTY_OTPS_VERIFIED: "کدهای تأیید هر دو طرف تأیید شدند",
|
||||
@@ -30,6 +32,7 @@ export const EVENT_TYPE_FA_LABELS: Record<string, string> = {
|
||||
|
||||
// Confession / accident type
|
||||
FIRST_BLAME_CONFESSION_SUBMITTED: "اقرار اولیه طرف اول ثبت شد",
|
||||
CALL_CENTER_INQUIRY_COMPLETED: "استعلام کالسنتر تکمیل شد",
|
||||
CAR_BODY_ACCIDENT_TYPE_SUBMITTED: "نوع تصادف بدنه خودرو ثبت شد",
|
||||
AUTO_ADVANCED_TO_CAR_BODY_FORM: "پیشرفت خودکار به فرم بدنه خودرو",
|
||||
AUTO_CONFESSION_SKIPPED: "مرحله اقرار بهصورت خودکار رد شد",
|
||||
@@ -41,6 +44,7 @@ export const EVENT_TYPE_FA_LABELS: Record<string, string> = {
|
||||
SECOND_PARTY_INVITED: "طرف دوم دعوت شد",
|
||||
|
||||
// Accident fields / expert in-person completion
|
||||
ACCIDENT_FIELDS_SAVED: "اطلاعات تصادف ثبت شد",
|
||||
ACCIDENT_FIELDS_SAVED_ADVANCED_TO_SIGNATURES:
|
||||
"اطلاعات تصادف ذخیره و به مرحله امضاها پیشرفت شد",
|
||||
EXPERT_COMPLETED_CAR_BODY_FORM_V2: "کارشناس فرم بدنه خودرو را تکمیل کرد",
|
||||
@@ -72,12 +76,15 @@ export const EVENT_TYPE_FA_LABELS: Record<string, string> = {
|
||||
V3_PARTY_VOICE_UPLOADED: "صدای طرف بارگذاری شد",
|
||||
V3_PARTY_LOCATION_SAVED: "موقعیت مکانی طرف ذخیره شد",
|
||||
V3_PARTY_DESCRIPTION_SAVED: "توضیحات طرف ذخیره شد",
|
||||
V4_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: "پروندهساز پرونده را رد کرد",
|
||||
V5_FILE_MAKER_REJECTED_BLAME_RESET:
|
||||
"پروندهساز پرونده را رد کرد و پرونده به مرحله قبل بازگردانده شد",
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Claim-phase events (recorded on ClaimCase.history)
|
||||
@@ -102,6 +109,8 @@ export const EVENT_TYPE_FA_LABELS: Record<string, string> = {
|
||||
// Documents & media
|
||||
DOCUMENT_UPLOADED: "مدرک بارگذاری شد",
|
||||
VIDEO_CAPTURE_UPLOADED: "تصویر ویدیویی بارگذاری شد",
|
||||
PART_CAPTURED: "تصویر قطعه آسیبدیده بارگذاری شد",
|
||||
ANGLE_CAPTURED: "تصویر زاویه خودرو بارگذاری شد",
|
||||
ALL_FACTORS_UPLOADED_PENDING_VALIDATION:
|
||||
"تمام فاکتورها بارگذاری شدند و در انتظار تأیید هستند",
|
||||
|
||||
|
||||
@@ -135,6 +135,26 @@ export class RequestManagementService {
|
||||
throw new BadRequestException(`Step ${stepKey} is not a party-scoped step`);
|
||||
}
|
||||
|
||||
private resolveBlameHistoryActorType(actor: any): string {
|
||||
switch (actor?.role) {
|
||||
case RoleEnum.FILE_MAKER:
|
||||
return "file_maker";
|
||||
case RoleEnum.FILE_REVIEWER:
|
||||
return "file_reviewer";
|
||||
case RoleEnum.REGISTRAR:
|
||||
return "registrar";
|
||||
case RoleEnum.CALL_CENTER:
|
||||
return "call_center";
|
||||
case RoleEnum.DAMAGE_EXPERT:
|
||||
return "damage_expert";
|
||||
case RoleEnum.EXPERT:
|
||||
return "expert";
|
||||
case RoleEnum.FIELD_EXPERT:
|
||||
default:
|
||||
return "field_expert";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse map: Fanavaran/Tejarat numeric letter code → Persian plate letter.
|
||||
* Tejarat inquiry stores Plk2 as a numeric code (e.g. 12 → "م", 5 → "د").
|
||||
@@ -4992,7 +5012,7 @@ export class RequestManagementService {
|
||||
actor: {
|
||||
actorId: expertId,
|
||||
actorName: `${expert.firstName || ""} ${expert.lastName || ""}`.trim(),
|
||||
actorType: "field_expert",
|
||||
actorType: this.resolveBlameHistoryActorType(expert),
|
||||
},
|
||||
metadata: { creationMethod: dto.creationMethod, type: dto.type },
|
||||
});
|
||||
@@ -5093,7 +5113,7 @@ export class RequestManagementService {
|
||||
actor: {
|
||||
actorId: new Types.ObjectId(expert.sub),
|
||||
actorName: `${expert.firstName || ""} ${expert.lastName || ""}`.trim(),
|
||||
actorType: "field_expert",
|
||||
actorType: this.resolveBlameHistoryActorType(expert),
|
||||
},
|
||||
metadata: { sentTo, template: "yara-field-expert-link" },
|
||||
});
|
||||
@@ -5279,8 +5299,7 @@ export class RequestManagementService {
|
||||
actor: {
|
||||
actorId: new Types.ObjectId(expert.sub),
|
||||
actorName: `${expert.firstName || ""} ${expert.lastName || ""}`.trim(),
|
||||
actorType:
|
||||
expert?.role === RoleEnum.REGISTRAR ? "registrar" : "field_expert",
|
||||
actorType: this.resolveBlameHistoryActorType(expert),
|
||||
},
|
||||
metadata: {
|
||||
firstPartyVerified: true,
|
||||
@@ -5375,7 +5394,7 @@ export class RequestManagementService {
|
||||
actor: {
|
||||
actorId: new Types.ObjectId(actor.sub),
|
||||
actorName: `${actor.firstName || ""} ${actor.lastName || ""}`.trim(),
|
||||
actorType: "field_expert",
|
||||
actorType: this.resolveBlameHistoryActorType(actor),
|
||||
},
|
||||
metadata: { phoneNumber: phone },
|
||||
} as any);
|
||||
@@ -5516,7 +5535,7 @@ export class RequestManagementService {
|
||||
actor: {
|
||||
actorId: new Types.ObjectId(actor.sub),
|
||||
actorName: `${actor.firstName || ""} ${actor.lastName || ""}`.trim(),
|
||||
actorType: "field_expert",
|
||||
actorType: this.resolveBlameHistoryActorType(actor),
|
||||
},
|
||||
metadata: { partyRole: role, phoneNumber: phone },
|
||||
} as any);
|
||||
@@ -5558,8 +5577,7 @@ export class RequestManagementService {
|
||||
actorId: new Types.ObjectId(actor.sub),
|
||||
actorName:
|
||||
`${actor.firstName || ""} ${actor.lastName || ""}`.trim(),
|
||||
actorType:
|
||||
actor?.role === RoleEnum.REGISTRAR ? "registrar" : "field_expert",
|
||||
actorType: this.resolveBlameHistoryActorType(actor),
|
||||
},
|
||||
metadata: { advancedTo: WorkflowStep.CAR_BODY_ACCIDENT_TYPE },
|
||||
} as any);
|
||||
@@ -5593,10 +5611,8 @@ export class RequestManagementService {
|
||||
type: "AUTO_CONFESSION_SKIPPED",
|
||||
actor: {
|
||||
actorId: new Types.ObjectId(actor.sub),
|
||||
actorName:
|
||||
`${actor.firstName || ""} ${actor.lastName || ""}`.trim(),
|
||||
actorType:
|
||||
actor?.role === RoleEnum.REGISTRAR ? "registrar" : "field_expert",
|
||||
actorName: `${actor.firstName || ""} ${actor.lastName || ""}`.trim(),
|
||||
actorType: this.resolveBlameHistoryActorType(actor),
|
||||
},
|
||||
metadata: {
|
||||
reason:
|
||||
@@ -5621,9 +5637,9 @@ export class RequestManagementService {
|
||||
type: "SECOND_PARTY_OTP_VERIFIED_ADVANCED",
|
||||
actor: {
|
||||
actorId: new Types.ObjectId(actor.sub),
|
||||
actorName: `${actor.firstName || ""} ${actor.lastName || ""}`.trim(),
|
||||
actorType:
|
||||
actor?.role === RoleEnum.REGISTRAR ? "registrar" : "field_expert",
|
||||
actorName:
|
||||
`${actor.firstName || ""} ${actor.lastName || ""}`.trim(),
|
||||
actorType: this.resolveBlameHistoryActorType(actor),
|
||||
},
|
||||
metadata: {
|
||||
phoneNumber: phone,
|
||||
@@ -6383,7 +6399,7 @@ export class RequestManagementService {
|
||||
actor: {
|
||||
actorId: new Types.ObjectId(expert.sub),
|
||||
actorName: `${expert.firstName || ""} ${expert.lastName || ""}`.trim(),
|
||||
actorType: "field_expert",
|
||||
actorType: this.resolveBlameHistoryActorType(expert),
|
||||
},
|
||||
metadata: {},
|
||||
} as any);
|
||||
@@ -6589,7 +6605,7 @@ export class RequestManagementService {
|
||||
actor: {
|
||||
actorId: new Types.ObjectId(expert.sub),
|
||||
actorName: `${expert.firstName || ""} ${expert.lastName || ""}`.trim(),
|
||||
actorType: "field_expert",
|
||||
actorType: this.resolveBlameHistoryActorType(expert),
|
||||
},
|
||||
metadata: { guiltyPartyPhoneNumber: formData.guiltyPartyPhoneNumber },
|
||||
} as any);
|
||||
@@ -6653,8 +6669,7 @@ export class RequestManagementService {
|
||||
actor: {
|
||||
actorId: new Types.ObjectId(expert.sub),
|
||||
actorName: `${expert.firstName || ""} ${expert.lastName || ""}`.trim(),
|
||||
actorType:
|
||||
expert?.role === RoleEnum.REGISTRAR ? "registrar" : "field_expert",
|
||||
actorType: this.resolveBlameHistoryActorType(expert),
|
||||
},
|
||||
metadata: {
|
||||
hasLocation: true,
|
||||
@@ -6714,7 +6729,7 @@ export class RequestManagementService {
|
||||
actor: {
|
||||
actorId: new Types.ObjectId(expert.sub),
|
||||
actorName: `${expert.firstName || ""} ${expert.lastName || ""}`.trim(),
|
||||
actorType: "field_expert",
|
||||
actorType: this.resolveBlameHistoryActorType(expert),
|
||||
},
|
||||
metadata: { videoId: firstParty.evidence.videoId },
|
||||
} as any);
|
||||
@@ -6771,7 +6786,7 @@ export class RequestManagementService {
|
||||
actor: {
|
||||
actorId: new Types.ObjectId(expert.sub),
|
||||
actorName: `${expert.firstName || ""} ${expert.lastName || ""}`.trim(),
|
||||
actorType: "field_expert",
|
||||
actorType: this.resolveBlameHistoryActorType(expert),
|
||||
},
|
||||
metadata: {},
|
||||
} as any);
|
||||
@@ -7001,7 +7016,7 @@ export class RequestManagementService {
|
||||
actorId: new Types.ObjectId(String(expert.sub)),
|
||||
actorName:
|
||||
`${expert.firstName || ""} ${expert.lastName || ""}`.trim(),
|
||||
actorType: "field_expert",
|
||||
actorType: this.resolveBlameHistoryActorType(expert),
|
||||
},
|
||||
metadata: {
|
||||
accidentWay: fields.accidentWay,
|
||||
@@ -9065,7 +9080,7 @@ export class RequestManagementService {
|
||||
actorId: new Types.ObjectId(actor.sub),
|
||||
actorName:
|
||||
`${actor.firstName || ""} ${actor.lastName || ""}`.trim(),
|
||||
actorType: "field_expert",
|
||||
actorType: this.resolveBlameHistoryActorType(actor),
|
||||
},
|
||||
timestamp: new Date(),
|
||||
metadata: {
|
||||
@@ -9904,7 +9919,7 @@ export class RequestManagementService {
|
||||
actor: {
|
||||
actorId: new Types.ObjectId(expert.sub),
|
||||
actorName: `${expert.firstName || ""} ${expert.lastName || ""}`.trim(),
|
||||
actorType: "field_expert",
|
||||
actorType: this.resolveBlameHistoryActorType(expert),
|
||||
},
|
||||
metadata: { partyRole, accepted: isAccept },
|
||||
} as any);
|
||||
@@ -9971,7 +9986,7 @@ export class RequestManagementService {
|
||||
actor: {
|
||||
actorId: new Types.ObjectId(String(expert.sub)),
|
||||
actorName: `${expert.firstName || ""} ${expert.lastName || ""}`.trim(),
|
||||
actorType: "field_expert",
|
||||
actorType: this.resolveBlameHistoryActorType(expert),
|
||||
},
|
||||
metadata: { accidentWay: fields.accidentWay },
|
||||
} as any);
|
||||
@@ -10089,11 +10104,11 @@ export class RequestManagementService {
|
||||
|
||||
if (!Array.isArray(req.history)) req.history = [];
|
||||
req.history.push({
|
||||
type: "V3_BLAME_ACCIDENT_VIDEO_UPLOADED",
|
||||
type: "V5_BLAME_ACCIDENT_VIDEO_UPLOADED",
|
||||
actor: {
|
||||
actorId: new Types.ObjectId(expert.sub),
|
||||
actorName: `${expert.firstName || ""} ${expert.lastName || ""}`.trim(),
|
||||
actorType: "field_expert",
|
||||
actorType: this.resolveBlameHistoryActorType(expert),
|
||||
},
|
||||
metadata: { videoId: videoId ?? null },
|
||||
} as any);
|
||||
@@ -10118,7 +10133,7 @@ export class RequestManagementService {
|
||||
actorId: new Types.ObjectId(expert.sub),
|
||||
actorName:
|
||||
`${expert.firstName || ""} ${expert.lastName || ""}`.trim(),
|
||||
actorType: "field_expert",
|
||||
actorType: this.resolveBlameHistoryActorType(expert),
|
||||
},
|
||||
timestamp: new Date(),
|
||||
metadata: {
|
||||
@@ -10232,7 +10247,7 @@ export class RequestManagementService {
|
||||
actor: {
|
||||
actorId: new Types.ObjectId(actor.sub),
|
||||
actorName: `${actor.firstName || ""} ${actor.lastName || ""}`.trim(),
|
||||
actorType: "field_expert",
|
||||
actorType: this.resolveBlameHistoryActorType(actor),
|
||||
},
|
||||
metadata: { partyRole: role, voiceId: String((voiceDoc as any)._id) },
|
||||
} as any);
|
||||
@@ -10275,7 +10290,7 @@ export class RequestManagementService {
|
||||
actor: {
|
||||
actorId: new Types.ObjectId(actor.sub),
|
||||
actorName: `${actor.firstName || ""} ${actor.lastName || ""}`.trim(),
|
||||
actorType: "field_expert",
|
||||
actorType: this.resolveBlameHistoryActorType(actor),
|
||||
},
|
||||
metadata: { partyRole: role, location: body },
|
||||
} as any);
|
||||
@@ -10309,7 +10324,7 @@ export class RequestManagementService {
|
||||
actor: {
|
||||
actorId: new Types.ObjectId(actor.sub),
|
||||
actorName: `${actor.firstName || ""} ${actor.lastName || ""}`.trim(),
|
||||
actorType: "field_expert",
|
||||
actorType: this.resolveBlameHistoryActorType(actor),
|
||||
},
|
||||
metadata: { partyRole: role },
|
||||
} as any);
|
||||
@@ -10369,7 +10384,7 @@ export class RequestManagementService {
|
||||
actor: {
|
||||
actorId: new Types.ObjectId(actor.sub),
|
||||
actorName: `${actor.firstName || ""} ${actor.lastName || ""}`.trim(),
|
||||
actorType: "field_expert",
|
||||
actorType: this.resolveBlameHistoryActorType(actor),
|
||||
},
|
||||
metadata: {
|
||||
partyRole: role,
|
||||
@@ -10425,7 +10440,7 @@ export class RequestManagementService {
|
||||
actor: {
|
||||
actorId: new Types.ObjectId(actor.sub),
|
||||
actorName: `${actor.firstName || ""} ${actor.lastName || ""}`.trim(),
|
||||
actorType: "field_expert",
|
||||
actorType: this.resolveBlameHistoryActorType(actor),
|
||||
},
|
||||
metadata: { car: body.car, object: body.object },
|
||||
} as any);
|
||||
|
||||
Reference in New Issue
Block a user