diff --git a/src/expert-insurer/expert-insurer.controller.ts b/src/expert-insurer/expert-insurer.controller.ts index d1457c0..f0193be 100644 --- a/src/expert-insurer/expert-insurer.controller.ts +++ b/src/expert-insurer/expert-insurer.controller.ts @@ -242,24 +242,19 @@ export class ExpertInsurerController { } @ApiParam({ name: "expertId" }) - @ApiQuery({ name: "role", enum: ["claim", "blame"] }) + @ApiOperation({ + summary: "Files handled by one roster expert (summary rows)", + description: + "Resolves id against this insurer's `expert` then `damage-expert` roster. Blame branch runs when the id is on the expert roster (field experts). Each item is a small summary (`kind`, ids, statuses, dates)—not full file payloads. Claim matches use final or draft damage-expert reply.", + }) @Get("/:expertId") - async requestDetail( - @CurrentUser() insurer, - @Param("expertId") id: string, - @Query("role") role: "claim" | "blame", - ) { - if (!Types.ObjectId.isValid(new Types.ObjectId(id))) { + async requestDetail(@CurrentUser() insurer, @Param("expertId") id: string) { + if (!Types.ObjectId.isValid(id)) { throw new BadRequestException("Invalid expert ID"); } - if (!["claim", "blame"].includes(role)) { - throw new BadRequestException("Invalid role"); - } - - return await this.expertInsurerService.getAllFilesByExpertAndRole( + return await this.expertInsurerService.getAllFilesForInsurerExpert( id, - role, insurer.clientKey, ); } diff --git a/src/expert-insurer/expert-insurer.service.ts b/src/expert-insurer/expert-insurer.service.ts index a49fedc..62755e9 100644 --- a/src/expert-insurer/expert-insurer.service.ts +++ b/src/expert-insurer/expert-insurer.service.ts @@ -144,6 +144,55 @@ export class ExpertInsurerService { }; } + /** BlameCases may tie the field expert via assignment, expert-initiated id, or decision author. */ + private blameFieldExpertIdCandidates(b: any): string[] { + const raw: unknown[] = [ + b?.expert?.assignedExpertId, + b?.initiatedByFieldExpertId, + b?.expert?.decision?.decidedByExpertId, + ]; + const out = new Set(); + for (const x of raw) { + if (x != null && x !== "") out.add(String(x)); + } + return [...out]; + } + + private claimDamageExpertActorId(c: any): string | null { + const reply = + c?.evaluation?.damageExpertReplyFinal || c?.evaluation?.damageExpertReply; + const aid = reply?.actorDetail?.actorId; + if (aid == null || aid === "") return null; + return String(aid); + } + + private mapBlameFileSummaryForInsurerExpert(b: any) { + return { + kind: "blame" as const, + requestId: String(b._id), + publicId: b.publicId, + requestNo: b.requestNo, + type: b.type, + status: b.status, + blameStatus: b.blameStatus, + createdAt: b.createdAt, + updatedAt: b.updatedAt, + }; + } + + private mapClaimFileSummaryForInsurerExpert(c: any) { + return { + kind: "claim" as const, + requestId: String(c._id), + publicId: c.publicId, + requestNo: c.requestNo ?? c.requestNumber, + status: c.status, + claimStatus: c.claimStatus, + createdAt: c.createdAt, + updatedAt: c.updatedAt, + }; + } + private buildPartiesPreview(parties: any[] | undefined) { if (!Array.isArray(parties) || !parties.length) return undefined; return parties.map((p: any) => ({ @@ -365,10 +414,8 @@ export class ExpertInsurerService { return scored.sort((a, b) => b.combinedScore - a.combinedScore).slice(0, 10); } - private async assertExpertBelongsToInsurer( - expertObjectId: Types.ObjectId, - 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); const [experts, damageExperts] = await Promise.all([ @@ -376,42 +423,36 @@ export class ExpertInsurerService { this.damageExpertDbService.findAll(ckFilter as never), ]); const id = String(expertObjectId); - const ok = [...experts, ...damageExperts].some((e) => String(e._id) === id); - if (!ok) { + const onBlameRoster = experts.some((e) => String(e._id) === id); + const onClaimRoster = damageExperts.some((e) => String(e._id) === id); + if (!onBlameRoster && !onClaimRoster) { throw new ForbiddenException( "This expert is not registered under your insurance company.", ); } - } - async getAllFilesByExpertAndRole( - expertId: string, - role: "claim" | "blame", - insurerClientKey: string, - ) { - const expertObjectId = this.parseObjectId(expertId, "expert id"); - await this.assertExpertBelongsToInsurer(expertObjectId, insurerClientKey); - const clientKeyStr = String(this.getClientId(insurerClientKey)); - - if (role === "claim") { + const clientKeyStr = String(clientOid); + if (onBlameRoster) { + const blames = (await this.blameRequestDbService.find({}, { lean: true })) as any[]; + return blames + .filter( + (b) => + blameCaseTouchesClient(b, clientKeyStr) && + this.blameFieldExpertIdCandidates(b).includes(id), + ) + .map((b) => this.mapBlameFileSummaryForInsurerExpert(b)); + } + if (onClaimRoster) { const claims = (await this.claimCaseDbService.find({}, { lean: true })) as any[]; return claims .filter( (c) => claimCaseTouchesClient(c, clientKeyStr) && - c?.evaluation?.damageExpertReply?.actorDetail?.actorId === - String(expertObjectId), + this.claimDamageExpertActorId(c) === id, ) - .map((c) => this.normalizeClaimCase(c)); + .map((c) => this.mapClaimFileSummaryForInsurerExpert(c)); } - const blames = (await this.blameRequestDbService.find({}, { lean: true })) as any[]; - return blames - .filter( - (b) => - blameCaseTouchesClient(b, clientKeyStr) && - String(b?.expert?.assignedExpertId ?? "") === String(expertObjectId), - ) - .map((b) => this.normalizeBlameCase(b)); + return []; } private validateInsurerFileRating(rating: FileRating) {