forked from Yara724/api
YARA-856
This commit is contained in:
@@ -205,6 +205,9 @@ export class ClaimFileRating {
|
|||||||
|
|
||||||
@Prop({ type: Number, min: 0, max: 5 })
|
@Prop({ type: Number, min: 0, max: 5 })
|
||||||
guiltyVehicleIdentification?: number;
|
guiltyVehicleIdentification?: number;
|
||||||
|
|
||||||
|
@Prop({ type: Number, min: 0, max: 5 })
|
||||||
|
botRating?: number;
|
||||||
}
|
}
|
||||||
export const ClaimFileRatingSchema =
|
export const ClaimFileRatingSchema =
|
||||||
SchemaFactory.createForClass(ClaimFileRating);
|
SchemaFactory.createForClass(ClaimFileRating);
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
ApiBearerAuth,
|
ApiBearerAuth,
|
||||||
ApiBody,
|
ApiBody,
|
||||||
|
ApiOperation,
|
||||||
ApiParam,
|
ApiParam,
|
||||||
ApiQuery,
|
ApiQuery,
|
||||||
ApiTags,
|
ApiTags,
|
||||||
@@ -105,6 +106,11 @@ export class ExpertInsurerController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get("experts/top")
|
@Get("experts/top")
|
||||||
|
@ApiOperation({
|
||||||
|
summary: "Top blame vs claim experts for this insurer",
|
||||||
|
description:
|
||||||
|
"Response has two arrays: `blameExperts` (roster from expert / blame files) and `claimExperts` (damage-expert roster / claim files). Each item includes `overallAverageRating` derived from ratings stored on those files plus user ratings where present.",
|
||||||
|
})
|
||||||
async getTopExperts(@CurrentUser() actor) {
|
async getTopExperts(@CurrentUser() actor) {
|
||||||
return await this.expertInsurerService.getTopExpertsForClient(actor);
|
return await this.expertInsurerService.getTopExpertsForClient(actor);
|
||||||
}
|
}
|
||||||
@@ -128,6 +134,78 @@ export class ExpertInsurerController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiBody({
|
||||||
|
description:
|
||||||
|
"One insurer rating for the shared publicId; persisted on claim and/or blame case documents when both exist.",
|
||||||
|
schema: {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
collisionMethodAccuracy: {
|
||||||
|
type: "number",
|
||||||
|
minimum: 0,
|
||||||
|
maximum: 5,
|
||||||
|
example: 4,
|
||||||
|
description: "تشخیص درست نحوه برخورد",
|
||||||
|
},
|
||||||
|
evaluationTimeliness: {
|
||||||
|
type: "number",
|
||||||
|
minimum: 0,
|
||||||
|
maximum: 5,
|
||||||
|
example: 3,
|
||||||
|
description: "زمان ارزیابی",
|
||||||
|
},
|
||||||
|
accidentCauseAccuracy: {
|
||||||
|
type: "number",
|
||||||
|
minimum: 0,
|
||||||
|
maximum: 5,
|
||||||
|
example: 5,
|
||||||
|
description: "تشخیص درست علت تصادف",
|
||||||
|
},
|
||||||
|
guiltyVehicleIdentification: {
|
||||||
|
type: "number",
|
||||||
|
minimum: 0,
|
||||||
|
maximum: 5,
|
||||||
|
example: 4,
|
||||||
|
description: "تشخیص درست وسیله نقلیه مقصر",
|
||||||
|
},
|
||||||
|
botRating: {
|
||||||
|
type: "number",
|
||||||
|
minimum: 0,
|
||||||
|
maximum: 5,
|
||||||
|
example: 4,
|
||||||
|
description: "برای امتیاز دادن به عملکرد بات",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
required: [
|
||||||
|
"collisionMethodAccuracy",
|
||||||
|
"evaluationTimeliness",
|
||||||
|
"accidentCauseAccuracy",
|
||||||
|
"guiltyVehicleIdentification",
|
||||||
|
"botRating",
|
||||||
|
],
|
||||||
|
example: {
|
||||||
|
collisionMethodAccuracy: 4,
|
||||||
|
evaluationTimeliness: 3,
|
||||||
|
accidentCauseAccuracy: 5,
|
||||||
|
guiltyVehicleIdentification: 4,
|
||||||
|
botRating: 4,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
@ApiParam({ name: "publicId" })
|
||||||
|
@Put("files/:publicId/rating")
|
||||||
|
async rateExpertsByPublicId(
|
||||||
|
@CurrentUser() insurer,
|
||||||
|
@Param("publicId") publicId: string,
|
||||||
|
@Body() rating: FileRating,
|
||||||
|
) {
|
||||||
|
return await this.expertInsurerService.rateExpertByPublicId(
|
||||||
|
publicId,
|
||||||
|
rating,
|
||||||
|
insurer.clientKey,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@Get("top-files")
|
@Get("top-files")
|
||||||
async getTopFiles(@CurrentUser() insurer) {
|
async getTopFiles(@CurrentUser() insurer) {
|
||||||
return await this.expertInsurerService.getTopFilesForClient(
|
return await this.expertInsurerService.getTopFilesForClient(
|
||||||
@@ -185,82 +263,4 @@ export class ExpertInsurerController {
|
|||||||
insurer.clientKey,
|
insurer.clientKey,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiBody({
|
|
||||||
description: "Detailed expert rating by insurer",
|
|
||||||
schema: {
|
|
||||||
type: "object",
|
|
||||||
properties: {
|
|
||||||
collisionMethodAccuracy: {
|
|
||||||
type: "number",
|
|
||||||
minimum: 0,
|
|
||||||
maximum: 5,
|
|
||||||
example: 4,
|
|
||||||
description: "تشخیص درست نحوه برخورد",
|
|
||||||
},
|
|
||||||
evaluationTimeliness: {
|
|
||||||
type: "number",
|
|
||||||
minimum: 0,
|
|
||||||
maximum: 5,
|
|
||||||
example: 3,
|
|
||||||
description: "زمان ارزیابی",
|
|
||||||
},
|
|
||||||
accidentCauseAccuracy: {
|
|
||||||
type: "number",
|
|
||||||
minimum: 0,
|
|
||||||
maximum: 5,
|
|
||||||
example: 5,
|
|
||||||
description: "تشخیص درست علت تصادف",
|
|
||||||
},
|
|
||||||
guiltyVehicleIdentification: {
|
|
||||||
type: "number",
|
|
||||||
minimum: 0,
|
|
||||||
maximum: 5,
|
|
||||||
example: 4,
|
|
||||||
description: "تشخیص درست وسیله نقلیه مقصر",
|
|
||||||
},
|
|
||||||
botRating: {
|
|
||||||
type: "number",
|
|
||||||
minimum: 0,
|
|
||||||
maximum: 5,
|
|
||||||
example: 4,
|
|
||||||
description: "برای امتیاز دادن به عملکرد بات",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
required: [
|
|
||||||
"collisionMethodAccuracy",
|
|
||||||
"evaluationTimeliness",
|
|
||||||
"accidentCauseAccuracy",
|
|
||||||
"guiltyVehicleIdentification",
|
|
||||||
"botRating",
|
|
||||||
],
|
|
||||||
example: {
|
|
||||||
collisionMethodAccuracy: 4,
|
|
||||||
evaluationTimeliness: 3,
|
|
||||||
accidentCauseAccuracy: 5,
|
|
||||||
guiltyVehicleIdentification: 4,
|
|
||||||
botRating: 4,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
@ApiParam({ name: "requestId" })
|
|
||||||
@ApiQuery({ name: "role", enum: ["claim", "blame"] })
|
|
||||||
@Put("/:requestId/rating")
|
|
||||||
async rateExperts(
|
|
||||||
@CurrentUser() insurer,
|
|
||||||
@Param("requestId") requestId: string,
|
|
||||||
@Body() rating: FileRating,
|
|
||||||
@Query("role") role: "claim" | "blame",
|
|
||||||
) {
|
|
||||||
if (!["claim", "blame"].includes(role)) {
|
|
||||||
throw new BadRequestException("Invalid role");
|
|
||||||
}
|
|
||||||
|
|
||||||
return await this.expertInsurerService.rateExpertOnFile(
|
|
||||||
requestId,
|
|
||||||
rating,
|
|
||||||
role,
|
|
||||||
insurer.clientKey,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,6 +50,16 @@ export class ExpertInsurerService {
|
|||||||
return new Types.ObjectId(raw);
|
return new Types.ObjectId(raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tenant scope on expert / damage-expert: `clientKey` may be stored as ObjectId or
|
||||||
|
* string (legacy and collection-specific writes). Match both, same as reports service.
|
||||||
|
*/
|
||||||
|
private clientKeyScopeFilter(clientObjectId: Types.ObjectId) {
|
||||||
|
const oid = clientObjectId;
|
||||||
|
const str = String(oid);
|
||||||
|
return { $or: [{ clientKey: oid }, { clientKey: str }] };
|
||||||
|
}
|
||||||
|
|
||||||
private async buildExpertActivityStatsMap(
|
private async buildExpertActivityStatsMap(
|
||||||
tenantId: Types.ObjectId,
|
tenantId: Types.ObjectId,
|
||||||
expertIds: string[],
|
expertIds: string[],
|
||||||
@@ -170,9 +180,14 @@ export class ExpertInsurerService {
|
|||||||
const insurerRating = file?.rating;
|
const insurerRating = file?.rating;
|
||||||
const userRating = file?.userRating;
|
const userRating = file?.userRating;
|
||||||
const insurerValues = insurerRating
|
const insurerValues = insurerRating
|
||||||
? Object.values(insurerRating).filter(
|
? (
|
||||||
(v): v is number => typeof v === "number" && !isNaN(v),
|
[
|
||||||
)
|
insurerRating.collisionMethodAccuracy,
|
||||||
|
insurerRating.evaluationTimeliness,
|
||||||
|
insurerRating.accidentCauseAccuracy,
|
||||||
|
insurerRating.guiltyVehicleIdentification,
|
||||||
|
] as unknown[]
|
||||||
|
).filter((v): v is number => typeof v === "number" && !isNaN(v))
|
||||||
: [];
|
: [];
|
||||||
const userValues = userRating
|
const userValues = userRating
|
||||||
? [userRating.progressSpeed, userRating.registrationEase, userRating.overallEvaluation].filter(
|
? [userRating.progressSpeed, userRating.registrationEase, userRating.overallEvaluation].filter(
|
||||||
@@ -210,9 +225,10 @@ export class ExpertInsurerService {
|
|||||||
|
|
||||||
async retrieveAllExpertsOfClient(actor, currentPage: number, countPerPage: number) {
|
async retrieveAllExpertsOfClient(actor, currentPage: number, countPerPage: number) {
|
||||||
const clientObjectId = this.getClientId(actor);
|
const clientObjectId = this.getClientId(actor);
|
||||||
|
const ckFilter = this.clientKeyScopeFilter(clientObjectId);
|
||||||
const [experts, damageExperts, blameFiles, claimFiles] = await Promise.all([
|
const [experts, damageExperts, blameFiles, claimFiles] = await Promise.all([
|
||||||
this.expertDbService.findAll({ clientKey: String(clientObjectId) }),
|
this.expertDbService.findAll(ckFilter as never),
|
||||||
this.damageExpertDbService.findAll({ clientKey: clientObjectId }),
|
this.damageExpertDbService.findAll(ckFilter as never),
|
||||||
this.getClientBlameFiles(clientObjectId),
|
this.getClientBlameFiles(clientObjectId),
|
||||||
this.getClientClaimFiles(clientObjectId),
|
this.getClientClaimFiles(clientObjectId),
|
||||||
]);
|
]);
|
||||||
@@ -237,6 +253,7 @@ export class ExpertInsurerService {
|
|||||||
if (!rating || typeof rating !== "object") return;
|
if (!rating || typeof rating !== "object") return;
|
||||||
if (!expertRatingsByCategoryMap[expertId]) expertRatingsByCategoryMap[expertId] = {};
|
if (!expertRatingsByCategoryMap[expertId]) expertRatingsByCategoryMap[expertId] = {};
|
||||||
for (const [category, value] of Object.entries(rating)) {
|
for (const [category, value] of Object.entries(rating)) {
|
||||||
|
if (category === "botRating") continue;
|
||||||
if (typeof value === "number" && !isNaN(value)) {
|
if (typeof value === "number" && !isNaN(value)) {
|
||||||
if (!expertRatingsByCategoryMap[expertId][category]) {
|
if (!expertRatingsByCategoryMap[expertId][category]) {
|
||||||
expertRatingsByCategoryMap[expertId][category] = [];
|
expertRatingsByCategoryMap[expertId][category] = [];
|
||||||
@@ -253,7 +270,7 @@ export class ExpertInsurerService {
|
|||||||
processRatings(file?.damageExpertReply?.actorDetail?.actorId, file);
|
processRatings(file?.damageExpertReply?.actorDetail?.actorId, file);
|
||||||
}
|
}
|
||||||
|
|
||||||
const allExperts = allExpertsRaw.map((expert) => {
|
const mapExpertRow = (expert: any, expertKind: "blame" | "claim") => {
|
||||||
const expertIdStr = expert._id.toString();
|
const expertIdStr = expert._id.toString();
|
||||||
const totalRatings = expertTotalRatingsMap[expertIdStr] || [];
|
const totalRatings = expertTotalRatingsMap[expertIdStr] || [];
|
||||||
const overallAverageRating = totalRatings.length
|
const overallAverageRating = totalRatings.length
|
||||||
@@ -274,6 +291,7 @@ export class ExpertInsurerService {
|
|||||||
_id: expert._id,
|
_id: expert._id,
|
||||||
fullName: `${expert.firstName} ${expert.lastName}`,
|
fullName: `${expert.firstName} ${expert.lastName}`,
|
||||||
role: expert.role,
|
role: expert.role,
|
||||||
|
expertKind,
|
||||||
type: expert.userType,
|
type: expert.userType,
|
||||||
requestStats: expertActivityStatsMap[expertIdStr] ?? {
|
requestStats: expertActivityStatsMap[expertIdStr] ?? {
|
||||||
totalHandled: 0,
|
totalHandled: 0,
|
||||||
@@ -283,7 +301,12 @@ export class ExpertInsurerService {
|
|||||||
overallAverageRating,
|
overallAverageRating,
|
||||||
averageRatingsByCategory,
|
averageRatingsByCategory,
|
||||||
};
|
};
|
||||||
});
|
};
|
||||||
|
|
||||||
|
const allExperts = [
|
||||||
|
...experts.map((e) => mapExpertRow(e, "blame")),
|
||||||
|
...damageExperts.map((e) => mapExpertRow(e, "claim")),
|
||||||
|
];
|
||||||
|
|
||||||
const page = Number(currentPage) > 0 ? Number(currentPage) : 1;
|
const page = Number(currentPage) > 0 ? Number(currentPage) : 1;
|
||||||
const perPage = Number(countPerPage) > 0 ? Number(countPerPage) : 20;
|
const perPage = Number(countPerPage) > 0 ? Number(countPerPage) : 20;
|
||||||
@@ -297,20 +320,33 @@ export class ExpertInsurerService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns top 10 experts for the current insurer client based on
|
* Top experts per roster: blame rows come from the `expert` collection (files they
|
||||||
* combined insurer + user ratings on their handled files.
|
* handled on blame cases); claim rows from `damage-expert` (claim evaluations).
|
||||||
|
* Scores are aggregated from **file documents** that insurer can see: for each such
|
||||||
|
* file we take `evaluation.rating` / `expert.rating` (insurer dimensions only,
|
||||||
|
* excluding `botRating`), optionally blend with the file’s user rating, average those
|
||||||
|
* combined scores per file, then average across that expert’s files (`overallAverageRating`).
|
||||||
*/
|
*/
|
||||||
async getTopExpertsForClient(actor): Promise<any[]> {
|
async getTopExpertsForClient(actor): Promise<{
|
||||||
|
blameExperts: any[];
|
||||||
|
claimExperts: any[];
|
||||||
|
}> {
|
||||||
const result = await this.retrieveAllExpertsOfClient(actor, 1, 1000);
|
const result = await this.retrieveAllExpertsOfClient(actor, 1, 1000);
|
||||||
const experts = result?.experts || [];
|
const rows = result?.experts || [];
|
||||||
|
|
||||||
const sorted = [...experts].sort((a, b) => {
|
const byRatingDesc = (a: any, b: any) =>
|
||||||
const ar = a.overallAverageRating ?? 0;
|
(b.overallAverageRating ?? 0) - (a.overallAverageRating ?? 0);
|
||||||
const br = b.overallAverageRating ?? 0;
|
|
||||||
return br - ar;
|
|
||||||
});
|
|
||||||
|
|
||||||
return sorted.slice(0, 10);
|
const blameExperts = rows
|
||||||
|
.filter((e) => e.expertKind === "blame")
|
||||||
|
.sort(byRatingDesc)
|
||||||
|
.slice(0, 10);
|
||||||
|
const claimExperts = rows
|
||||||
|
.filter((e) => e.expertKind === "claim")
|
||||||
|
.sort(byRatingDesc)
|
||||||
|
.slice(0, 10);
|
||||||
|
|
||||||
|
return { blameExperts, claimExperts };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -333,11 +369,11 @@ export class ExpertInsurerService {
|
|||||||
expertObjectId: Types.ObjectId,
|
expertObjectId: Types.ObjectId,
|
||||||
insurerClientKey: string,
|
insurerClientKey: string,
|
||||||
) {
|
) {
|
||||||
const ck = String(insurerClientKey);
|
const clientOid = this.getClientId(insurerClientKey);
|
||||||
const clientOid = new Types.ObjectId(ck);
|
const ckFilter = this.clientKeyScopeFilter(clientOid);
|
||||||
const [experts, damageExperts] = await Promise.all([
|
const [experts, damageExperts] = await Promise.all([
|
||||||
this.expertDbService.findAll({ clientKey: ck }),
|
this.expertDbService.findAll(ckFilter as never),
|
||||||
this.damageExpertDbService.findAll({ clientKey: clientOid }),
|
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 ok = [...experts, ...damageExperts].some((e) => String(e._id) === id);
|
||||||
@@ -378,52 +414,83 @@ export class ExpertInsurerService {
|
|||||||
.map((b) => this.normalizeBlameCase(b));
|
.map((b) => this.normalizeBlameCase(b));
|
||||||
}
|
}
|
||||||
|
|
||||||
async rateExpertOnFile(
|
private validateInsurerFileRating(rating: FileRating) {
|
||||||
requestId: string,
|
const required: (keyof FileRating)[] = [
|
||||||
rating: FileRating,
|
"collisionMethodAccuracy",
|
||||||
role: "claim" | "blame",
|
"evaluationTimeliness",
|
||||||
insurerClientKey: string,
|
"accidentCauseAccuracy",
|
||||||
) {
|
"guiltyVehicleIdentification",
|
||||||
const _id = this.parseObjectId(requestId, "request id");
|
"botRating",
|
||||||
const clientKeyStr = String(this.getClientId(insurerClientKey));
|
];
|
||||||
for (const [key, value] of Object.entries(rating || {})) {
|
for (const key of required) {
|
||||||
|
const value = rating?.[key];
|
||||||
if (typeof value !== "number" || value < 0 || value > 5) {
|
if (typeof value !== "number" || value < 0 || value > 5) {
|
||||||
throw new BadRequestException(`${key} must be a number between 0 and 5`);
|
throw new BadRequestException(`${key} must be a number between 0 and 5`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (role === "claim") {
|
}
|
||||||
const existing = await this.claimCaseDbService.findById(_id);
|
|
||||||
if (!existing) throw new NotFoundException("Claim file not found");
|
/**
|
||||||
if (!claimCaseTouchesClient(existing as any, clientKeyStr)) {
|
* Applies one insurer rating to every underlying case (claim and/or blame) for
|
||||||
throw new ForbiddenException("This claim does not belong to your organization.");
|
* the shared `publicId`, when that case exists and belongs to this insurer.
|
||||||
}
|
*/
|
||||||
const updated = await this.claimCaseDbService.findByIdAndUpdate(_id, {
|
async rateExpertByPublicId(
|
||||||
|
publicId: string,
|
||||||
|
rating: FileRating,
|
||||||
|
insurerClientKey: string,
|
||||||
|
) {
|
||||||
|
if (!publicId?.trim()) {
|
||||||
|
throw new BadRequestException("publicId is required");
|
||||||
|
}
|
||||||
|
this.validateInsurerFileRating(rating);
|
||||||
|
|
||||||
|
const id = this.getClientId(insurerClientKey);
|
||||||
|
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 out: {
|
||||||
|
publicId: string;
|
||||||
|
claim?: { requestId: string; updatedRating: FileRating };
|
||||||
|
blame?: { requestId: string; updatedRating: FileRating };
|
||||||
|
} = { publicId };
|
||||||
|
|
||||||
|
if (claim) {
|
||||||
|
const claimId = this.parseObjectId(String((claim as any)._id), "claim id");
|
||||||
|
const updated = await this.claimCaseDbService.findByIdAndUpdate(claimId, {
|
||||||
$set: { "evaluation.rating": rating },
|
$set: { "evaluation.rating": rating },
|
||||||
});
|
});
|
||||||
if (!updated) throw new NotFoundException("Claim file not found");
|
if (!updated) throw new NotFoundException("Claim file not found");
|
||||||
return {
|
out.claim = {
|
||||||
message: "Claim expert rated successfully",
|
requestId: String((updated as any)._id),
|
||||||
updatedRating: (updated as any)?.evaluation?.rating || rating,
|
updatedRating: (updated as any)?.evaluation?.rating ?? rating,
|
||||||
requestId: updated._id,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (role === "blame") {
|
|
||||||
const existing = await this.blameRequestDbService.findById(_id);
|
if (blame) {
|
||||||
if (!existing) throw new NotFoundException("Blame file not found");
|
const blameId = this.parseObjectId(String((blame as any)._id), "blame id");
|
||||||
if (!blameCaseTouchesClient(existing as any, clientKeyStr)) {
|
const updated = await this.blameRequestDbService.findByIdAndUpdate(blameId, {
|
||||||
throw new ForbiddenException("This blame case does not belong to your organization.");
|
|
||||||
}
|
|
||||||
const updated = await this.blameRequestDbService.findByIdAndUpdate(_id, {
|
|
||||||
$set: { "expert.rating": rating },
|
$set: { "expert.rating": rating },
|
||||||
});
|
});
|
||||||
if (!updated) throw new NotFoundException("Blame file not found");
|
if (!updated) throw new NotFoundException("Blame file not found");
|
||||||
return {
|
out.blame = {
|
||||||
message: "Blame expert rated successfully",
|
requestId: String((updated as any)._id),
|
||||||
updatedRating: (updated as any)?.expert?.rating || rating,
|
updatedRating: (updated as any)?.expert?.rating ?? rating,
|
||||||
requestId: updated._id,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
throw new BadRequestException("Invalid role");
|
|
||||||
|
return {
|
||||||
|
message: "Rating saved for this file",
|
||||||
|
...out,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async retrieveAllFilesOfClient(insurerId: string) {
|
async retrieveAllFilesOfClient(insurerId: string) {
|
||||||
|
|||||||
Reference in New Issue
Block a user