diff --git a/src/expert-insurer/expert-insurer.service.ts b/src/expert-insurer/expert-insurer.service.ts index 135a7b5..5fab25d 100644 --- a/src/expert-insurer/expert-insurer.service.ts +++ b/src/expert-insurer/expert-insurer.service.ts @@ -51,6 +51,10 @@ import { } from "src/helpers/outer-damage-parts"; import { ClaimVehicleTypeV2 } from "src/static/outer-car-parts-catalog"; import { ClaimSignDbService } from "src/claim-request-management/entites/db-service/claim-sign.db.service"; +import { + extractExpertNamesFromBlame, + extractExpertNamesFromClaim, +} from "./helper/insurer.helper"; @Injectable() export class ExpertInsurerService { @@ -70,7 +74,8 @@ export class ExpertInsurerService { ) {} private getClientId(actorOrId: any): Types.ObjectId { - const raw = typeof actorOrId === "string" ? actorOrId : actorOrId?.clientKey; + const raw = + typeof actorOrId === "string" ? actorOrId : actorOrId?.clientKey; if (!raw || !Types.ObjectId.isValid(raw)) { throw new BadRequestException("Client key is required"); } @@ -91,15 +96,22 @@ export class ExpertInsurerService { tenantId: Types.ObjectId, expertIds: string[], ): Promise> { - const events = await this.expertFileActivityDbService.findByTenantAndExperts( - tenantId, - expertIds, - ); - const stateByExpertFile = new Map(); + const events = + await this.expertFileActivityDbService.findByTenantAndExperts( + tenantId, + expertIds, + ); + const stateByExpertFile = new Map< + string, + { checked: boolean; handled: boolean } + >(); for (const e of events) { const key = `${String(e.expertId)}:${String(e.fileId)}`; - const prev = stateByExpertFile.get(key) ?? { checked: false, handled: false }; + const prev = stateByExpertFile.get(key) ?? { + checked: false, + handled: false, + }; if (e.eventType === ExpertFileActivityType.CHECKED) { if (!prev.handled) prev.checked = true; } else if (e.eventType === ExpertFileActivityType.UNCHECKED) { @@ -111,13 +123,17 @@ export class ExpertInsurerService { stateByExpertFile.set(key, prev); } - const result: Record = {}; + const result: Record< + string, + { totalHandled: number; totalChecked: number } + > = {}; for (const expertId of expertIds) { result[expertId] = { totalHandled: 0, totalChecked: 0 }; } for (const [key, st] of stateByExpertFile.entries()) { const [expertId] = key.split(":"); - if (!result[expertId]) result[expertId] = { totalHandled: 0, totalChecked: 0 }; + if (!result[expertId]) + result[expertId] = { totalHandled: 0, totalChecked: 0 }; if (st.handled) result[expertId].totalHandled += 1; else if (st.checked) result[expertId].totalChecked += 1; } @@ -265,18 +281,18 @@ export class ExpertInsurerService { return { ...d, guiltyPartyId: - guilty != null && typeof (guilty as { toString?: () => string }).toString === "function" + guilty != null && + typeof (guilty as { toString?: () => string }).toString === "function" ? String(guilty) : guilty, decidedByExpertId: decidedBy != null && - typeof (decidedBy as { toString?: () => string }).toString === "function" + typeof (decidedBy as { toString?: () => string }).toString === + "function" ? String(decidedBy) : decidedBy, decidedAt: - decidedAt instanceof Date - ? decidedAt.toISOString() - : decidedAt, + decidedAt instanceof Date ? decidedAt.toISOString() : decidedAt, }; } @@ -300,7 +316,8 @@ export class ExpertInsurerService { if (!Array.isArray(parties) || parties.length === 0) return out; const first = - (parties as any[]).find((p: any) => p?.role === PartyRole.FIRST) ?? (parties as any[])[0]; + (parties as any[]).find((p: any) => p?.role === PartyRole.FIRST) ?? + (parties as any[])[0]; const cbf = first?.carBodyFirstForm; delete out.blameStatus; if (cbf && typeof cbf === "object") { @@ -340,7 +357,8 @@ export class ExpertInsurerService { ) { if (!person || typeof person !== "object") return person; const cid = - person.clientId?.toString?.() ?? (person.clientId != null ? String(person.clientId) : undefined); + person.clientId?.toString?.() ?? + (person.clientId != null ? String(person.clientId) : undefined); return { ...person, userId: person.userId?.toString?.() ?? undefined, @@ -412,7 +430,11 @@ export class ExpertInsurerService { side: sp.side, label_fa: sp.label_fa, catalogKey: sp.catalogKey, - captured: hasDamagedPartCapture(damagedPartsDataExpert, ck, selectedNormExpert), + captured: hasDamagedPartCapture( + damagedPartsDataExpert, + ck, + selectedNormExpert, + ), path: cap?.path, fileName: cap?.fileName, url, @@ -439,7 +461,9 @@ export class ExpertInsurerService { }); } - private async claimSignLinkFromId(signDetailId: unknown): Promise { + private async claimSignLinkFromId( + signDetailId: unknown, + ): Promise { if (signDetailId == null || signDetailId === "") return undefined; const id = String(signDetailId); if (!Types.ObjectId.isValid(id)) return undefined; @@ -454,7 +478,10 @@ export class ExpertInsurerService { evaluation: Record | undefined, ): Promise | undefined> { if (!evaluation || typeof evaluation !== "object") return evaluation; - const ev = JSON.parse(JSON.stringify(evaluation)) as Record; + const ev = JSON.parse(JSON.stringify(evaluation)) as Record< + string, + unknown + >; const enrichReply = async (reply: any) => { if (!reply || typeof reply !== "object") return; @@ -487,14 +514,18 @@ export class ExpertInsurerService { const evidence = party.evidence as Record; if (evidence.videoId) { - const videoDoc = await this.blameVideoDbService.findById(String(evidence.videoId)); + const videoDoc = await this.blameVideoDbService.findById( + String(evidence.videoId), + ); if (videoDoc?.path) evidence.videoUrl = buildFileLink(videoDoc.path); } if (evidence.voices && Array.isArray(evidence.voices)) { const voiceUrls: string[] = []; for (const voiceId of evidence.voices) { - const voiceDoc = await this.blameVoiceDbService.findById(String(voiceId)); + const voiceDoc = await this.blameVoiceDbService.findById( + String(voiceId), + ); if (voiceDoc?.path) voiceUrls.push(buildFileLink(voiceDoc.path)); } evidence.voiceUrls = voiceUrls; @@ -511,14 +542,20 @@ export class ExpertInsurerService { doc.parties = enrichBlamePartiesForAgreementView(doc); const mergedParties = doc.parties as any[]; - const clientIds = mergedParties.map((p) => p?.person?.clientId?.toString?.()).filter(Boolean); + const clientIds = mergedParties + .map((p) => p?.person?.clientId?.toString?.()) + .filter(Boolean); const clientNames = await this.clientNamesByClientIds(clientIds); - doc.parties = mergedParties.map((p) => this.mapPartyForInsurerFull(p, clientNames)); + doc.parties = mergedParties.map((p) => + this.mapPartyForInsurerFull(p, clientNames), + ); this.enrichPartiesConfirmationSignatures(doc.parties as any[]); if (doc.expert && typeof doc.expert === "object") { const ex = { ...(doc.expert as Record) }; - const serialized = this.serializeBlameExpertDecisionForInsurer(ex.decision); + const serialized = this.serializeBlameExpertDecisionForInsurer( + ex.decision, + ); if (serialized !== undefined) ex.decision = serialized; doc.expert = ex; } @@ -551,7 +588,8 @@ export class ExpertInsurerService { ? Array.from(requiredDocs.keys()) : Object.keys(requiredDocs); for (const k of keys as string[]) { - const row = requiredDocs instanceof Map ? requiredDocs.get(k) : requiredDocs[k]; + const row = + requiredDocs instanceof Map ? requiredDocs.get(k) : requiredDocs[k]; requiredDocumentsStatus[k] = { uploaded: !!row?.uploaded, fileId: row?.fileId?.toString?.(), @@ -576,7 +614,9 @@ export class ExpertInsurerService { }; } - const carTypeExpert = (claim.vehicle as any)?.carType as ClaimVehicleTypeV2 | undefined; + const carTypeExpert = (claim.vehicle as any)?.carType as + | ClaimVehicleTypeV2 + | undefined; const selectedNormExpert = normalizeDamageSelectedParts( (claim.damage as any)?.selectedParts, carTypeExpert, @@ -646,13 +686,17 @@ export class ExpertInsurerService { }; } - const evaluationEnriched = await this.enrichClaimEvaluationForInsurer(evaluation); + const evaluationEnriched = + await this.enrichClaimEvaluationForInsurer(evaluation); const vehicleSanitized = claim.vehicle ? this.sanitizeVehicleInquiry(claim.vehicle as any) : undefined; const vehicleOut = vehicleSanitized ? (() => { - const { carType: _omit, ...rest } = vehicleSanitized as Record; + const { carType: _omit, ...rest } = vehicleSanitized as Record< + string, + unknown + >; return rest; })() : undefined; @@ -705,9 +749,11 @@ export class ExpertInsurerService { ).filter((v): v is number => typeof v === "number" && !isNaN(v)) : []; const userValues = userRating - ? [userRating.progressSpeed, userRating.registrationEase, userRating.overallEvaluation].filter( - (v) => typeof v === "number" && !isNaN(v), - ) + ? [ + userRating.progressSpeed, + userRating.registrationEase, + userRating.overallEvaluation, + ].filter((v) => typeof v === "number" && !isNaN(v)) : []; const insurerAvg = insurerValues.length ? insurerValues.reduce((a, b) => a + b, 0) / insurerValues.length @@ -715,30 +761,50 @@ export class ExpertInsurerService { const userAvg = userValues.length ? userValues.reduce((a, b) => a + b, 0) / userValues.length : null; - const scores = [insurerAvg, userAvg].filter((v): v is number => typeof v === "number"); + const scores = [insurerAvg, userAvg].filter( + (v): v is number => typeof v === "number", + ); if (!scores.length) return null; - return parseFloat((scores.reduce((a, b) => a + b, 0) / scores.length).toFixed(2)); + return parseFloat( + (scores.reduce((a, b) => a + b, 0) / scores.length).toFixed(2), + ); } - private async getClientBlameFiles(clientObjectId: Types.ObjectId): Promise { - const all = (await this.blameRequestDbService.find({}, { lean: true })) as any[]; + private async getClientBlameFiles( + clientObjectId: Types.ObjectId, + ): Promise { + const all = (await this.blameRequestDbService.find( + {}, + { lean: true }, + )) as any[]; const idStr = String(clientObjectId); return all .filter((f) => - (f?.parties || []).some((p) => String(p?.person?.clientId || "") === idStr), + (f?.parties || []).some( + (p) => String(p?.person?.clientId || "") === idStr, + ), ) .map((f) => this.normalizeBlameCase(f)); } - private async getClientClaimFiles(clientObjectId: Types.ObjectId): Promise { - const all = (await this.claimCaseDbService.find({}, { lean: true })) as any[]; + private async getClientClaimFiles( + clientObjectId: Types.ObjectId, + ): Promise { + const all = (await this.claimCaseDbService.find( + {}, + { lean: true }, + )) as any[]; const idStr = String(clientObjectId); return all .filter((f) => claimCaseTouchesClient(f, idStr)) .map((f) => this.normalizeClaimCase(f)); } - async retrieveAllExpertsOfClient(actor, currentPage: number, countPerPage: number) { + async retrieveAllExpertsOfClient( + actor, + currentPage: number, + countPerPage: number, + ) { const clientObjectId = this.getClientId(actor); const ckFilter = this.clientKeyScopeFilter(clientObjectId); const [experts, damageExperts, blameFiles, claimFiles] = await Promise.all([ @@ -755,18 +821,23 @@ export class ExpertInsurerService { expertIds, ); const expertTotalRatingsMap: Record = {}; - const expertRatingsByCategoryMap: Record> = {}; + const expertRatingsByCategoryMap: Record< + string, + Record + > = {}; const processRatings = (expertId: string | undefined, file: any) => { if (!expertId) return; const rating = file?.rating; const combinedScore = this.getCombinedFileScore(file); if (combinedScore !== null) { - if (!expertTotalRatingsMap[expertId]) expertTotalRatingsMap[expertId] = []; + if (!expertTotalRatingsMap[expertId]) + expertTotalRatingsMap[expertId] = []; expertTotalRatingsMap[expertId].push(combinedScore); } if (!rating || typeof rating !== "object") return; - if (!expertRatingsByCategoryMap[expertId]) expertRatingsByCategoryMap[expertId] = {}; + if (!expertRatingsByCategoryMap[expertId]) + expertRatingsByCategoryMap[expertId] = {}; for (const [category, value] of Object.entries(rating)) { if (category === "botRating") continue; if (typeof value === "number" && !isNaN(value)) { @@ -790,7 +861,9 @@ export class ExpertInsurerService { const totalRatings = expertTotalRatingsMap[expertIdStr] || []; const overallAverageRating = totalRatings.length ? parseFloat( - (totalRatings.reduce((a, b) => a + b, 0) / totalRatings.length).toFixed(2), + ( + totalRatings.reduce((a, b) => a + b, 0) / totalRatings.length + ).toFixed(2), ) : null; const averageRatingsByCategory: Record = {}; @@ -869,7 +942,9 @@ export class ExpertInsurerService { * combined insurer + user ratings. */ async getTopFilesForClient(insurerId: string): Promise { - const claimFiles = await this.getClientClaimFiles(this.getClientId(insurerId)); + const claimFiles = await this.getClientClaimFiles( + this.getClientId(insurerId), + ); const scored = claimFiles .map((file) => { const combinedScore = this.getCombinedFileScore(file); @@ -877,10 +952,15 @@ export class ExpertInsurerService { return { ...file, combinedScore }; }) .filter((f) => f !== null); - return scored.sort((a, b) => b.combinedScore - a.combinedScore).slice(0, 10); + return scored + .sort((a, b) => b.combinedScore - a.combinedScore) + .slice(0, 10); } - async getAllFilesForInsurerExpert(expertId: string, insurerClientKey: string) { + async getAllFilesForInsurerExpert( + expertId: string, + insurerClientKey: string, + ) { const expertObjectId = this.parseObjectId(expertId, "expert id"); const clientOid = this.getClientId(insurerClientKey); const ckFilter = this.clientKeyScopeFilter(clientOid); @@ -899,7 +979,10 @@ export class ExpertInsurerService { const clientKeyStr = String(clientOid); if (onBlameRoster) { - const blames = (await this.blameRequestDbService.find({}, { lean: true })) as any[]; + const blames = (await this.blameRequestDbService.find( + {}, + { lean: true }, + )) as any[]; return blames .filter( (b) => @@ -909,7 +992,10 @@ export class ExpertInsurerService { .map((b) => this.mapBlameFileSummaryForInsurerExpert(b)); } if (onClaimRoster) { - const claims = (await this.claimCaseDbService.find({}, { lean: true })) as any[]; + const claims = (await this.claimCaseDbService.find( + {}, + { lean: true }, + )) as any[]; return claims .filter( (c) => @@ -932,7 +1018,9 @@ export class ExpertInsurerService { for (const key of required) { const value = rating?.[key]; if (typeof value !== "number" || value < 0 || value > 5) { - throw new BadRequestException(`${key} must be a number between 0 and 5`); + throw new BadRequestException( + `${key} must be a number between 0 and 5`, + ); } } } @@ -957,8 +1045,12 @@ export class ExpertInsurerService { 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); + 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"); @@ -971,7 +1063,10 @@ export class ExpertInsurerService { } = { publicId }; if (claim) { - const claimId = this.parseObjectId(String((claim as any)._id), "claim id"); + const claimId = this.parseObjectId( + String((claim as any)._id), + "claim id", + ); const updated = await this.claimCaseDbService.findByIdAndUpdate(claimId, { $set: { "evaluation.rating": rating }, }); @@ -983,10 +1078,16 @@ export class ExpertInsurerService { } if (blame) { - const blameId = this.parseObjectId(String((blame as any)._id), "blame id"); - const updated = await this.blameRequestDbService.findByIdAndUpdate(blameId, { - $set: { "expert.rating": rating }, - }); + const blameId = this.parseObjectId( + String((blame as any)._id), + "blame id", + ); + const updated = await this.blameRequestDbService.findByIdAndUpdate( + blameId, + { + $set: { "expert.rating": rating }, + }, + ); if (!updated) throw new NotFoundException("Blame file not found"); out.blame = { requestId: String((updated as any)._id), @@ -1034,6 +1135,7 @@ export class ExpertInsurerService { plate?: string; }; }>; + expertNames?: string[]; createdAt?: Date | string; updatedAt?: Date | string; }; @@ -1043,6 +1145,7 @@ export class ExpertInsurerService { status?: string; claimStatus?: string; currentStep?: string; + expertNames?: string[]; createdAt?: Date | string; updatedAt?: Date | string; }; @@ -1058,10 +1161,13 @@ export class ExpertInsurerService { const partiesPreview = this.buildPartiesPreview((b as any).parties); prev.fileType = prev.fileType ?? (b as any).type; prev.blame = { - requestId: (b as any)?._id?.toString?.() || String((b as any)?._id || ""), - requestNo: (b as any).requestNo || String((b as any).requestNumber || ""), + requestId: + (b as any)?._id?.toString?.() || String((b as any)?._id || ""), + requestNo: + (b as any).requestNo || String((b as any).requestNumber || ""), status: (b as any).status, parties: partiesPreview, + expertNames: extractExpertNamesFromBlame(b), // ← add createdAt: (b as any).createdAt, updatedAt: (b as any).updatedAt, }; @@ -1075,14 +1181,19 @@ export class ExpertInsurerService { const key = String((c as any).publicId || (c as any)._id || ""); if (!key) continue; const prev = byPublicId.get(key) ?? { publicId: key }; - const claimPartiesPreview = this.buildPartiesPreview((c as any)?.snapshot?.parties); + const claimPartiesPreview = this.buildPartiesPreview( + (c as any)?.snapshot?.parties, + ); prev.fileType = prev.fileType ?? (c as any)?.snapshot?.accident?.type; prev.claim = { - requestId: (c as any)?._id?.toString?.() || String((c as any)?._id || ""), - requestNo: (c as any).requestNo || String((c as any).requestNumber || ""), + requestId: + (c as any)?._id?.toString?.() || String((c as any)?._id || ""), + requestNo: + (c as any).requestNo || String((c as any).requestNumber || ""), status: (c as any).status, claimStatus: (c as any).claimStatus, currentStep: (c as any).currentStep, + expertNames: extractExpertNamesFromClaim(c), // ← add createdAt: (c as any).createdAt, updatedAt: (c as any).updatedAt, }; @@ -1127,8 +1238,12 @@ export class ExpertInsurerService { 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); + 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"); @@ -1303,13 +1418,17 @@ export class ExpertInsurerService { const email = payload.email.trim().toLowerCase(); const existingByEmail = await this.expertDbService.findOne({ email }); - const existingDamageByEmail = await this.damageExpertDbService.findOne({ email }); + const existingDamageByEmail = await this.damageExpertDbService.findOne({ + email, + }); if (existingByEmail || existingDamageByEmail) { throw new ConflictException("An expert with this email already exists."); } const nationalCode = payload.nationalCode.trim(); - const existingByNational = await this.expertDbService.findOne({ nationalCode }); + const existingByNational = await this.expertDbService.findOne({ + nationalCode, + }); const existingDamageByNational = await this.damageExpertDbService.findOne({ nationalCode, }); @@ -1388,7 +1507,14 @@ export class ExpertInsurerService { // Calculate current month date range const now = new Date(); const monthStart = new Date(now.getFullYear(), now.getMonth(), 1); - const monthEnd = new Date(now.getFullYear(), now.getMonth() + 1, 0, 23, 59, 59); + const monthEnd = new Date( + now.getFullYear(), + now.getMonth() + 1, + 0, + 23, + 59, + 59, + ); // Filter files created this month const filesThisMonth = claimFiles.filter((file) => { @@ -1416,7 +1542,9 @@ export class ExpertInsurerService { insurerRating.evaluationTimeliness, insurerRating.accidentCauseAccuracy, insurerRating.guiltyVehicleIdentification, - ].filter((val): val is number => typeof val === "number" && !isNaN(val)); + ].filter( + (val): val is number => typeof val === "number" && !isNaN(val), + ); if (insurerValues.length > 0) { filesWithInsurerRating++; @@ -1428,7 +1556,11 @@ export class ExpertInsurerService { // Check for bot rating (if botRating field exists in rating object) const botRating = (insurerRating as any)?.botRating; - if (botRating !== undefined && !isNaN(botRating) && typeof botRating === "number") { + if ( + botRating !== undefined && + !isNaN(botRating) && + typeof botRating === "number" + ) { filesWithBotRating++; totalBotRatings += botRating; } @@ -1446,7 +1578,7 @@ export class ExpertInsurerService { // Calculate percentages const totalFiles = claimFiles.length; - + // Calculate average insurer rating (excluding botRating) and average bot rating const averageInsurerRating = filesWithInsurerRating > 0 @@ -1511,12 +1643,15 @@ export class ExpertInsurerService { const clientObjectId = this.getClientId(insuranceId); const { fromDate, toDate } = this.parseDateRange(opts?.from, opts?.to); const isActiveFilter = this.parseOptionalBoolean(opts?.isActive); - const branches = await this.branchDbService.findAllWithFilters(String(clientObjectId), { - search: opts?.search, - from: fromDate, - to: toDate, - isActive: isActiveFilter, - }); + const branches = await this.branchDbService.findAllWithFilters( + String(clientObjectId), + { + search: opts?.search, + from: fromDate, + to: toDate, + isActive: isActiveFilter, + }, + ); const branchIdSet = new Set(branches.map((b: any) => String(b._id))); const ckFilter = this.clientKeyScopeFilter(clientObjectId); @@ -1536,7 +1671,8 @@ export class ExpertInsurerService { const eid = String(e?._id || ""); if (!eid) continue; expertBranchById.set(eid, bid); - if (!activeExpertsByBranch.has(bid)) activeExpertsByBranch.set(bid, new Set()); + if (!activeExpertsByBranch.has(bid)) + activeExpertsByBranch.set(bid, new Set()); activeExpertsByBranch.get(bid)!.add(eid); } for (const e of damageExperts as any[]) { @@ -1545,14 +1681,20 @@ export class ExpertInsurerService { const eid = String(e?._id || ""); if (!eid) continue; claimExpertBranchById.set(eid, bid); - if (!activeExpertsByBranch.has(bid)) activeExpertsByBranch.set(bid, new Set()); + if (!activeExpertsByBranch.has(bid)) + activeExpertsByBranch.set(bid, new Set()); activeExpertsByBranch.get(bid)!.add(eid); } const completedByBranch = new Map>(); const handlingByBranch = new Map>(); - const { blameInHandling, claimInHandling } = this.buildHandlingBranchStatusSets(); - const addPublicId = (map: Map>, branchId: string, fileKey: string) => { + const { blameInHandling, claimInHandling } = + this.buildHandlingBranchStatusSets(); + const addPublicId = ( + map: Map>, + branchId: string, + fileKey: string, + ) => { if (!map.has(branchId)) map.set(branchId, new Set()); map.get(branchId)!.add(fileKey); }; @@ -1565,8 +1707,10 @@ export class ExpertInsurerService { const fileKey = String(b?.publicId || b?._id || ""); if (!fileKey) continue; const status = String(b?.status || ""); - if (status === CaseStatus.COMPLETED) addPublicId(completedByBranch, bid, fileKey); - if (blameInHandling.has(status)) addPublicId(handlingByBranch, bid, fileKey); + if (status === CaseStatus.COMPLETED) + addPublicId(completedByBranch, bid, fileKey); + if (blameInHandling.has(status)) + addPublicId(handlingByBranch, bid, fileKey); } for (const c of claimFiles as any[]) { @@ -1577,8 +1721,10 @@ export class ExpertInsurerService { const fileKey = String(c?.publicId || c?._id || ""); if (!fileKey) continue; const status = String(c?.status || ""); - if (status === ClaimCaseStatus.COMPLETED) addPublicId(completedByBranch, bid, fileKey); - if (claimInHandling.has(status)) addPublicId(handlingByBranch, bid, fileKey); + if (status === ClaimCaseStatus.COMPLETED) + addPublicId(completedByBranch, bid, fileKey); + if (claimInHandling.has(status)) + addPublicId(handlingByBranch, bid, fileKey); } const list = branches.map((branch: any) => { @@ -1654,10 +1800,7 @@ export class ExpertInsurerService { this.isInDateRange((f as any)?.createdAt, fromDate, toDate), ); - const fileMap = new Map< - string, - { blame?: any; claim?: any } - >(); + const fileMap = new Map(); for (const b of blameFiles) { const key = String((b as any).publicId || (b as any)._id || ""); @@ -1686,8 +1829,7 @@ export class ExpertInsurerService { completed += 1; } - const blameUnderReview = - blameStatus === CaseStatus.WAITING_FOR_EXPERT; + const blameUnderReview = blameStatus === CaseStatus.WAITING_FOR_EXPERT; const claimUnderReview = claimStatus === ClaimCaseStatus.WAITING_FOR_DAMAGE_EXPERT || claimStatus === ClaimCaseStatus.EXPERT_REVIEWING; diff --git a/src/expert-insurer/helper/insurer.helper.ts b/src/expert-insurer/helper/insurer.helper.ts new file mode 100644 index 0000000..992e923 --- /dev/null +++ b/src/expert-insurer/helper/insurer.helper.ts @@ -0,0 +1,42 @@ +export function extractExpertNamesFromBlame(b: any): string[] { + const names = new Set(); + + const add = (v: unknown) => { + if (typeof v === "string" && v.trim()) names.add(v.trim()); + }; + + add(b?.workflow?.assignedForReviewBy?.actorName); + add(b?.workflow?.lockedBy?.actorName); + + // History entries from expert actions + for (const h of b?.history ?? []) { + if ( + h?.actor?.actorType === "field_expert" || + h?.actor?.actorType === "damage_expert" + ) { + add(h?.actor?.actorName); + } + } + + return [...names]; +} + +export function extractExpertNamesFromClaim(c: any): string[] { + const names = new Set(); + + const add = (v: unknown) => { + if (typeof v === "string" && v.trim()) names.add(v.trim()); + }; + + add(c?.workflow?.assignedForReviewBy?.actorName); + add(c?.workflow?.lockedBy?.actorName); + + // Evaluation snapshots + add(c?.evaluation?.damageExpertResend?.expertProfileSnapshot?.fullName); + add(c?.evaluation?.damageExpertReply?.expertProfileSnapshot?.fullName); + add(c?.evaluation?.damageExpertReplyFinal?.expertProfileSnapshot?.fullName); + add(c?.evaluation?.factorValidationExpertProfileSnapshot?.fullName); + add(c?.evaluation?.inPersonVisitExpertProfileSnapshot?.fullName); + + return [...names]; +}