This commit is contained in:
2026-04-30 10:32:53 +03:30
parent 715a9f2467
commit bffb8a3b97
2 changed files with 77 additions and 41 deletions

View File

@@ -242,24 +242,19 @@ export class ExpertInsurerController {
} }
@ApiParam({ name: "expertId" }) @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") @Get("/:expertId")
async requestDetail( async requestDetail(@CurrentUser() insurer, @Param("expertId") id: string) {
@CurrentUser() insurer, if (!Types.ObjectId.isValid(id)) {
@Param("expertId") id: string,
@Query("role") role: "claim" | "blame",
) {
if (!Types.ObjectId.isValid(new Types.ObjectId(id))) {
throw new BadRequestException("Invalid expert ID"); throw new BadRequestException("Invalid expert ID");
} }
if (!["claim", "blame"].includes(role)) { return await this.expertInsurerService.getAllFilesForInsurerExpert(
throw new BadRequestException("Invalid role");
}
return await this.expertInsurerService.getAllFilesByExpertAndRole(
id, id,
role,
insurer.clientKey, insurer.clientKey,
); );
} }

View File

@@ -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<string>();
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) { private buildPartiesPreview(parties: any[] | undefined) {
if (!Array.isArray(parties) || !parties.length) return undefined; if (!Array.isArray(parties) || !parties.length) return undefined;
return parties.map((p: any) => ({ return parties.map((p: any) => ({
@@ -365,10 +414,8 @@ export class ExpertInsurerService {
return scored.sort((a, b) => b.combinedScore - a.combinedScore).slice(0, 10); return scored.sort((a, b) => b.combinedScore - a.combinedScore).slice(0, 10);
} }
private async assertExpertBelongsToInsurer( async getAllFilesForInsurerExpert(expertId: string, insurerClientKey: string) {
expertObjectId: Types.ObjectId, const expertObjectId = this.parseObjectId(expertId, "expert id");
insurerClientKey: string,
) {
const clientOid = this.getClientId(insurerClientKey); const clientOid = this.getClientId(insurerClientKey);
const ckFilter = this.clientKeyScopeFilter(clientOid); const ckFilter = this.clientKeyScopeFilter(clientOid);
const [experts, damageExperts] = await Promise.all([ const [experts, damageExperts] = await Promise.all([
@@ -376,42 +423,36 @@ export class ExpertInsurerService {
this.damageExpertDbService.findAll(ckFilter as never), this.damageExpertDbService.findAll(ckFilter as never),
]); ]);
const id = String(expertObjectId); const id = String(expertObjectId);
const ok = [...experts, ...damageExperts].some((e) => String(e._id) === id); const onBlameRoster = experts.some((e) => String(e._id) === id);
if (!ok) { const onClaimRoster = damageExperts.some((e) => String(e._id) === id);
if (!onBlameRoster && !onClaimRoster) {
throw new ForbiddenException( throw new ForbiddenException(
"This expert is not registered under your insurance company.", "This expert is not registered under your insurance company.",
); );
} }
}
async getAllFilesByExpertAndRole( const clientKeyStr = String(clientOid);
expertId: string, if (onBlameRoster) {
role: "claim" | "blame", const blames = (await this.blameRequestDbService.find({}, { lean: true })) as any[];
insurerClientKey: string, return blames
) { .filter(
const expertObjectId = this.parseObjectId(expertId, "expert id"); (b) =>
await this.assertExpertBelongsToInsurer(expertObjectId, insurerClientKey); blameCaseTouchesClient(b, clientKeyStr) &&
const clientKeyStr = String(this.getClientId(insurerClientKey)); this.blameFieldExpertIdCandidates(b).includes(id),
)
if (role === "claim") { .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 return claims
.filter( .filter(
(c) => (c) =>
claimCaseTouchesClient(c, clientKeyStr) && claimCaseTouchesClient(c, clientKeyStr) &&
c?.evaluation?.damageExpertReply?.actorDetail?.actorId === this.claimDamageExpertActorId(c) === id,
String(expertObjectId),
) )
.map((c) => this.normalizeClaimCase(c)); .map((c) => this.mapClaimFileSummaryForInsurerExpert(c));
} }
const blames = (await this.blameRequestDbService.find({}, { lean: true })) as any[]; return [];
return blames
.filter(
(b) =>
blameCaseTouchesClient(b, clientKeyStr) &&
String(b?.expert?.assignedExpertId ?? "") === String(expertObjectId),
)
.map((b) => this.normalizeBlameCase(b));
} }
private validateInsurerFileRating(rating: FileRating) { private validateInsurerFileRating(rating: FileRating) {