From f999313476b1d86cda160d12d2072e4518d7fcc1 Mon Sep 17 00:00:00 2001 From: SepehrYahyaee <7heycallmegray@gmail.com> Date: Tue, 28 Apr 2026 15:22:04 +0330 Subject: [PATCH] YARA-853 --- .../expert-insurer.controller.ts | 12 + src/expert-insurer/expert-insurer.service.ts | 242 +++++++++++++++++- 2 files changed, 247 insertions(+), 7 deletions(-) diff --git a/src/expert-insurer/expert-insurer.controller.ts b/src/expert-insurer/expert-insurer.controller.ts index c8e4727..926306a 100644 --- a/src/expert-insurer/expert-insurer.controller.ts +++ b/src/expert-insurer/expert-insurer.controller.ts @@ -116,6 +116,18 @@ export class ExpertInsurerController { ); } + @Get("files/:publicId") + @ApiParam({ name: "publicId" }) + async getFileDetailsByPublicId( + @CurrentUser() insurer, + @Param("publicId") publicId: string, + ) { + return await this.expertInsurerService.retrieveFileDetailsByPublicId( + insurer.clientKey, + publicId, + ); + } + @Get("top-files") async getTopFiles(@CurrentUser() insurer) { return await this.expertInsurerService.getTopFilesForClient( diff --git a/src/expert-insurer/expert-insurer.service.ts b/src/expert-insurer/expert-insurer.service.ts index af702be..aa7e550 100644 --- a/src/expert-insurer/expert-insurer.service.ts +++ b/src/expert-insurer/expert-insurer.service.ts @@ -134,6 +134,19 @@ export class ExpertInsurerService { }; } + private buildPartiesPreview(parties: any[] | undefined) { + if (!Array.isArray(parties) || !parties.length) return undefined; + return parties.map((p: any) => ({ + role: p?.role, + fullName: p?.person?.fullName, + vehicle: { + carName: p?.vehicle?.carName ?? p?.vehicle?.name, + carModel: p?.vehicle?.carModel ?? p?.vehicle?.model, + plate: p?.vehicle?.plate ?? p?.vehicle?.plateId, + }, + })); + } + private getCombinedFileScore(file: any): number | null { const insurerRating = file?.rating; const userRating = file?.userRating; @@ -165,11 +178,6 @@ export class ExpertInsurerService { .filter((f) => (f?.parties || []).some((p) => String(p?.person?.clientId || "") === idStr), ) - .filter( - (f) => - f?.status !== CaseStatus.OPEN && - f?.status !== CaseStatus.WAITING_FOR_SECOND_PARTY, - ) .map((f) => this.normalizeBlameCase(f)); } @@ -177,7 +185,7 @@ export class ExpertInsurerService { const all = (await this.claimCaseDbService.find({}, { lean: true })) as any[]; const idStr = String(clientObjectId); return all - .filter((f) => String(f?.owner?.userClientKey || "") === idStr) + .filter((f) => claimCaseTouchesClient(f, idStr)) .map((f) => this.normalizeClaimCase(f)); } @@ -405,7 +413,227 @@ export class ExpertInsurerService { this.getClientBlameFiles(id), this.getClientClaimFiles(id), ]); - return { blameFiles, claimFiles }; + + const byPublicId = new Map< + string, + { + publicId: string; + fileType?: string; + parties?: Array<{ + role?: string; + fullName?: string; + vehicle?: { + carName?: string; + carModel?: string; + plate?: string; + }; + }>; + blame?: { + requestId?: string; + requestNo?: string; + status?: string; + parties?: Array<{ + role?: string; + fullName?: string; + vehicle?: { + carName?: string; + carModel?: string; + plate?: string; + }; + }>; + createdAt?: Date | string; + updatedAt?: Date | string; + }; + claim?: { + requestId?: string; + requestNo?: string; + status?: string; + claimStatus?: string; + currentStep?: string; + createdAt?: Date | string; + updatedAt?: Date | string; + }; + createdAt?: Date | string; + updatedAt?: Date | string; + } + >(); + + for (const b of blameFiles) { + const key = String((b as any).publicId || (b as any)._id || ""); + if (!key) continue; + const prev = byPublicId.get(key) ?? { publicId: key }; + 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 || ""), + status: (b as any).status, + parties: partiesPreview, + createdAt: (b as any).createdAt, + updatedAt: (b as any).updatedAt, + }; + prev.parties = prev.parties ?? partiesPreview; + prev.createdAt = prev.createdAt ?? (b as any).createdAt; + prev.updatedAt = (b as any).updatedAt ?? prev.updatedAt; + byPublicId.set(key, prev); + } + + for (const c of claimFiles) { + 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); + 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 || ""), + status: (c as any).status, + claimStatus: (c as any).claimStatus, + currentStep: (c as any).currentStep, + createdAt: (c as any).createdAt, + updatedAt: (c as any).updatedAt, + }; + prev.parties = prev.parties ?? claimPartiesPreview; + prev.createdAt = prev.createdAt ?? (c as any).createdAt; + prev.updatedAt = (c as any).updatedAt ?? prev.updatedAt; + byPublicId.set(key, prev); + } + + const files = Array.from(byPublicId.values()) + .map((f) => ({ + publicId: f.publicId, + fileType: f.fileType, + hasClaim: !!f.claim, + parties: f.parties, + blame: f.blame, + claim: f.claim, + createdAt: f.createdAt, + updatedAt: f.updatedAt, + })) + .sort((a, b) => { + const at = a.updatedAt ? new Date(a.updatedAt).getTime() : 0; + const bt = b.updatedAt ? new Date(b.updatedAt).getTime() : 0; + return bt - at; + }); + + return { + total: files.length, + files, + }; + } + + async retrieveFileDetailsByPublicId(insurerId: string, publicId: string) { + 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 parties = Array.isArray((blame as any)?.parties) ? (blame as any).parties : []; + const firstParty = parties.find((p: any) => p?.role === "FIRST"); + const secondParty = parties.find((p: any) => p?.role === "SECOND"); + + const overview = { + publicId, + requestNo: + (blame as any)?.requestNo || + (claim as any)?.requestNo || + (blame as any)?.requestNumber || + (claim as any)?.requestNumber, + type: (blame as any)?.type, + hasClaim: !!claim, + blameStatus: (blame as any)?.status, + claimStatus: (claim as any)?.status, + claimReviewStatus: (claim as any)?.claimStatus, + createdAt: (blame as any)?.createdAt ?? (claim as any)?.createdAt, + updatedAt: (claim as any)?.updatedAt ?? (blame as any)?.updatedAt, + }; + + const blameDetails = blame + ? { + requestId: + (blame as any)?._id?.toString?.() || String((blame as any)?._id || ""), + requestNo: + (blame as any).requestNo || String((blame as any).requestNumber || ""), + status: (blame as any).status, + blameStatus: (blame as any).blameStatus, + workflow: (blame as any).workflow, + parties: { + first: firstParty + ? { + fullName: firstParty?.person?.fullName, + phoneNumber: firstParty?.person?.phoneNumber, + role: firstParty?.role, + vehicle: firstParty?.vehicle, + } + : undefined, + second: secondParty + ? { + fullName: secondParty?.person?.fullName, + phoneNumber: secondParty?.person?.phoneNumber, + role: secondParty?.role, + vehicle: secondParty?.vehicle, + } + : undefined, + }, + expert: (blame as any).expert + ? { + assignedExpertId: (blame as any).expert?.assignedExpertId, + decision: (blame as any).expert?.decision, + resend: (blame as any).expert?.resend, + rating: (blame as any).expert?.rating, + } + : undefined, + createdAt: (blame as any).createdAt, + updatedAt: (blame as any).updatedAt, + } + : undefined; + + // Keep claim section claim-specific to avoid duplicating shared data already in blame/overview. + const claimDetails = claim + ? { + requestId: + (claim as any)?._id?.toString?.() || String((claim as any)?._id || ""), + requestNo: + (claim as any).requestNo || String((claim as any).requestNumber || ""), + status: (claim as any).status, + claimStatus: (claim as any).claimStatus, + workflow: { + currentStep: (claim as any).currentStep, + nextStep: (claim as any).nextStep, + }, + evaluation: { + damageExpertReply: (claim as any).damageExpertReply, + damageExpertReplyFinal: (claim as any).damageExpertReplyFinal, + damageExpertResend: (claim as any).damageExpertResend, + objection: (claim as any).objection, + priceDrop: (claim as any).priceDrop, + visitLocation: (claim as any).visitLocation, + }, + userResendDocuments: (claim as any).userResendDocuments, + userRating: (claim as any).userRating, + insurerRating: (claim as any).rating, + createdAt: (claim as any).createdAt, + updatedAt: (claim as any).updatedAt, + } + : undefined; + + return { + overview, + blame: blameDetails, + claim: claimDetails, + }; } async addBranch(clientKey: string, branchDto: CreateBranchDto) {