forked from Yara724/api
Added field expert support for insurer
This commit is contained in:
@@ -24,6 +24,7 @@ import { CaseStatus } from "src/Types&Enums/blame-request-management/caseStatus.
|
||||
import { ClaimCaseStatus } from "src/Types&Enums/claim-request-management/claim-case-status.enum";
|
||||
import { DamageExpertDbService } from "src/users/entities/db-service/damage-expert.db.service";
|
||||
import { ExpertDbService } from "src/users/entities/db-service/expert.db.service";
|
||||
import { FieldExpertDbService } from "src/users/entities/db-service/field-expert.db.service";
|
||||
import { ExpertFileActivityDbService } from "src/users/entities/db-service/expert-file-activity.db.service";
|
||||
import { ExpertFileActivityType } from "src/users/entities/schema/expert-file-activity.schema";
|
||||
import { HashService } from "src/utils/hash/hash.service";
|
||||
@@ -62,6 +63,7 @@ export class ExpertInsurerService {
|
||||
constructor(
|
||||
private readonly expertDbService: ExpertDbService,
|
||||
private readonly damageExpertDbService: DamageExpertDbService,
|
||||
private readonly fieldExpertDbService: FieldExpertDbService,
|
||||
private readonly expertFileActivityDbService: ExpertFileActivityDbService,
|
||||
private readonly blameRequestDbService: BlameRequestDbService,
|
||||
private readonly claimCaseDbService: ClaimCaseDbService,
|
||||
@@ -74,6 +76,26 @@ export class ExpertInsurerService {
|
||||
private readonly claimSignDbService: ClaimSignDbService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Returns all expert IDs (blame-expert, damage-expert, field-expert) that
|
||||
* belong to this insurer's clientKey in a single parallel fetch.
|
||||
*/
|
||||
private async getAllRosterExpertIds(
|
||||
clientObjectId: Types.ObjectId,
|
||||
): Promise<Set<string>> {
|
||||
const ckFilter = this.clientKeyScopeFilter(clientObjectId);
|
||||
const [experts, damageExperts, fieldExperts] = await Promise.all([
|
||||
this.expertDbService.findAll(ckFilter as never),
|
||||
this.damageExpertDbService.findAll(ckFilter as never),
|
||||
this.fieldExpertDbService.findAll(ckFilter as never),
|
||||
]);
|
||||
return new Set<string>(
|
||||
[...experts, ...damageExperts, ...fieldExperts].map((e: any) =>
|
||||
String(e._id),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
private getClientId(actorOrId: any): Types.ObjectId {
|
||||
const raw =
|
||||
typeof actorOrId === "string" ? actorOrId : actorOrId?.clientKey;
|
||||
@@ -786,13 +808,26 @@ export class ExpertInsurerService {
|
||||
private async getClientClaimFiles(
|
||||
clientObjectId: Types.ObjectId,
|
||||
): Promise<any[]> {
|
||||
const all = (await this.claimCaseDbService.find(
|
||||
{},
|
||||
{ lean: true },
|
||||
)) as any[];
|
||||
const [all, rosterExpertIds] = await Promise.all([
|
||||
this.claimCaseDbService.find({}, { lean: true }) as Promise<any[]>,
|
||||
this.getAllRosterExpertIds(clientObjectId),
|
||||
]);
|
||||
const idStr = String(clientObjectId);
|
||||
|
||||
return all
|
||||
.filter((f) => claimCaseTouchesClient(f, idStr))
|
||||
.filter((f) => {
|
||||
// Primary: owner.clientId matches this insurer (guilty party's insurance)
|
||||
if (claimCaseTouchesClient(f, idStr)) return true;
|
||||
// Secondary: initiated or evaluated by one of this insurer's experts
|
||||
// (covers field-expert IN_PERSON flow where owner.clientId ≠ this insurer)
|
||||
const fieldExpertId = f?.initiatedByFieldExpertId
|
||||
? String(f.initiatedByFieldExpertId)
|
||||
: null;
|
||||
if (fieldExpertId && rosterExpertIds.has(fieldExpertId)) return true;
|
||||
const damageExpertId = this.claimDamageExpertActorId(f);
|
||||
if (damageExpertId && rosterExpertIds.has(damageExpertId)) return true;
|
||||
return false;
|
||||
})
|
||||
.map((f) => this.normalizeClaimCase(f));
|
||||
}
|
||||
|
||||
@@ -803,14 +838,16 @@ export class ExpertInsurerService {
|
||||
) {
|
||||
const clientObjectId = this.getClientId(actor);
|
||||
const ckFilter = this.clientKeyScopeFilter(clientObjectId);
|
||||
const [experts, damageExperts, blameFiles, claimFiles] = await Promise.all([
|
||||
this.expertDbService.findAll(ckFilter as never),
|
||||
this.damageExpertDbService.findAll(ckFilter as never),
|
||||
this.getClientBlameFiles(clientObjectId),
|
||||
this.getClientClaimFiles(clientObjectId),
|
||||
]);
|
||||
const [experts, damageExperts, fieldExperts, blameFiles, claimFiles] =
|
||||
await Promise.all([
|
||||
this.expertDbService.findAll(ckFilter as never),
|
||||
this.damageExpertDbService.findAll(ckFilter as never),
|
||||
this.fieldExpertDbService.findAll(ckFilter as never),
|
||||
this.getClientBlameFiles(clientObjectId),
|
||||
this.getClientClaimFiles(clientObjectId),
|
||||
]);
|
||||
|
||||
const allExpertsRaw = [...experts, ...damageExperts];
|
||||
const allExpertsRaw = [...experts, ...damageExperts, ...fieldExperts];
|
||||
const expertIds = allExpertsRaw.map((e) => String(e._id));
|
||||
const expertActivityStatsMap = await this.buildExpertActivityStatsMap(
|
||||
clientObjectId,
|
||||
@@ -890,6 +927,7 @@ export class ExpertInsurerService {
|
||||
const allExperts = [
|
||||
...experts.map((e) => mapExpertRow(e, "blame")),
|
||||
...damageExperts.map((e) => mapExpertRow(e, "claim")),
|
||||
...fieldExperts.map((e) => mapExpertRow(e, "blame")),
|
||||
];
|
||||
|
||||
const page = Number(currentPage) > 0 ? Number(currentPage) : 1;
|
||||
@@ -960,12 +998,15 @@ export class ExpertInsurerService {
|
||||
const expertObjectId = this.parseObjectId(expertId, "expert id");
|
||||
const clientOid = this.getClientId(insurerClientKey);
|
||||
const ckFilter = this.clientKeyScopeFilter(clientOid);
|
||||
const [experts, damageExperts] = await Promise.all([
|
||||
const [experts, damageExperts, fieldExperts] = await Promise.all([
|
||||
this.expertDbService.findAll(ckFilter as never),
|
||||
this.damageExpertDbService.findAll(ckFilter as never),
|
||||
this.fieldExpertDbService.findAll(ckFilter as never),
|
||||
]);
|
||||
const id = String(expertObjectId);
|
||||
const onBlameRoster = experts.some((e) => String(e._id) === id);
|
||||
const onBlameRoster =
|
||||
experts.some((e) => String(e._id) === id) ||
|
||||
fieldExperts.some((e) => String(e._id) === id);
|
||||
const onClaimRoster = damageExperts.some((e) => String(e._id) === id);
|
||||
if (!onBlameRoster && !onClaimRoster) {
|
||||
throw new ForbiddenException(
|
||||
@@ -973,34 +1014,41 @@ export class ExpertInsurerService {
|
||||
);
|
||||
}
|
||||
|
||||
const clientKeyStr = String(clientOid);
|
||||
// Gather all files this expert touched — blame and claim — regardless of
|
||||
// which insurance company's party appears on the file. The roster check
|
||||
// above already ensures the expert belongs to THIS insurer, so showing all
|
||||
// files they handled is the correct scope.
|
||||
const [allBlames, allClaims] = await Promise.all([
|
||||
onBlameRoster
|
||||
? (this.blameRequestDbService.find({}, { lean: true }) as Promise<any[]>)
|
||||
: Promise.resolve([] as any[]),
|
||||
(this.claimCaseDbService.find({}, { lean: true }) as Promise<any[]>),
|
||||
]);
|
||||
|
||||
const result: any[] = [];
|
||||
|
||||
// Blame files handled by a field/blame expert
|
||||
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));
|
||||
for (const b of allBlames) {
|
||||
if (this.blameFieldExpertIdCandidates(b).includes(id)) {
|
||||
result.push(this.mapBlameFileSummaryForInsurerExpert(b));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (onClaimRoster) {
|
||||
const claims = (await this.claimCaseDbService.find(
|
||||
{},
|
||||
{ lean: true },
|
||||
)) as any[];
|
||||
return claims
|
||||
.filter(
|
||||
(c) =>
|
||||
claimCaseTouchesClient(c, clientKeyStr) &&
|
||||
this.claimDamageExpertActorId(c) === id,
|
||||
)
|
||||
.map((c) => this.mapClaimFileSummaryForInsurerExpert(c));
|
||||
|
||||
// Claim files: initiated by this field expert OR evaluated by this damage expert
|
||||
for (const c of allClaims) {
|
||||
const isFieldExpertClaim =
|
||||
c?.initiatedByFieldExpertId &&
|
||||
String(c.initiatedByFieldExpertId) === id;
|
||||
const isDamageExpertClaim =
|
||||
onClaimRoster && this.claimDamageExpertActorId(c) === id;
|
||||
if (isFieldExpertClaim || isDamageExpertClaim) {
|
||||
result.push(this.mapClaimFileSummaryForInsurerExpert(c));
|
||||
}
|
||||
}
|
||||
return [];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private validateInsurerFileRating(rating: FileRating) {
|
||||
@@ -1697,17 +1745,20 @@ export class ExpertInsurerService {
|
||||
|
||||
const branchIdSet = new Set(branches.map((b: any) => String(b._id)));
|
||||
const ckFilter = this.clientKeyScopeFilter(clientObjectId);
|
||||
const [experts, damageExperts, blameFiles, claimFiles] = await Promise.all([
|
||||
this.expertDbService.findAll(ckFilter as never),
|
||||
this.damageExpertDbService.findAll(ckFilter as never),
|
||||
this.getClientBlameFiles(clientObjectId),
|
||||
this.getClientClaimFiles(clientObjectId),
|
||||
]);
|
||||
const [experts, damageExperts, fieldExperts, blameFiles, claimFiles] =
|
||||
await Promise.all([
|
||||
this.expertDbService.findAll(ckFilter as never),
|
||||
this.damageExpertDbService.findAll(ckFilter as never),
|
||||
this.fieldExpertDbService.findAll(ckFilter as never),
|
||||
this.getClientBlameFiles(clientObjectId),
|
||||
this.getClientClaimFiles(clientObjectId),
|
||||
]);
|
||||
|
||||
const activeExpertsByBranch = new Map<string, Set<string>>();
|
||||
const expertBranchById = new Map<string, string>();
|
||||
const claimExpertBranchById = new Map<string, string>();
|
||||
for (const e of experts as any[]) {
|
||||
// blame-expert + field-expert both handle blame/IN_PERSON files
|
||||
for (const e of [...(experts as any[]), ...(fieldExperts as any[])]) {
|
||||
const bid = e?.branchId ? String(e.branchId) : "";
|
||||
if (!bid || !branchIdSet.has(bid)) continue;
|
||||
const eid = String(e?._id || "");
|
||||
@@ -1742,9 +1793,11 @@ export class ExpertInsurerService {
|
||||
};
|
||||
|
||||
for (const b of blameFiles as any[]) {
|
||||
const expertId = String(b?.expert?.assignedExpertId || "");
|
||||
if (!expertId) continue;
|
||||
const bid = expertBranchById.get(expertId);
|
||||
// Resolve expert via assignment OR field-expert initiation
|
||||
const candidateIds = this.blameFieldExpertIdCandidates(b);
|
||||
const bid = candidateIds
|
||||
.map((eid) => expertBranchById.get(eid))
|
||||
.find(Boolean);
|
||||
if (!bid) continue;
|
||||
const fileKey = String(b?.publicId || b?._id || "");
|
||||
if (!fileKey) continue;
|
||||
@@ -1756,9 +1809,17 @@ export class ExpertInsurerService {
|
||||
}
|
||||
|
||||
for (const c of claimFiles as any[]) {
|
||||
const expertId = String(this.claimDamageExpertActorId(c) || "");
|
||||
// Field-expert-initiated claims
|
||||
const fieldExpertId = c?.initiatedByFieldExpertId
|
||||
? String(c.initiatedByFieldExpertId)
|
||||
: null;
|
||||
const damageExpertId = this.claimDamageExpertActorId(c);
|
||||
const expertId = fieldExpertId ?? damageExpertId ?? "";
|
||||
if (!expertId) continue;
|
||||
const bid = claimExpertBranchById.get(expertId);
|
||||
const bid =
|
||||
expertBranchById.get(expertId) ??
|
||||
claimExpertBranchById.get(expertId) ??
|
||||
undefined;
|
||||
if (!bid) continue;
|
||||
const fileKey = String(c?.publicId || c?._id || "");
|
||||
if (!fileKey) continue;
|
||||
|
||||
Reference in New Issue
Block a user