forked from Yara724/api
Added extra fields for insurer
This commit is contained in:
@@ -98,6 +98,7 @@ import { JwtModule } from "@nestjs/jwt";
|
||||
DamageImageDbService,
|
||||
VideoCaptureDbService,
|
||||
ClaimRequiredDocumentDbService,
|
||||
ClaimSignDbService,
|
||||
],
|
||||
})
|
||||
export class ClaimRequestManagementModule {}
|
||||
|
||||
@@ -29,6 +29,28 @@ import { ExpertFileActivityType } from "src/users/entities/schema/expert-file-ac
|
||||
import { HashService } from "src/utils/hash/hash.service";
|
||||
import { RoleEnum } from "src/Types&Enums/role.enum";
|
||||
import { UserType } from "src/Types&Enums/userType.enum";
|
||||
import { BlameVideoDbService } from "src/request-management/entities/db-service/blame-video.db.service";
|
||||
import { BlameVoiceDbService } from "src/request-management/entities/db-service/blame.voice.db.service";
|
||||
import { VideoCaptureDbService } from "src/claim-request-management/entites/db-service/video-capture.db.service";
|
||||
import { ClientDbService } from "src/client/entities/db-service/client.db.service";
|
||||
import { buildFileLink } from "src/helpers/urlCreator";
|
||||
import { toJalaliDateAndTime } from "src/helpers/date-jalali";
|
||||
import { enrichBlamePartiesForAgreementView } from "src/helpers/blame-party-agreement-decision";
|
||||
import { BlameRequestType } from "src/Types&Enums/blame-request-management/blameRequestType.enum";
|
||||
import { PartyRole } from "src/request-management/entities/schema/partyRole.enum";
|
||||
import {
|
||||
getClaimCarAngleCaptureBlob,
|
||||
getDamagedPartCaptureBlob,
|
||||
hasDamagedPartCapture,
|
||||
type ClaimCarAngleKey,
|
||||
} from "src/helpers/claim-car-angle-media";
|
||||
import {
|
||||
catalogLikeKeyFromPart,
|
||||
normalizeDamageSelectedParts,
|
||||
type DamageSelectedPartV2,
|
||||
} 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";
|
||||
|
||||
@Injectable()
|
||||
export class ExpertInsurerService {
|
||||
@@ -40,6 +62,11 @@ export class ExpertInsurerService {
|
||||
private readonly claimCaseDbService: ClaimCaseDbService,
|
||||
private readonly branchDbService: BranchDbService,
|
||||
private readonly hashService: HashService,
|
||||
private readonly blameVideoDbService: BlameVideoDbService,
|
||||
private readonly blameVoiceDbService: BlameVoiceDbService,
|
||||
private readonly videoCaptureDbService: VideoCaptureDbService,
|
||||
private readonly clientDbService: ClientDbService,
|
||||
private readonly claimSignDbService: ClaimSignDbService,
|
||||
) {}
|
||||
|
||||
private getClientId(actorOrId: any): Types.ObjectId {
|
||||
@@ -225,6 +252,445 @@ export class ExpertInsurerService {
|
||||
};
|
||||
}
|
||||
|
||||
private serializeBlameExpertDecisionForInsurer(
|
||||
decision: unknown,
|
||||
): Record<string, unknown> | undefined {
|
||||
if (!decision || typeof decision !== "object") {
|
||||
return undefined;
|
||||
}
|
||||
const d = decision as Record<string, unknown>;
|
||||
const guilty = d.guiltyPartyId;
|
||||
const decidedBy = d.decidedByExpertId;
|
||||
const decidedAt = d.decidedAt;
|
||||
return {
|
||||
...d,
|
||||
guiltyPartyId:
|
||||
guilty != null && typeof (guilty as { toString?: () => string }).toString === "function"
|
||||
? String(guilty)
|
||||
: guilty,
|
||||
decidedByExpertId:
|
||||
decidedBy != null &&
|
||||
typeof (decidedBy as { toString?: () => string }).toString === "function"
|
||||
? String(decidedBy)
|
||||
: decidedBy,
|
||||
decidedAt:
|
||||
decidedAt instanceof Date
|
||||
? decidedAt.toISOString()
|
||||
: decidedAt,
|
||||
};
|
||||
}
|
||||
|
||||
/** Same semantics as damage-expert claim detail `blameFileContext`. */
|
||||
private blameFileContextForInsurer(blame: Record<string, unknown> | null): {
|
||||
blameRequestType?: BlameRequestType;
|
||||
carBodyFirstForm?: { car?: boolean; object?: boolean };
|
||||
blameStatus?: string;
|
||||
} {
|
||||
if (!blame?.type) return {};
|
||||
const blameRequestType = blame.type as BlameRequestType;
|
||||
const out: {
|
||||
blameRequestType?: BlameRequestType;
|
||||
carBodyFirstForm?: { car?: boolean; object?: boolean };
|
||||
blameStatus?: string;
|
||||
} = { blameRequestType };
|
||||
out.blameStatus = blame.blameStatus as string | undefined;
|
||||
if (blameRequestType !== BlameRequestType.CAR_BODY) return out;
|
||||
|
||||
const parties = blame.parties;
|
||||
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];
|
||||
const cbf = first?.carBodyFirstForm;
|
||||
delete out.blameStatus;
|
||||
if (cbf && typeof cbf === "object") {
|
||||
out.carBodyFirstForm = {
|
||||
...(typeof cbf.car === "boolean" ? { car: cbf.car } : {}),
|
||||
...(typeof cbf.object === "boolean" ? { object: cbf.object } : {}),
|
||||
};
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
private async clientNamesByClientIds(
|
||||
ids: Array<string | undefined | null>,
|
||||
): Promise<Record<string, { persian: string; english: string }>> {
|
||||
const unique = [
|
||||
...new Set(
|
||||
ids
|
||||
.filter((x): x is string => !!x && Types.ObjectId.isValid(String(x)))
|
||||
.map(String),
|
||||
),
|
||||
];
|
||||
const out: Record<string, { persian: string; english: string }> = {};
|
||||
await Promise.all(
|
||||
unique.map(async (id) => {
|
||||
const doc = await this.clientDbService.find({
|
||||
_id: new Types.ObjectId(id),
|
||||
});
|
||||
if (doc?.clientName) out[id] = doc.clientName;
|
||||
}),
|
||||
);
|
||||
return out;
|
||||
}
|
||||
|
||||
private mapPersonForInsurer(
|
||||
person: any,
|
||||
clientNames: Record<string, { persian: string; english: string }>,
|
||||
) {
|
||||
if (!person || typeof person !== "object") return person;
|
||||
const cid =
|
||||
person.clientId?.toString?.() ?? (person.clientId != null ? String(person.clientId) : undefined);
|
||||
return {
|
||||
...person,
|
||||
userId: person.userId?.toString?.() ?? undefined,
|
||||
clientId: cid,
|
||||
clientName: cid ? clientNames[cid] : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
private mapPartyForInsurerFull(
|
||||
party: any,
|
||||
clientNames: Record<string, { persian: string; english: string }>,
|
||||
) {
|
||||
if (!party || typeof party !== "object") return party;
|
||||
return {
|
||||
...party,
|
||||
person: this.mapPersonForInsurer(party.person, clientNames),
|
||||
vehicle: this.sanitizeVehicleInquiry(party.vehicle),
|
||||
};
|
||||
}
|
||||
|
||||
/** Public URL for a stored relative path or pass-through if already absolute. */
|
||||
private linkFromPathOrUrl(stored: string | undefined): string | undefined {
|
||||
if (stored == null || stored === "") return undefined;
|
||||
const s = String(stored);
|
||||
if (/^https?:\/\//i.test(s)) return s;
|
||||
return buildFileLink(s);
|
||||
}
|
||||
|
||||
private enrichSignatureRecord(
|
||||
sig: Record<string, unknown> | undefined,
|
||||
): Record<string, unknown> | undefined {
|
||||
if (!sig || typeof sig !== "object") return sig;
|
||||
const fileUrl = sig.fileUrl != null ? String(sig.fileUrl) : undefined;
|
||||
const link = this.linkFromPathOrUrl(fileUrl);
|
||||
return { ...sig, link };
|
||||
}
|
||||
|
||||
private enrichPartiesConfirmationSignatures(parties: any[]): void {
|
||||
if (!Array.isArray(parties)) return;
|
||||
for (const p of parties) {
|
||||
const sig = p?.confirmation?.signature;
|
||||
if (sig && typeof sig === "object") {
|
||||
p.confirmation = {
|
||||
...p.confirmation,
|
||||
signature: this.enrichSignatureRecord(sig as Record<string, unknown>),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private insurerDamagedPartRowFromCapture(
|
||||
sp: DamageSelectedPartV2,
|
||||
index: number,
|
||||
damagedPartsDataExpert: unknown,
|
||||
selectedNormExpert: DamageSelectedPartV2[],
|
||||
) {
|
||||
const ck = sp.catalogKey ?? catalogLikeKeyFromPart(sp);
|
||||
const cap = getDamagedPartCaptureBlob(
|
||||
damagedPartsDataExpert,
|
||||
ck,
|
||||
selectedNormExpert,
|
||||
) as { url?: string; path?: string; fileName?: string } | undefined;
|
||||
const url = cap?.url || (cap?.path ? buildFileLink(cap.path) : undefined);
|
||||
const link = url;
|
||||
return {
|
||||
index,
|
||||
id: sp.id,
|
||||
name: sp.name,
|
||||
side: sp.side,
|
||||
label_fa: sp.label_fa,
|
||||
catalogKey: sp.catalogKey,
|
||||
captured: hasDamagedPartCapture(damagedPartsDataExpert, ck, selectedNormExpert),
|
||||
path: cap?.path,
|
||||
fileName: cap?.fileName,
|
||||
url,
|
||||
link,
|
||||
};
|
||||
}
|
||||
|
||||
private sanitizeDamageForInsurerDetail(damage: unknown): unknown {
|
||||
if (!damage || typeof damage !== "object") return damage;
|
||||
const d = { ...(damage as Record<string, unknown>) };
|
||||
delete d.selectedOuterParts;
|
||||
delete d.selectedPartIds;
|
||||
return d;
|
||||
}
|
||||
|
||||
private stripCarTypeFromPartyVehicles(parties: any[]): any[] {
|
||||
if (!Array.isArray(parties)) return parties;
|
||||
return parties.map((p) => {
|
||||
const v = p?.vehicle;
|
||||
if (!v || typeof v !== "object") return p;
|
||||
const { carType: _omit, ...rest } = v as Record<string, unknown>;
|
||||
const nextV = Object.keys(rest).length ? rest : undefined;
|
||||
return { ...p, vehicle: nextV };
|
||||
});
|
||||
}
|
||||
|
||||
private async claimSignLinkFromId(signDetailId: unknown): Promise<string | undefined> {
|
||||
if (signDetailId == null || signDetailId === "") return undefined;
|
||||
const id = String(signDetailId);
|
||||
if (!Types.ObjectId.isValid(id)) return undefined;
|
||||
const doc = await this.claimSignDbService.findOne({
|
||||
_id: new Types.ObjectId(id),
|
||||
});
|
||||
const path = (doc as any)?.path;
|
||||
return path ? buildFileLink(String(path)) : undefined;
|
||||
}
|
||||
|
||||
private async enrichClaimEvaluationForInsurer(
|
||||
evaluation: Record<string, unknown> | undefined,
|
||||
): Promise<Record<string, unknown> | undefined> {
|
||||
if (!evaluation || typeof evaluation !== "object") return evaluation;
|
||||
const ev = JSON.parse(JSON.stringify(evaluation)) as Record<string, unknown>;
|
||||
|
||||
const enrichReply = async (reply: any) => {
|
||||
if (!reply || typeof reply !== "object") return;
|
||||
const uc = reply.userComment;
|
||||
if (uc?.signDetailId != null) {
|
||||
const signLink = await this.claimSignLinkFromId(uc.signDetailId);
|
||||
reply.userComment = { ...uc, signLink };
|
||||
}
|
||||
};
|
||||
await enrichReply(ev.damageExpertReply);
|
||||
await enrichReply(ev.damageExpertReplyFinal);
|
||||
|
||||
const enrichApproval = async (key: string) => {
|
||||
const o = ev[key] as Record<string, unknown> | undefined;
|
||||
if (o?.signDetailId != null) {
|
||||
const signLink = await this.claimSignLinkFromId(o.signDetailId);
|
||||
ev[key] = { ...o, signLink };
|
||||
}
|
||||
};
|
||||
await enrichApproval("ownerInsurerApproval");
|
||||
await enrichApproval("ownerPricedPartsApproval");
|
||||
|
||||
return ev;
|
||||
}
|
||||
|
||||
private async enrichBlamePartyEvidenceUrls(parties: any[]): Promise<void> {
|
||||
if (!Array.isArray(parties)) return;
|
||||
for (const party of parties) {
|
||||
if (!party?.evidence) continue;
|
||||
const evidence = party.evidence as Record<string, unknown>;
|
||||
|
||||
if (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));
|
||||
if (voiceDoc?.path) voiceUrls.push(buildFileLink(voiceDoc.path));
|
||||
}
|
||||
evidence.voiceUrls = voiceUrls;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async buildInsurerBlameDetail(
|
||||
blameLean: Record<string, unknown>,
|
||||
): Promise<Record<string, unknown>> {
|
||||
const doc: Record<string, unknown> = { ...blameLean };
|
||||
const parties = (Array.isArray(doc.parties) ? doc.parties : []) as any[];
|
||||
await this.enrichBlamePartyEvidenceUrls(parties);
|
||||
doc.parties = enrichBlamePartiesForAgreementView(doc);
|
||||
|
||||
const mergedParties = doc.parties as any[];
|
||||
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));
|
||||
this.enrichPartiesConfirmationSignatures(doc.parties as any[]);
|
||||
|
||||
if (doc.expert && typeof doc.expert === "object") {
|
||||
const ex = { ...(doc.expert as Record<string, unknown>) };
|
||||
const serialized = this.serializeBlameExpertDecisionForInsurer(ex.decision);
|
||||
if (serialized !== undefined) ex.decision = serialized;
|
||||
doc.expert = ex;
|
||||
}
|
||||
|
||||
const createdAt = doc.createdAt
|
||||
? new Date(doc.createdAt as string | number | Date)
|
||||
: new Date();
|
||||
const updatedAt = doc.updatedAt
|
||||
? new Date(doc.updatedAt as string | number | Date)
|
||||
: new Date();
|
||||
const [cd, ct] = toJalaliDateAndTime(createdAt);
|
||||
const [ud, ut] = toJalaliDateAndTime(updatedAt);
|
||||
doc.createdAtFormatted = `${cd} ${ct}`;
|
||||
doc.updatedAtFormatted = `${ud} ${ut}`;
|
||||
|
||||
delete doc.history;
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
||||
private async buildInsurerClaimDetail(
|
||||
claim: Record<string, unknown>,
|
||||
blameForContext: Record<string, unknown> | null,
|
||||
): Promise<Record<string, unknown>> {
|
||||
const requiredDocs = claim.requiredDocuments as any;
|
||||
const requiredDocumentsStatus: Record<string, any> = {};
|
||||
if (requiredDocs) {
|
||||
const keys =
|
||||
requiredDocs instanceof Map
|
||||
? Array.from(requiredDocs.keys())
|
||||
: Object.keys(requiredDocs);
|
||||
for (const k of keys as string[]) {
|
||||
const row = requiredDocs instanceof Map ? requiredDocs.get(k) : requiredDocs[k];
|
||||
requiredDocumentsStatus[k] = {
|
||||
uploaded: !!row?.uploaded,
|
||||
fileId: row?.fileId?.toString?.(),
|
||||
fileName: row?.fileName,
|
||||
fileUrl: row?.filePath ? buildFileLink(row.filePath) : undefined,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const carAnglesData = (claim.media as any)?.carAngles;
|
||||
const damagedPartsDataForAngles = (claim.media as any)?.damagedParts;
|
||||
const carAngles: Record<string, { captured: boolean; url?: string }> = {};
|
||||
for (const k of ["front", "back", "left", "right"] as ClaimCarAngleKey[]) {
|
||||
const cap = getClaimCarAngleCaptureBlob(
|
||||
carAnglesData,
|
||||
damagedPartsDataForAngles,
|
||||
k,
|
||||
) as { url?: string; path?: string } | undefined;
|
||||
carAngles[k] = {
|
||||
captured: !!cap,
|
||||
url: cap?.url || (cap?.path ? buildFileLink(cap.path) : undefined),
|
||||
};
|
||||
}
|
||||
|
||||
const carTypeExpert = (claim.vehicle as any)?.carType as ClaimVehicleTypeV2 | undefined;
|
||||
const selectedNormExpert = normalizeDamageSelectedParts(
|
||||
(claim.damage as any)?.selectedParts,
|
||||
carTypeExpert,
|
||||
(claim.damage as any)?.selectedOuterParts,
|
||||
);
|
||||
const damagedPartsDataExpert = (claim.media as any)?.damagedParts;
|
||||
const damagedParts = selectedNormExpert.map((sp, index) =>
|
||||
this.insurerDamagedPartRowFromCapture(
|
||||
sp,
|
||||
index,
|
||||
damagedPartsDataExpert,
|
||||
selectedNormExpert,
|
||||
),
|
||||
);
|
||||
const selectedPartsNormalized = selectedNormExpert.map((sp, index) =>
|
||||
this.insurerDamagedPartRowFromCapture(
|
||||
sp,
|
||||
index,
|
||||
damagedPartsDataExpert,
|
||||
selectedNormExpert,
|
||||
),
|
||||
);
|
||||
|
||||
const videoCaptureIdRaw = (claim.media as any)?.videoCaptureId;
|
||||
let videoCapture:
|
||||
| { id: string; fileName?: string; path?: string; url?: string }
|
||||
| undefined;
|
||||
if (videoCaptureIdRaw) {
|
||||
const vc = (await this.videoCaptureDbService.findById(
|
||||
String(videoCaptureIdRaw),
|
||||
)) as any;
|
||||
if (vc) {
|
||||
videoCapture = {
|
||||
id: String(vc._id ?? videoCaptureIdRaw),
|
||||
fileName: vc.fileName,
|
||||
path: vc.path,
|
||||
url: vc.path ? buildFileLink(vc.path) : undefined,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const evaluation = claim.evaluation as Record<string, unknown> | undefined;
|
||||
const owner = claim.owner as any;
|
||||
const ownerOut = owner
|
||||
? {
|
||||
...owner,
|
||||
userId: owner.userId?.toString?.(),
|
||||
userClientKey: owner.userClientKey?.toString?.(),
|
||||
}
|
||||
: undefined;
|
||||
|
||||
let snapshotOut = claim.snapshot as any;
|
||||
if (snapshotOut && Array.isArray(snapshotOut.parties)) {
|
||||
const snapIds = snapshotOut.parties
|
||||
.map((p: any) => p?.person?.clientId?.toString?.())
|
||||
.filter(Boolean);
|
||||
const snapNames = await this.clientNamesByClientIds(snapIds);
|
||||
const snapParties = this.stripCarTypeFromPartyVehicles(
|
||||
snapshotOut.parties.map((p: any) =>
|
||||
this.mapPartyForInsurerFull(p, snapNames),
|
||||
),
|
||||
);
|
||||
this.enrichPartiesConfirmationSignatures(snapParties);
|
||||
snapshotOut = {
|
||||
...snapshotOut,
|
||||
parties: snapParties,
|
||||
};
|
||||
}
|
||||
|
||||
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<string, unknown>;
|
||||
return rest;
|
||||
})()
|
||||
: undefined;
|
||||
|
||||
return {
|
||||
claimRequestId: (claim._id as any)?.toString?.(),
|
||||
publicId: claim.publicId,
|
||||
status: claim.status,
|
||||
claimStatus: claim.claimStatus,
|
||||
blameDocumentResendPending: claim.blameDocumentResendPending,
|
||||
workflow: claim.workflow,
|
||||
owner: ownerOut,
|
||||
vehicle: vehicleOut,
|
||||
blameRequestId: (claim as any).blameRequestId?.toString?.(),
|
||||
blameRequestNo: claim.blameRequestNo,
|
||||
blameFileContext: blameForContext
|
||||
? this.blameFileContextForInsurer(blameForContext)
|
||||
: undefined,
|
||||
money: claim.money,
|
||||
damage: this.sanitizeDamageForInsurerDetail(claim.damage),
|
||||
media: claim.media,
|
||||
requiredDocuments:
|
||||
Object.keys(requiredDocumentsStatus).length > 0
|
||||
? requiredDocumentsStatus
|
||||
: undefined,
|
||||
carAngles,
|
||||
damagedParts,
|
||||
selectedPartsNormalized,
|
||||
videoCapture,
|
||||
evaluation: evaluationEnriched,
|
||||
userRating: claim.userRating,
|
||||
insurerRating: (evaluationEnriched as any)?.rating,
|
||||
snapshot: snapshotOut,
|
||||
createdAt: claim.createdAt,
|
||||
updatedAt: claim.updatedAt,
|
||||
};
|
||||
}
|
||||
|
||||
private getCombinedFileScore(file: any): number | null {
|
||||
const insurerRating = file?.rating;
|
||||
const userRating = file?.userRating;
|
||||
@@ -655,6 +1121,7 @@ export class ExpertInsurerService {
|
||||
}
|
||||
|
||||
const id = this.getClientId(insurerId);
|
||||
const idStr = String(id);
|
||||
const [blameFiles, claimFiles] = await Promise.all([
|
||||
this.getClientBlameFiles(id),
|
||||
this.getClientClaimFiles(id),
|
||||
@@ -667,12 +1134,31 @@ export class ExpertInsurerService {
|
||||
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 claimSnapshotParties = Array.isArray((claim as any)?.snapshot?.parties)
|
||||
? (claim as any).snapshot.parties
|
||||
: undefined;
|
||||
let blameFull: Record<string, unknown> | null = null;
|
||||
if (blame) {
|
||||
blameFull = (await this.blameRequestDbService.findByIdWithoutHistory(
|
||||
String((blame as any)._id),
|
||||
)) as Record<string, unknown> | null;
|
||||
}
|
||||
|
||||
let claimFull: Record<string, unknown> | null = null;
|
||||
if (claim) {
|
||||
const rows = (await this.claimCaseDbService.find(
|
||||
{ _id: new Types.ObjectId(String((claim as any)._id)) },
|
||||
{ lean: true },
|
||||
)) as Record<string, unknown>[];
|
||||
claimFull = rows[0] ?? null;
|
||||
}
|
||||
|
||||
let blameForClaimContext: Record<string, unknown> | null = blameFull;
|
||||
if (!blameForClaimContext && claimFull?.blameRequestId) {
|
||||
const linked = (await this.blameRequestDbService.findByIdWithoutHistory(
|
||||
String(claimFull.blameRequestId),
|
||||
)) as Record<string, unknown> | null;
|
||||
if (linked && blameCaseTouchesClient(linked, idStr)) {
|
||||
blameForClaimContext = linked;
|
||||
}
|
||||
}
|
||||
|
||||
const overview = {
|
||||
publicId,
|
||||
@@ -681,97 +1167,21 @@ export class ExpertInsurerService {
|
||||
(claim as any)?.requestNo ||
|
||||
(blame as any)?.requestNumber ||
|
||||
(claim as any)?.requestNumber,
|
||||
type: (blame as any)?.type,
|
||||
type: (blame as any)?.type ?? (blameFull as any)?.type,
|
||||
hasClaim: !!claim,
|
||||
blameStatus: (blame as any)?.status,
|
||||
blameStatus: (blame as any)?.status ?? (blameFull 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: this.sanitizeVehicleInquiry(firstParty?.vehicle),
|
||||
}
|
||||
: undefined,
|
||||
second: secondParty
|
||||
? {
|
||||
fullName: secondParty?.person?.fullName,
|
||||
phoneNumber: secondParty?.person?.phoneNumber,
|
||||
role: secondParty?.role,
|
||||
vehicle: this.sanitizeVehicleInquiry(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,
|
||||
}
|
||||
const blameDetails = blameFull
|
||||
? await this.buildInsurerBlameDetail(blameFull)
|
||||
: 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,
|
||||
owner: (claim as any).owner,
|
||||
vehicle: (claim as any).vehicle,
|
||||
money: (claim as any).money,
|
||||
damage: (claim as any).damage,
|
||||
media: (claim as any).media,
|
||||
requiredDocuments: (claim as any).requiredDocuments,
|
||||
snapshot: (claim as any).snapshot
|
||||
? {
|
||||
...(claim as any).snapshot,
|
||||
parties: claimSnapshotParties?.map((p: any) =>
|
||||
this.sanitizePartyForInsurerView(p),
|
||||
),
|
||||
}
|
||||
: undefined,
|
||||
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,
|
||||
factors: (claim as any).evaluation?.factors,
|
||||
},
|
||||
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,
|
||||
}
|
||||
const claimDetails = claimFull
|
||||
? await this.buildInsurerClaimDetail(claimFull, blameForClaimContext)
|
||||
: undefined;
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user