forked from Yara724/api
Merge pull request 'YARA-853, YARA-857, YARA-858 and a couple of fixes' (#44) from s.yahyaee/yara724-api:main into main
Reviewed-on: Yara724/api#44
This commit is contained in:
@@ -22,9 +22,9 @@ import {
|
|||||||
ApiParam,
|
ApiParam,
|
||||||
ApiQuery,
|
ApiQuery,
|
||||||
ApiTags,
|
ApiTags,
|
||||||
|
ApiOperation,
|
||||||
} from "@nestjs/swagger";
|
} from "@nestjs/swagger";
|
||||||
import { diskStorage } from "multer";
|
import { diskStorage } from "multer";
|
||||||
import { GlobalGuard } from "src/auth/guards/global.guard";
|
|
||||||
import { ClaimAccessGuard } from "src/auth/guards/claim-access.guard";
|
import { ClaimAccessGuard } from "src/auth/guards/claim-access.guard";
|
||||||
import { RolesGuard } from "src/auth/guards/role.guard";
|
import { RolesGuard } from "src/auth/guards/role.guard";
|
||||||
import { Roles } from "src/decorators/roles.decorator";
|
import { Roles } from "src/decorators/roles.decorator";
|
||||||
@@ -49,6 +49,7 @@ export class ClaimRequestManagementController {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
@ApiParam({ name: "blameId" })
|
@ApiParam({ name: "blameId" })
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Post("/:blameId")
|
@Post("/:blameId")
|
||||||
async createClaimRequest(
|
async createClaimRequest(
|
||||||
@Param("blameId") requestId: string,
|
@Param("blameId") requestId: string,
|
||||||
@@ -62,6 +63,7 @@ export class ClaimRequestManagementController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@ApiBody({ type: CarDamagePartDto })
|
@ApiBody({ type: CarDamagePartDto })
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Patch("/car-part-damage/:claimRequestID")
|
@Patch("/car-part-damage/:claimRequestID")
|
||||||
@ApiParam({ name: "claimRequestID" })
|
@ApiParam({ name: "claimRequestID" })
|
||||||
async carPartDamage(
|
async carPartDamage(
|
||||||
@@ -76,6 +78,7 @@ export class ClaimRequestManagementController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Get("/car-other-part")
|
@Get("/car-other-part")
|
||||||
async getCarOtherParts() {
|
async getCarOtherParts() {
|
||||||
const carOtherPart = await readFile(
|
const carOtherPart = await readFile(
|
||||||
@@ -104,6 +107,7 @@ export class ClaimRequestManagementController {
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@ApiConsumes("multipart/form-data")
|
@ApiConsumes("multipart/form-data")
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Patch("/car-other-part-damage/:claimRequestID")
|
@Patch("/car-other-part-damage/:claimRequestID")
|
||||||
async carOtherPartDamage(
|
async carOtherPartDamage(
|
||||||
@Param("claimRequestID") requestId: string,
|
@Param("claimRequestID") requestId: string,
|
||||||
@@ -119,6 +123,7 @@ export class ClaimRequestManagementController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Get("required-documents-status/:claimRequestID")
|
@Get("required-documents-status/:claimRequestID")
|
||||||
@ApiParam({ name: "claimRequestID" })
|
@ApiParam({ name: "claimRequestID" })
|
||||||
async getRequiredDocumentsStatus(
|
async getRequiredDocumentsStatus(
|
||||||
@@ -129,6 +134,7 @@ export class ClaimRequestManagementController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Get("car-part-image-required/:claimRequestID")
|
@Get("car-part-image-required/:claimRequestID")
|
||||||
@ApiParam({ name: "claimRequestID" })
|
@ApiParam({ name: "claimRequestID" })
|
||||||
async getImageRequired(@Param("claimRequestID") requestId) {
|
async getImageRequired(@Param("claimRequestID") requestId) {
|
||||||
@@ -171,6 +177,7 @@ export class ClaimRequestManagementController {
|
|||||||
enum: ClaimRequiredDocumentType,
|
enum: ClaimRequiredDocumentType,
|
||||||
description: "Type of required document to upload",
|
description: "Type of required document to upload",
|
||||||
})
|
})
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Patch("upload-required-document/:claimRequestID")
|
@Patch("upload-required-document/:claimRequestID")
|
||||||
async uploadRequiredDocument(
|
async uploadRequiredDocument(
|
||||||
@Param("claimRequestID") requestId: string,
|
@Param("claimRequestID") requestId: string,
|
||||||
@@ -223,6 +230,7 @@ export class ClaimRequestManagementController {
|
|||||||
name: "partId",
|
name: "partId",
|
||||||
description: "The ID of the specific car part being photographed.",
|
description: "The ID of the specific car part being photographed.",
|
||||||
})
|
})
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Patch("capture-car-part-damage/:claimRequestID/:partId")
|
@Patch("capture-car-part-damage/:claimRequestID/:partId")
|
||||||
async captureCarPartDamage(
|
async captureCarPartDamage(
|
||||||
@Param("partId") partId: string,
|
@Param("partId") partId: string,
|
||||||
@@ -263,6 +271,7 @@ export class ClaimRequestManagementController {
|
|||||||
)
|
)
|
||||||
@ApiConsumes("multipart/form-data")
|
@ApiConsumes("multipart/form-data")
|
||||||
@ApiParam({ name: "claimRequestID" })
|
@ApiParam({ name: "claimRequestID" })
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Patch("car-capture/:claimRequestID")
|
@Patch("car-capture/:claimRequestID")
|
||||||
async captureVideoCapture(
|
async captureVideoCapture(
|
||||||
@Param("claimRequestID") requestId: string,
|
@Param("claimRequestID") requestId: string,
|
||||||
@@ -274,11 +283,13 @@ export class ClaimRequestManagementController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Get("requests/")
|
@Get("requests/")
|
||||||
async getRequest(@CurrentUser() currentUser) {
|
async getRequest(@CurrentUser() currentUser) {
|
||||||
return await this.claimRequestManagementService.myRequests(currentUser);
|
return await this.claimRequestManagementService.myRequests(currentUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Get("request/:claimRequestId")
|
@Get("request/:claimRequestId")
|
||||||
@ApiParam({ name: "claimRequestId" })
|
@ApiParam({ name: "claimRequestId" })
|
||||||
myRequests(
|
myRequests(
|
||||||
@@ -288,6 +299,7 @@ export class ClaimRequestManagementController {
|
|||||||
return this.claimRequestManagementService.requestDetails(requestId, user);
|
return this.claimRequestManagementService.requestDetails(requestId, user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Put("request/reply/:claimRequestId")
|
@Put("request/reply/:claimRequestId")
|
||||||
@ApiParam({ name: "claimRequestId" })
|
@ApiParam({ name: "claimRequestId" })
|
||||||
@UseInterceptors(
|
@UseInterceptors(
|
||||||
@@ -326,6 +338,7 @@ export class ClaimRequestManagementController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Put("request/resend/:claimRequestId/objection")
|
@Put("request/resend/:claimRequestId/objection")
|
||||||
@ApiParam({ name: "claimRequestId" })
|
@ApiParam({ name: "claimRequestId" })
|
||||||
@ApiConsumes("application/json")
|
@ApiConsumes("application/json")
|
||||||
@@ -343,6 +356,7 @@ export class ClaimRequestManagementController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Patch("request/resend/:claimRequestId")
|
@Patch("request/resend/:claimRequestId")
|
||||||
@ApiConsumes("multipart/form-data")
|
@ApiConsumes("multipart/form-data")
|
||||||
@ApiParam({ name: "claimRequestId" })
|
@ApiParam({ name: "claimRequestId" })
|
||||||
@@ -393,6 +407,7 @@ export class ClaimRequestManagementController {
|
|||||||
* User satisfaction rating for a completed claim file.
|
* User satisfaction rating for a completed claim file.
|
||||||
* Only the damaged user (claim owner) can rate their claim after it is closed.
|
* Only the damaged user (claim owner) can rate their claim after it is closed.
|
||||||
*/
|
*/
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Put("request/:claimRequestId/user-rating")
|
@Put("request/:claimRequestId/user-rating")
|
||||||
@ApiParam({ name: "claimRequestId" })
|
@ApiParam({ name: "claimRequestId" })
|
||||||
@ApiBody({ type: UserRatingDto })
|
@ApiBody({ type: UserRatingDto })
|
||||||
@@ -408,6 +423,7 @@ export class ClaimRequestManagementController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Patch("request/reply/:claimRequestId/:partId/upload-factor")
|
@Patch("request/reply/:claimRequestId/:partId/upload-factor")
|
||||||
@ApiConsumes("multipart/form-data")
|
@ApiConsumes("multipart/form-data")
|
||||||
@ApiParam({ name: "claimRequestId" })
|
@ApiParam({ name: "claimRequestId" })
|
||||||
@@ -452,6 +468,7 @@ export class ClaimRequestManagementController {
|
|||||||
|
|
||||||
@ApiBody({ type: InPersonVisitDto })
|
@ApiBody({ type: InPersonVisitDto })
|
||||||
@ApiParam({ name: "id" })
|
@ApiParam({ name: "id" })
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Patch(":id/visit")
|
@Patch(":id/visit")
|
||||||
async inPersonVisit(
|
async inPersonVisit(
|
||||||
@Param("id") requestId: string,
|
@Param("id") requestId: string,
|
||||||
@@ -466,6 +483,7 @@ export class ClaimRequestManagementController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Get("branches/:insuranceId")
|
@Get("branches/:insuranceId")
|
||||||
async insuranceBranches(@Param("insuranceId") insuranceId: string) {
|
async insuranceBranches(@Param("insuranceId") insuranceId: string) {
|
||||||
return await this.claimRequestManagementService.retrieveInsuranceBranches(
|
return await this.claimRequestManagementService.retrieveInsuranceBranches(
|
||||||
@@ -473,6 +491,7 @@ export class ClaimRequestManagementController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Get("fanavaran-submit/:claimRequestId")
|
@Get("fanavaran-submit/:claimRequestId")
|
||||||
@ApiParam({ name: "claimRequestId" })
|
@ApiParam({ name: "claimRequestId" })
|
||||||
async fanavaranSubmit(@Param("claimRequestId") claimRequestId: string) {
|
async fanavaranSubmit(@Param("claimRequestId") claimRequestId: string) {
|
||||||
@@ -481,6 +500,7 @@ export class ClaimRequestManagementController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Post("fanavaran-submit/:claimRequestId")
|
@Post("fanavaran-submit/:claimRequestId")
|
||||||
@ApiParam({ name: "claimRequestId" })
|
@ApiParam({ name: "claimRequestId" })
|
||||||
async submitToFanavaran(@Param("claimRequestId") claimRequestId: string) {
|
async submitToFanavaran(@Param("claimRequestId") claimRequestId: string) {
|
||||||
|
|||||||
@@ -1900,6 +1900,90 @@ export class ClaimRequestManagementService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* V2 response mapper:
|
||||||
|
* - convert factorLink ObjectId -> public URL
|
||||||
|
* - enrich daghi.branchId with branchName when available
|
||||||
|
*/
|
||||||
|
private async mapEvaluationForClient(evaluation: any): Promise<any | undefined> {
|
||||||
|
if (!evaluation) return undefined;
|
||||||
|
|
||||||
|
const mapReply = async (reply: any) => {
|
||||||
|
if (!reply) return reply;
|
||||||
|
|
||||||
|
const mapped = {
|
||||||
|
...(typeof reply?.toObject === "function" ? reply.toObject() : reply),
|
||||||
|
};
|
||||||
|
const parts = Array.isArray(mapped.parts) ? mapped.parts : [];
|
||||||
|
|
||||||
|
const factorIds = new Set<string>();
|
||||||
|
const branchIds = new Set<string>();
|
||||||
|
for (const p of parts) {
|
||||||
|
if (p?.factorLink && Types.ObjectId.isValid(String(p.factorLink))) {
|
||||||
|
factorIds.add(String(p.factorLink));
|
||||||
|
}
|
||||||
|
const daghi = p?.daghi;
|
||||||
|
const rawBranchId =
|
||||||
|
daghi && typeof daghi === "object" ? (daghi as any).branchId : undefined;
|
||||||
|
if (rawBranchId && Types.ObjectId.isValid(String(rawBranchId))) {
|
||||||
|
branchIds.add(String(rawBranchId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const factorMap = new Map<string, string>();
|
||||||
|
await Promise.all(
|
||||||
|
Array.from(factorIds).map(async (id) => {
|
||||||
|
const factorDoc = await this.claimFactorsImageDbService.findById(id);
|
||||||
|
if (factorDoc?.path) {
|
||||||
|
factorMap.set(id, buildFileLink(factorDoc.path));
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const branchMap = new Map<string, string>();
|
||||||
|
await Promise.all(
|
||||||
|
Array.from(branchIds).map(async (id) => {
|
||||||
|
const branch = await this.branchDbService.findById(id);
|
||||||
|
if (branch?.name) {
|
||||||
|
branchMap.set(id, branch.name);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
mapped.parts = parts.map((part: any) => {
|
||||||
|
const nextPart = { ...part };
|
||||||
|
|
||||||
|
if (nextPart.factorLink && Types.ObjectId.isValid(String(nextPart.factorLink))) {
|
||||||
|
const link = factorMap.get(String(nextPart.factorLink));
|
||||||
|
if (link) nextPart.factorLink = link;
|
||||||
|
}
|
||||||
|
|
||||||
|
const daghi = nextPart.daghi;
|
||||||
|
if (daghi && typeof daghi === "object" && !Array.isArray(daghi)) {
|
||||||
|
const branchId = (daghi as any).branchId;
|
||||||
|
const branchName =
|
||||||
|
branchId && Types.ObjectId.isValid(String(branchId))
|
||||||
|
? branchMap.get(String(branchId))
|
||||||
|
: undefined;
|
||||||
|
nextPart.daghi = {
|
||||||
|
...daghi,
|
||||||
|
...(branchName ? { branchName } : {}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return nextPart;
|
||||||
|
});
|
||||||
|
|
||||||
|
return mapped;
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
...evaluation,
|
||||||
|
damageExpertReply: await mapReply(evaluation.damageExpertReply),
|
||||||
|
damageExpertReplyFinal: await mapReply(evaluation.damageExpertReplyFinal),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
async submitUserReply(
|
async submitUserReply(
|
||||||
requestId: string,
|
requestId: string,
|
||||||
body: UserCommentDto,
|
body: UserCommentDto,
|
||||||
@@ -2189,6 +2273,7 @@ export class ClaimRequestManagementService {
|
|||||||
return {
|
return {
|
||||||
message: "Factor uploaded successfully. Awaiting expert validation.",
|
message: "Factor uploaded successfully. Awaiting expert validation.",
|
||||||
factorId: factorRecord._id,
|
factorId: factorRecord._id,
|
||||||
|
factorLink: buildFileLink(file.path),
|
||||||
url: buildFileLink(file.path),
|
url: buildFileLink(file.path),
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -5191,6 +5276,8 @@ export class ClaimRequestManagementService {
|
|||||||
}
|
}
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
|
const mappedEvaluation = await this.mapEvaluationForClient(claim.evaluation);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
claimRequestId: claim._id.toString(),
|
claimRequestId: claim._id.toString(),
|
||||||
publicId: claim.publicId,
|
publicId: claim.publicId,
|
||||||
@@ -5210,10 +5297,10 @@ export class ClaimRequestManagementService {
|
|||||||
carAngles,
|
carAngles,
|
||||||
damagedParts,
|
damagedParts,
|
||||||
expertResend,
|
expertResend,
|
||||||
evaluation: claim.evaluation
|
evaluation: mappedEvaluation
|
||||||
? {
|
? {
|
||||||
damageExpertReply: claim.evaluation.damageExpertReply,
|
damageExpertReply: mappedEvaluation.damageExpertReply,
|
||||||
damageExpertReplyFinal: claim.evaluation.damageExpertReplyFinal,
|
damageExpertReplyFinal: mappedEvaluation.damageExpertReplyFinal,
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
...(isExpertViewer
|
...(isExpertViewer
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
UseInterceptors,
|
UseInterceptors,
|
||||||
UploadedFile,
|
UploadedFile,
|
||||||
} from "@nestjs/common";
|
} from "@nestjs/common";
|
||||||
|
import { readFile } from "node:fs/promises";
|
||||||
import { ApiBearerAuth, ApiParam, ApiTags, ApiOperation, ApiResponse, ApiBody, ApiConsumes } from "@nestjs/swagger";
|
import { ApiBearerAuth, ApiParam, ApiTags, ApiOperation, ApiResponse, ApiBody, ApiConsumes } from "@nestjs/swagger";
|
||||||
import { FileInterceptor } from "@nestjs/platform-express";
|
import { FileInterceptor } from "@nestjs/platform-express";
|
||||||
import { diskStorage } from "multer";
|
import { diskStorage } from "multer";
|
||||||
@@ -273,6 +274,49 @@ export class ClaimRequestManagementV2Controller {
|
|||||||
return this.claimRequestManagementService.getOuterPartsCatalogV2(carType);
|
return this.claimRequestManagementService.getOuterPartsCatalogV2(carType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Get("branches/:insuranceId")
|
||||||
|
@ApiOperation({
|
||||||
|
summary: "Get insurer branches (V2)",
|
||||||
|
description:
|
||||||
|
"Returns branch list for a given insurer/client id so frontend can render branch options (name/code/address/city/state) and submit selected branchId in daghi part options.",
|
||||||
|
})
|
||||||
|
@ApiParam({
|
||||||
|
name: "insuranceId",
|
||||||
|
description: "Insurer client id (MongoDB ObjectId)",
|
||||||
|
example: "60d5ec49e7b2f8001c8e4d2a",
|
||||||
|
})
|
||||||
|
@ApiResponse({
|
||||||
|
status: 200,
|
||||||
|
description: "List of branches for insurer",
|
||||||
|
})
|
||||||
|
async getInsuranceBranchesV2(@Param("insuranceId") insuranceId: string) {
|
||||||
|
return await this.claimRequestManagementService.retrieveInsuranceBranches(
|
||||||
|
insuranceId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get("car-other-part")
|
||||||
|
@ApiOperation({
|
||||||
|
summary: "Get other (non-body) parts catalog",
|
||||||
|
description:
|
||||||
|
"Returns legacy other-parts catalog used by frontend. Response is parsed JSON.",
|
||||||
|
})
|
||||||
|
@ApiResponse({
|
||||||
|
status: 200,
|
||||||
|
description: "Other parts catalog",
|
||||||
|
})
|
||||||
|
async getCarOtherPartsV2() {
|
||||||
|
const raw = await readFile(
|
||||||
|
`${process.cwd()}/src/static/car-part.json`,
|
||||||
|
"utf-8",
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
return JSON.parse(raw);
|
||||||
|
} catch {
|
||||||
|
return raw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Patch("select-outer-parts/:claimRequestId")
|
@Patch("select-outer-parts/:claimRequestId")
|
||||||
@ApiOperation({
|
@ApiOperation({
|
||||||
summary: "Select Damaged Outer Car Parts (V2 - Step 2)",
|
summary: "Select Damaged Outer Car Parts (V2 - Step 2)",
|
||||||
|
|||||||
@@ -7,25 +7,27 @@ import {
|
|||||||
} from "@nestjs/common";
|
} from "@nestjs/common";
|
||||||
import { ApiBearerAuth, ApiTags } from "@nestjs/swagger";
|
import { ApiBearerAuth, ApiTags } from "@nestjs/swagger";
|
||||||
import { GlobalGuard } from "src/auth/guards/global.guard";
|
import { GlobalGuard } from "src/auth/guards/global.guard";
|
||||||
|
import { RolesGuard } from "src/auth/guards/role.guard";
|
||||||
|
import { Roles } from "src/decorators/roles.decorator";
|
||||||
import { CurrentUser } from "src/decorators/user.decorator";
|
import { CurrentUser } from "src/decorators/user.decorator";
|
||||||
|
import { RoleEnum } from "src/Types&Enums/role.enum";
|
||||||
import { ClientService } from "./client.service";
|
import { ClientService } from "./client.service";
|
||||||
import { ClientDto } from "./dto/create-client.dto";
|
import { ClientDto } from "./dto/create-client.dto";
|
||||||
|
|
||||||
@Controller("client")
|
@Controller("client")
|
||||||
@ApiTags("client-management")
|
@ApiTags("client-management")
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@UseGuards(GlobalGuard, RolesGuard)
|
||||||
|
@Roles(RoleEnum.ADMIN)
|
||||||
export class ClientController {
|
export class ClientController {
|
||||||
constructor(private readonly clientService: ClientService) {}
|
constructor(private readonly clientService: ClientService) {}
|
||||||
|
|
||||||
@Post()
|
@Post()
|
||||||
@UseGuards(GlobalGuard)
|
|
||||||
@ApiBearerAuth()
|
|
||||||
async addClient(@Body() client: ClientDto) {
|
async addClient(@Body() client: ClientDto) {
|
||||||
return await this.clientService.addClient(client);
|
return await this.clientService.addClient(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
@ApiBearerAuth()
|
|
||||||
@UseGuards(GlobalGuard)
|
|
||||||
async getClient(@CurrentUser() user) {
|
async getClient(@CurrentUser() user) {
|
||||||
return await this.clientService.getClients();
|
return await this.clientService.getClients();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,31 +1,44 @@
|
|||||||
import { Injectable } from "@nestjs/common";
|
|
||||||
import { ApiProperty } from "@nestjs/swagger";
|
import { ApiProperty } from "@nestjs/swagger";
|
||||||
|
import { IsIn, IsNotEmpty, IsNumber, IsOptional, IsString, ValidateNested } from "class-validator";
|
||||||
|
import { Type } from "class-transformer";
|
||||||
import { Types } from "mongoose";
|
import { Types } from "mongoose";
|
||||||
|
|
||||||
class ClientName {
|
class ClientName {
|
||||||
@ApiProperty({})
|
@ApiProperty({})
|
||||||
|
@IsString()
|
||||||
|
@IsNotEmpty()
|
||||||
persian: string;
|
persian: string;
|
||||||
|
|
||||||
@ApiProperty({})
|
@ApiProperty({})
|
||||||
|
@IsString()
|
||||||
|
@IsNotEmpty()
|
||||||
english: string;
|
english: string;
|
||||||
}
|
}
|
||||||
class Property {
|
class Property {
|
||||||
@ApiProperty({})
|
@ApiProperty({})
|
||||||
|
@IsString()
|
||||||
|
@IsNotEmpty()
|
||||||
smsApiKey: string;
|
smsApiKey: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class ClientDto {
|
export class ClientDto {
|
||||||
@ApiProperty({ required: true })
|
@ApiProperty({ required: true })
|
||||||
|
@ValidateNested()
|
||||||
|
@Type(() => ClientName)
|
||||||
clientName: ClientName;
|
clientName: ClientName;
|
||||||
|
|
||||||
@ApiProperty({ required: true })
|
@ApiProperty({ required: true })
|
||||||
|
@IsNumber()
|
||||||
clientCode: number;
|
clientCode: number;
|
||||||
|
|
||||||
@ApiProperty({ required: false })
|
@ApiProperty({ required: false })
|
||||||
property: Property;
|
@IsOptional()
|
||||||
|
@ValidateNested()
|
||||||
|
@Type(() => Property)
|
||||||
|
property?: Property;
|
||||||
|
|
||||||
@ApiProperty({ examples: ["legal", "genuine"] })
|
@ApiProperty({ examples: ["legal", "genuine"] })
|
||||||
|
@IsIn(["legal", "genuine"])
|
||||||
useExpertMode: "legal" | "genuine";
|
useExpertMode: "legal" | "genuine";
|
||||||
}
|
}
|
||||||
export class ClientDtoRs {
|
export class ClientDtoRs {
|
||||||
@@ -35,6 +48,9 @@ export class ClientDtoRs {
|
|||||||
useExpertsMode: string;
|
useExpertsMode: string;
|
||||||
constructor(readonly client) {
|
constructor(readonly client) {
|
||||||
this.persian = client.clientName.persian;
|
this.persian = client.clientName.persian;
|
||||||
|
this.english = client.clientName.english;
|
||||||
|
this.clientId = client._id;
|
||||||
|
this.useExpertsMode = client.useExpertMode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export class BranchDbService {
|
|||||||
async findAll(insuranceId: string): Promise<BranchModel[]> {
|
async findAll(insuranceId: string): Promise<BranchModel[]> {
|
||||||
return await this.branchModel.find({
|
return await this.branchModel.find({
|
||||||
clientKey: new Types.ObjectId(insuranceId),
|
clientKey: new Types.ObjectId(insuranceId),
|
||||||
});
|
}, { _id: 1, name: 1, code: 1, city: 1, state: 1, address: 1, phoneNumber: 1, createdAtFa: 1, updatedAtFa: 1 });
|
||||||
}
|
}
|
||||||
|
|
||||||
async findById(id: string): Promise<BranchModel | null> {
|
async findById(id: string): Promise<BranchModel | null> {
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
import { ExecutionContext, createParamDecorator } from "@nestjs/common";
|
|
||||||
|
|
||||||
export const CustomHeader = createParamDecorator(
|
|
||||||
(data, ctx: ExecutionContext) => {
|
|
||||||
},
|
|
||||||
);
|
|
||||||
@@ -18,6 +18,7 @@ import {
|
|||||||
ApiParam,
|
ApiParam,
|
||||||
ApiProduces,
|
ApiProduces,
|
||||||
ApiTags,
|
ApiTags,
|
||||||
|
ApiOperation,
|
||||||
} from "@nestjs/swagger";
|
} from "@nestjs/swagger";
|
||||||
import { Response, Request } from "express";
|
import { Response, Request } from "express";
|
||||||
import { LocalActorAuthGuard } from "src/auth/guards/actor-local.guard";
|
import { LocalActorAuthGuard } from "src/auth/guards/actor-local.guard";
|
||||||
@@ -45,30 +46,35 @@ export class ExpertBlameController {
|
|||||||
|
|
||||||
// TODO role guard for expert fix
|
// TODO role guard for expert fix
|
||||||
@Roles(RoleEnum.EXPERT)
|
@Roles(RoleEnum.EXPERT)
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Get()
|
@Get()
|
||||||
async findAll(@CurrentUser() actor, @ClientKey() client) {
|
async findAll(@CurrentUser() actor, @ClientKey() client) {
|
||||||
return await this.expertBlameService.findAll(actor);
|
return await this.expertBlameService.findAll(actor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Roles(RoleEnum.EXPERT)
|
@Roles(RoleEnum.EXPERT)
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Get(":id")
|
@Get(":id")
|
||||||
async findOne(@Param("id") id: string, @CurrentUser() actor) {
|
async findOne(@Param("id") id: string, @CurrentUser() actor) {
|
||||||
return await this.expertBlameService.findOne(id, actor.sub);
|
return await this.expertBlameService.findOne(id, actor.sub);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Roles(RoleEnum.EXPERT)
|
@Roles(RoleEnum.EXPERT)
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Put("lock/:id")
|
@Put("lock/:id")
|
||||||
async lockRequest(@Param("id") id: string, @CurrentUser() actor) {
|
async lockRequest(@Param("id") id: string, @CurrentUser() actor) {
|
||||||
return await this.expertBlameService.lockRequest(id, actor);
|
return await this.expertBlameService.lockRequest(id, actor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Roles(RoleEnum.EXPERT)
|
@Roles(RoleEnum.EXPERT)
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Get("request/accident-fields")
|
@Get("request/accident-fields")
|
||||||
async getAccidentFields() {
|
async getAccidentFields() {
|
||||||
return await this.expertBlameService.getAccidentField();
|
return await this.expertBlameService.getAccidentField();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Roles(RoleEnum.EXPERT)
|
@Roles(RoleEnum.EXPERT)
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Put("reply/submit/:id")
|
@Put("reply/submit/:id")
|
||||||
@ApiBody({ type: SubmitReplyDto })
|
@ApiBody({ type: SubmitReplyDto })
|
||||||
@ApiParam({ name: "id" })
|
@ApiParam({ name: "id" })
|
||||||
@@ -95,6 +101,7 @@ export class ExpertBlameController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Roles(RoleEnum.EXPERT)
|
@Roles(RoleEnum.EXPERT)
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Put("reply/resend/first/:id")
|
@Put("reply/resend/first/:id")
|
||||||
@ApiBody({ type: ResendFirstPartyDto })
|
@ApiBody({ type: ResendFirstPartyDto })
|
||||||
@ApiParam({ name: "id" })
|
@ApiParam({ name: "id" })
|
||||||
@@ -107,6 +114,7 @@ export class ExpertBlameController {
|
|||||||
return this.handleResendRequest(id, body, actor.sub, req);
|
return this.handleResendRequest(id, body, actor.sub, req);
|
||||||
}
|
}
|
||||||
@Roles(RoleEnum.EXPERT)
|
@Roles(RoleEnum.EXPERT)
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Put("reply/resend/second/:id")
|
@Put("reply/resend/second/:id")
|
||||||
@Roles(RoleEnum.EXPERT)
|
@Roles(RoleEnum.EXPERT)
|
||||||
@ApiBody({ type: ResendSecondPartyDto })
|
@ApiBody({ type: ResendSecondPartyDto })
|
||||||
@@ -120,6 +128,7 @@ export class ExpertBlameController {
|
|||||||
return this.handleResendRequest(id, body, actor.sub, req);
|
return this.handleResendRequest(id, body, actor.sub, req);
|
||||||
}
|
}
|
||||||
@Roles(RoleEnum.EXPERT)
|
@Roles(RoleEnum.EXPERT)
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Get("stream/:requestId")
|
@Get("stream/:requestId")
|
||||||
@ApiParam({ name: "requestId" })
|
@ApiParam({ name: "requestId" })
|
||||||
async streamVideo(@Param("requestId") requestId: string) {
|
async streamVideo(@Param("requestId") requestId: string) {
|
||||||
@@ -127,6 +136,7 @@ export class ExpertBlameController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@UseInterceptors(LoggingInterceptor)
|
@UseInterceptors(LoggingInterceptor)
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Get("voice/:requestId/:voiceId")
|
@Get("voice/:requestId/:voiceId")
|
||||||
@Roles(RoleEnum.EXPERT, RoleEnum.DAMAGE_EXPERT)
|
@Roles(RoleEnum.EXPERT, RoleEnum.DAMAGE_EXPERT)
|
||||||
@ApiParam({ name: "requestId" })
|
@ApiParam({ name: "requestId" })
|
||||||
@@ -149,6 +159,7 @@ export class ExpertBlameController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@ApiParam({ name: "id" })
|
@ApiParam({ name: "id" })
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Patch(":id/visit")
|
@Patch(":id/visit")
|
||||||
async inPersonVisit(@Param("id") requestId: string, @CurrentUser() actor) {
|
async inPersonVisit(@Param("id") requestId: string, @CurrentUser() actor) {
|
||||||
return await this.expertBlameService.inPersonVisit(requestId, actor);
|
return await this.expertBlameService.inPersonVisit(requestId, actor);
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
ApiParam,
|
ApiParam,
|
||||||
ApiQuery,
|
ApiQuery,
|
||||||
ApiTags,
|
ApiTags,
|
||||||
|
ApiOperation,
|
||||||
} from "@nestjs/swagger";
|
} from "@nestjs/swagger";
|
||||||
import { LocalActorAuthGuard } from "src/auth/guards/actor-local.guard";
|
import { LocalActorAuthGuard } from "src/auth/guards/actor-local.guard";
|
||||||
import { RolesGuard } from "src/auth/guards/role.guard";
|
import { RolesGuard } from "src/auth/guards/role.guard";
|
||||||
@@ -40,11 +41,13 @@ import { FactorValidationDto } from "./dto/factor-validation.dto";
|
|||||||
export class ExpertClaimController {
|
export class ExpertClaimController {
|
||||||
constructor(private readonly expertClaimService: ExpertClaimService) {}
|
constructor(private readonly expertClaimService: ExpertClaimService) {}
|
||||||
|
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Get()
|
@Get()
|
||||||
async getAllClaimRequests(@CurrentUser() actor, @ClientKey() client) {
|
async getAllClaimRequests(@CurrentUser() actor, @ClientKey() client) {
|
||||||
return await this.expertClaimService.getClaimRequestsListForExpert(actor);
|
return await this.expertClaimService.getClaimRequestsListForExpert(actor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Get("/:id")
|
@Get("/:id")
|
||||||
@ApiParam({ name: "id" })
|
@ApiParam({ name: "id" })
|
||||||
@ApiQuery({
|
@ApiQuery({
|
||||||
@@ -64,12 +67,14 @@ export class ExpertClaimController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Get("imageRequired/:id")
|
@Get("imageRequired/:id")
|
||||||
@ApiParam({ name: "id" })
|
@ApiParam({ name: "id" })
|
||||||
async getImageRequired(@Param("id") id: string, @CurrentUser() currentUser) {
|
async getImageRequired(@Param("id") id: string, @CurrentUser() currentUser) {
|
||||||
return await this.expertClaimService.imageRequired(id, currentUser);
|
return await this.expertClaimService.imageRequired(id, currentUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Get("required-documents/:id")
|
@Get("required-documents/:id")
|
||||||
@ApiParam({ name: "id" })
|
@ApiParam({ name: "id" })
|
||||||
async getRequiredDocuments(
|
async getRequiredDocuments(
|
||||||
@@ -79,11 +84,13 @@ export class ExpertClaimController {
|
|||||||
return await this.expertClaimService.getRequiredDocuments(id, currentUser);
|
return await this.expertClaimService.getRequiredDocuments(id, currentUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Put("lock/:id")
|
@Put("lock/:id")
|
||||||
async lockClaimRequest(@Param("id") requestId: string, @CurrentUser() actor) {
|
async lockClaimRequest(@Param("id") requestId: string, @CurrentUser() actor) {
|
||||||
return await this.expertClaimService.lockClaimRequest(requestId, actor);
|
return await this.expertClaimService.lockClaimRequest(requestId, actor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Put("reply/submit/:id")
|
@Put("reply/submit/:id")
|
||||||
@ApiParam({ name: "id" })
|
@ApiParam({ name: "id" })
|
||||||
async submitReplyRequest(
|
async submitReplyRequest(
|
||||||
@@ -98,6 +105,7 @@ export class ExpertClaimController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Put("resend/submit/:id")
|
@Put("resend/submit/:id")
|
||||||
@ApiParam({ name: "id" })
|
@ApiParam({ name: "id" })
|
||||||
async submitResend(
|
async submitResend(
|
||||||
@@ -117,6 +125,7 @@ export class ExpertClaimController {
|
|||||||
enum: ["car-capture", "accident"],
|
enum: ["car-capture", "accident"],
|
||||||
required: true,
|
required: true,
|
||||||
})
|
})
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Get("stream/:id/video")
|
@Get("stream/:id/video")
|
||||||
@Header("Accept-Ranges", "bytes")
|
@Header("Accept-Ranges", "bytes")
|
||||||
@Header("Content-Type", "video/mp4")
|
@Header("Content-Type", "video/mp4")
|
||||||
@@ -134,6 +143,7 @@ export class ExpertClaimController {
|
|||||||
enum: ["car-capture", "accident"],
|
enum: ["car-capture", "accident"],
|
||||||
required: true,
|
required: true,
|
||||||
})
|
})
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Get("link/:id/video")
|
@Get("link/:id/video")
|
||||||
@ApiParam({ name: "id" })
|
@ApiParam({ name: "id" })
|
||||||
async getClaimVideoLink(
|
async getClaimVideoLink(
|
||||||
@@ -143,12 +153,14 @@ export class ExpertClaimController {
|
|||||||
return this.expertClaimService.getVideoLink(id, query);
|
return this.expertClaimService.getVideoLink(id, query);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Get("stream/:id/voice")
|
@Get("stream/:id/voice")
|
||||||
@ApiParam({ name: "id" })
|
@ApiParam({ name: "id" })
|
||||||
async getClaimVoiceLink(@Param("id") id: string) {
|
async getClaimVoiceLink(@Param("id") id: string) {
|
||||||
return await this.expertClaimService.getVoiceLink(id);
|
return await this.expertClaimService.getVoiceLink(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Patch("validate-factors/:claimId")
|
@Patch("validate-factors/:claimId")
|
||||||
@ApiParam({ name: "claimId" })
|
@ApiParam({ name: "claimId" })
|
||||||
@ApiBody({ type: FactorValidationDto })
|
@ApiBody({ type: FactorValidationDto })
|
||||||
@@ -166,11 +178,13 @@ export class ExpertClaimController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@ApiParam({ name: "id" })
|
@ApiParam({ name: "id" })
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Patch(":id/visit")
|
@Patch(":id/visit")
|
||||||
async inPersonVisit(@Param("id") requestId: string, @CurrentUser() actor) {
|
async inPersonVisit(@Param("id") requestId: string, @CurrentUser() actor) {
|
||||||
return await this.expertClaimService.inPersonVisit(requestId, actor);
|
return await this.expertClaimService.inPersonVisit(requestId, actor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Get("branches/:insuranceId")
|
@Get("branches/:insuranceId")
|
||||||
@ApiParam({ name: "insuranceId" })
|
@ApiParam({ name: "insuranceId" })
|
||||||
async getInsuranceBranches(@Param("insuranceId") insuranceId: string) {
|
async getInsuranceBranches(@Param("insuranceId") insuranceId: string) {
|
||||||
|
|||||||
83
src/expert-insurer/dto/create-insurer-expert.dto.ts
Normal file
83
src/expert-insurer/dto/create-insurer-expert.dto.ts
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
|
||||||
|
import { IsEmail, IsEnum, IsMongoId, IsNotEmpty, IsOptional, IsString } from "class-validator";
|
||||||
|
import { RoleEnum } from "src/Types&Enums/role.enum";
|
||||||
|
import { UserType } from "src/Types&Enums/userType.enum";
|
||||||
|
|
||||||
|
export class CreateInsurerExpertDto {
|
||||||
|
@ApiProperty({ example: "Ali" })
|
||||||
|
@IsString()
|
||||||
|
@IsNotEmpty()
|
||||||
|
firstName: string;
|
||||||
|
|
||||||
|
@ApiProperty({ example: "Ahmadi" })
|
||||||
|
@IsString()
|
||||||
|
@IsNotEmpty()
|
||||||
|
lastName: string;
|
||||||
|
|
||||||
|
@ApiProperty({ example: "1234567890" })
|
||||||
|
@IsString()
|
||||||
|
@IsNotEmpty()
|
||||||
|
nationalCode: string;
|
||||||
|
|
||||||
|
@ApiProperty({ example: "expert@example.com" })
|
||||||
|
@IsEmail()
|
||||||
|
email: string;
|
||||||
|
|
||||||
|
@ApiProperty({ example: "StrongPass!123" })
|
||||||
|
@IsString()
|
||||||
|
@IsNotEmpty()
|
||||||
|
password: string;
|
||||||
|
|
||||||
|
@ApiProperty({ example: "09121234567" })
|
||||||
|
@IsString()
|
||||||
|
@IsNotEmpty()
|
||||||
|
mobile: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
example: "665f0e5ab74d670939b08920",
|
||||||
|
description: "Branch id this expert belongs to",
|
||||||
|
})
|
||||||
|
@IsMongoId()
|
||||||
|
branchId: string;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({ enum: UserType, example: UserType.LEGAL })
|
||||||
|
@IsOptional()
|
||||||
|
@IsEnum(UserType)
|
||||||
|
userType?: UserType;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({ example: "02112345678" })
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
phone?: string;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({ example: "Tehran" })
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
state?: string;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({ example: "Tehran" })
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
city?: string;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({ example: "No. 12, Sample St." })
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
address?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CreateBlameExpertByInsurerDto extends CreateInsurerExpertDto {
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
enum: [RoleEnum.EXPERT],
|
||||||
|
default: RoleEnum.EXPERT,
|
||||||
|
})
|
||||||
|
role?: RoleEnum.EXPERT;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CreateClaimExpertByInsurerDto extends CreateInsurerExpertDto {
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
enum: [RoleEnum.DAMAGE_EXPERT],
|
||||||
|
default: RoleEnum.DAMAGE_EXPERT,
|
||||||
|
})
|
||||||
|
role?: RoleEnum.DAMAGE_EXPERT;
|
||||||
|
}
|
||||||
@@ -26,6 +26,10 @@ import { FileRating } from "src/request-management/entities/schema/request-manag
|
|||||||
import { RoleEnum } from "src/Types&Enums/role.enum";
|
import { RoleEnum } from "src/Types&Enums/role.enum";
|
||||||
import { ExpertInsurerService } from "./expert-insurer.service";
|
import { ExpertInsurerService } from "./expert-insurer.service";
|
||||||
import { CreateBranchDto } from "src/client/dto/create-branch.dto";
|
import { CreateBranchDto } from "src/client/dto/create-branch.dto";
|
||||||
|
import {
|
||||||
|
CreateBlameExpertByInsurerDto,
|
||||||
|
CreateClaimExpertByInsurerDto,
|
||||||
|
} from "./dto/create-insurer-expert.dto";
|
||||||
|
|
||||||
@Controller("expert-insurer")
|
@Controller("expert-insurer")
|
||||||
@ApiTags("expert-insurer-panel")
|
@ApiTags("expert-insurer-panel")
|
||||||
@@ -35,16 +39,59 @@ import { CreateBranchDto } from "src/client/dto/create-branch.dto";
|
|||||||
export class ExpertInsurerController {
|
export class ExpertInsurerController {
|
||||||
constructor(private readonly expertInsurerService: ExpertInsurerService) {}
|
constructor(private readonly expertInsurerService: ExpertInsurerService) {}
|
||||||
|
|
||||||
@Get("files")
|
@Get("branches")
|
||||||
async getAllFiles(@CurrentUser() insurer) {
|
async getInsuranceBranches(@CurrentUser() insurer) {
|
||||||
return await this.expertInsurerService.retrieveAllFilesOfClient(
|
if (!insurer) {
|
||||||
|
throw new UnauthorizedException("Could not identify the current user.");
|
||||||
|
}
|
||||||
|
return await this.expertInsurerService.retrieveInsuranceBranches(
|
||||||
insurer.clientKey,
|
insurer.clientKey,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Post("branches")
|
||||||
|
@ApiBody({ type: CreateBranchDto })
|
||||||
|
async addBranch(
|
||||||
|
@CurrentUser() insurer,
|
||||||
|
@Body() createBranchDto: CreateBranchDto,
|
||||||
|
) {
|
||||||
|
if (!insurer) {
|
||||||
|
throw new UnauthorizedException("Could not identify the current user.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return await this.expertInsurerService.addBranch(
|
||||||
|
insurer.clientKey,
|
||||||
|
createBranchDto,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post("experts/blame")
|
||||||
|
@ApiBody({ type: CreateBlameExpertByInsurerDto })
|
||||||
|
async addBlameExpert(
|
||||||
|
@CurrentUser() insurer,
|
||||||
|
@Body() body: CreateBlameExpertByInsurerDto,
|
||||||
|
) {
|
||||||
|
if (!insurer) {
|
||||||
|
throw new UnauthorizedException("Could not identify the current user.");
|
||||||
|
}
|
||||||
|
return this.expertInsurerService.addBlameExpert(insurer.clientKey, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post("experts/claim")
|
||||||
|
@ApiBody({ type: CreateClaimExpertByInsurerDto })
|
||||||
|
async addClaimExpert(
|
||||||
|
@CurrentUser() insurer,
|
||||||
|
@Body() body: CreateClaimExpertByInsurerDto,
|
||||||
|
) {
|
||||||
|
if (!insurer) {
|
||||||
|
throw new UnauthorizedException("Could not identify the current user.");
|
||||||
|
}
|
||||||
|
return this.expertInsurerService.addClaimExpert(insurer.clientKey, body);
|
||||||
|
}
|
||||||
|
|
||||||
@ApiQuery({ name: "page", type: Number })
|
@ApiQuery({ name: "page", type: Number })
|
||||||
@ApiQuery({ name: "response_count", type: Number })
|
@ApiQuery({ name: "response_count", type: Number })
|
||||||
@Get("list")
|
@Get("experts/list")
|
||||||
async getAllExperts(
|
async getAllExperts(
|
||||||
@Query("page") page: number,
|
@Query("page") page: number,
|
||||||
@Query("response_count") count: number,
|
@Query("response_count") count: number,
|
||||||
@@ -57,11 +104,30 @@ export class ExpertInsurerController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get("top-experts")
|
@Get("experts/top")
|
||||||
async getTopExperts(@CurrentUser() actor) {
|
async getTopExperts(@CurrentUser() actor) {
|
||||||
return await this.expertInsurerService.getTopExpertsForClient(actor);
|
return await this.expertInsurerService.getTopExpertsForClient(actor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Get("files")
|
||||||
|
async getAllFiles(@CurrentUser() insurer) {
|
||||||
|
return await this.expertInsurerService.retrieveAllFilesOfClient(
|
||||||
|
insurer.clientKey,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get("files/:publicId")
|
||||||
|
@ApiParam({ name: "publicId" })
|
||||||
|
async getFileDetailsByPublicId(
|
||||||
|
@CurrentUser() insurer,
|
||||||
|
@Param("publicId") publicId: string,
|
||||||
|
) {
|
||||||
|
return await this.expertInsurerService.retrieveFileDetailsByPublicId(
|
||||||
|
insurer.clientKey,
|
||||||
|
publicId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@Get("top-files")
|
@Get("top-files")
|
||||||
async getTopFiles(@CurrentUser() insurer) {
|
async getTopFiles(@CurrentUser() insurer) {
|
||||||
return await this.expertInsurerService.getTopFilesForClient(
|
return await this.expertInsurerService.getTopFilesForClient(
|
||||||
@@ -74,6 +140,29 @@ export class ExpertInsurerController {
|
|||||||
return await this.expertInsurerService.getExpertStatisticsReport(actor);
|
return await this.expertInsurerService.getExpertStatisticsReport(actor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Get("report/status-counts")
|
||||||
|
@ApiQuery({
|
||||||
|
name: "from",
|
||||||
|
required: false,
|
||||||
|
description: "Optional start datetime (ISO string)",
|
||||||
|
})
|
||||||
|
@ApiQuery({
|
||||||
|
name: "to",
|
||||||
|
required: false,
|
||||||
|
description: "Optional end datetime (ISO string)",
|
||||||
|
})
|
||||||
|
async getInsurerStatusReport(
|
||||||
|
@CurrentUser() actor,
|
||||||
|
@Query("from") from?: string,
|
||||||
|
@Query("to") to?: string,
|
||||||
|
) {
|
||||||
|
return await this.expertInsurerService.getInsurerFileStatusCounts(
|
||||||
|
actor,
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@ApiParam({ name: "expertId" })
|
@ApiParam({ name: "expertId" })
|
||||||
@ApiQuery({ name: "role", enum: ["claim", "blame"] })
|
@ApiQuery({ name: "role", enum: ["claim", "blame"] })
|
||||||
@Get("/:expertId")
|
@Get("/:expertId")
|
||||||
@@ -174,28 +263,4 @@ export class ExpertInsurerController {
|
|||||||
insurer.clientKey,
|
insurer.clientKey,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get("branches/:insuranceId")
|
|
||||||
@ApiParam({ name: "insuranceId" })
|
|
||||||
async getInsuranceBranches(@Param("insuranceId") insuranceId: string) {
|
|
||||||
return await this.expertInsurerService.retrieveInsuranceBranches(
|
|
||||||
insuranceId,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Post("branches")
|
|
||||||
@ApiBody({ type: CreateBranchDto })
|
|
||||||
async addBranch(
|
|
||||||
@CurrentUser() insurer,
|
|
||||||
@Body() createBranchDto: CreateBranchDto,
|
|
||||||
) {
|
|
||||||
if (!insurer) {
|
|
||||||
throw new UnauthorizedException("Could not identify the current user.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return await this.expertInsurerService.addBranch(
|
|
||||||
insurer.clientKey,
|
|
||||||
createBranchDto,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,12 +19,14 @@ import {
|
|||||||
BranchModel,
|
BranchModel,
|
||||||
BranchSchema,
|
BranchSchema,
|
||||||
} from "src/client/entities/schema/branch.schema";
|
} from "src/client/entities/schema/branch.schema";
|
||||||
|
import { HashModule } from "src/utils/hash/hash.module";
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
ClaimRequestManagementModule,
|
ClaimRequestManagementModule,
|
||||||
RequestManagementModule,
|
RequestManagementModule,
|
||||||
AuthModule,
|
AuthModule,
|
||||||
|
HashModule,
|
||||||
UsersModule,
|
UsersModule,
|
||||||
ClientModule,
|
ClientModule,
|
||||||
MongooseModule.forFeature([
|
MongooseModule.forFeature([
|
||||||
|
|||||||
@@ -9,6 +9,11 @@ import { Types } from "mongoose";
|
|||||||
import { ClaimCaseDbService } from "src/claim-request-management/entites/db-service/claim-case.db.service";
|
import { ClaimCaseDbService } from "src/claim-request-management/entites/db-service/claim-case.db.service";
|
||||||
import { CreateBranchDto } from "src/client/dto/create-branch.dto";
|
import { CreateBranchDto } from "src/client/dto/create-branch.dto";
|
||||||
import { BranchDbService } from "src/client/entities/db-service/branch.db.service";
|
import { BranchDbService } from "src/client/entities/db-service/branch.db.service";
|
||||||
|
import {
|
||||||
|
CreateBlameExpertByInsurerDto,
|
||||||
|
CreateClaimExpertByInsurerDto,
|
||||||
|
CreateInsurerExpertDto,
|
||||||
|
} from "./dto/create-insurer-expert.dto";
|
||||||
import {
|
import {
|
||||||
blameCaseTouchesClient,
|
blameCaseTouchesClient,
|
||||||
claimCaseTouchesClient,
|
claimCaseTouchesClient,
|
||||||
@@ -16,10 +21,14 @@ import {
|
|||||||
import { BlameRequestDbService } from "src/request-management/entities/db-service/blame-request.db.service";
|
import { BlameRequestDbService } from "src/request-management/entities/db-service/blame-request.db.service";
|
||||||
import { FileRating } from "src/request-management/entities/schema/request-management.schema";
|
import { FileRating } from "src/request-management/entities/schema/request-management.schema";
|
||||||
import { CaseStatus } from "src/Types&Enums/blame-request-management/caseStatus.enum";
|
import { CaseStatus } from "src/Types&Enums/blame-request-management/caseStatus.enum";
|
||||||
|
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 { DamageExpertDbService } from "src/users/entities/db-service/damage-expert.db.service";
|
||||||
import { ExpertDbService } from "src/users/entities/db-service/expert.db.service";
|
import { ExpertDbService } from "src/users/entities/db-service/expert.db.service";
|
||||||
import { ExpertFileActivityDbService } from "src/users/entities/db-service/expert-file-activity.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 { ExpertFileActivityType } from "src/users/entities/schema/expert-file-activity.schema";
|
||||||
|
import { HashService } from "src/utils/hash/hash.service";
|
||||||
|
import { RoleEnum } from "src/Types&Enums/role.enum";
|
||||||
|
import { UserType } from "src/Types&Enums/userType.enum";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ExpertInsurerService {
|
export class ExpertInsurerService {
|
||||||
@@ -30,6 +39,7 @@ export class ExpertInsurerService {
|
|||||||
private readonly blameRequestDbService: BlameRequestDbService,
|
private readonly blameRequestDbService: BlameRequestDbService,
|
||||||
private readonly claimCaseDbService: ClaimCaseDbService,
|
private readonly claimCaseDbService: ClaimCaseDbService,
|
||||||
private readonly branchDbService: BranchDbService,
|
private readonly branchDbService: BranchDbService,
|
||||||
|
private readonly hashService: HashService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
private getClientId(actorOrId: any): Types.ObjectId {
|
private getClientId(actorOrId: any): Types.ObjectId {
|
||||||
@@ -124,6 +134,38 @@ export class ExpertInsurerService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private buildPartiesPreview(parties: any[] | undefined) {
|
||||||
|
if (!Array.isArray(parties) || !parties.length) return undefined;
|
||||||
|
return parties.map((p: any) => ({
|
||||||
|
role: p?.role,
|
||||||
|
fullName: p?.person?.fullName,
|
||||||
|
vehicle: {
|
||||||
|
carName: p?.vehicle?.carName ?? p?.vehicle?.name,
|
||||||
|
carModel: p?.vehicle?.carModel ?? p?.vehicle?.model,
|
||||||
|
plate: p?.vehicle?.plate ?? p?.vehicle?.plateId,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
private sanitizeVehicleInquiry(vehicle: any) {
|
||||||
|
if (!vehicle || typeof vehicle !== "object") return vehicle;
|
||||||
|
const inquiry = vehicle.inquiry;
|
||||||
|
if (!inquiry || typeof inquiry !== "object") return vehicle;
|
||||||
|
const { raw, mapped, ...safeInquiry } = inquiry;
|
||||||
|
return {
|
||||||
|
...vehicle,
|
||||||
|
inquiry: safeInquiry,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private sanitizePartyForInsurerView(party: any) {
|
||||||
|
if (!party || typeof party !== "object") return party;
|
||||||
|
return {
|
||||||
|
...party,
|
||||||
|
vehicle: this.sanitizeVehicleInquiry(party.vehicle),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private getCombinedFileScore(file: any): number | null {
|
private getCombinedFileScore(file: any): number | null {
|
||||||
const insurerRating = file?.rating;
|
const insurerRating = file?.rating;
|
||||||
const userRating = file?.userRating;
|
const userRating = file?.userRating;
|
||||||
@@ -155,11 +197,6 @@ export class ExpertInsurerService {
|
|||||||
.filter((f) =>
|
.filter((f) =>
|
||||||
(f?.parties || []).some((p) => String(p?.person?.clientId || "") === idStr),
|
(f?.parties || []).some((p) => String(p?.person?.clientId || "") === idStr),
|
||||||
)
|
)
|
||||||
.filter(
|
|
||||||
(f) =>
|
|
||||||
f?.status !== CaseStatus.OPEN &&
|
|
||||||
f?.status !== CaseStatus.WAITING_FOR_SECOND_PARTY,
|
|
||||||
)
|
|
||||||
.map((f) => this.normalizeBlameCase(f));
|
.map((f) => this.normalizeBlameCase(f));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,7 +204,7 @@ export class ExpertInsurerService {
|
|||||||
const all = (await this.claimCaseDbService.find({}, { lean: true })) as any[];
|
const all = (await this.claimCaseDbService.find({}, { lean: true })) as any[];
|
||||||
const idStr = String(clientObjectId);
|
const idStr = String(clientObjectId);
|
||||||
return all
|
return all
|
||||||
.filter((f) => String(f?.owner?.userClientKey || "") === idStr)
|
.filter((f) => claimCaseTouchesClient(f, idStr))
|
||||||
.map((f) => this.normalizeClaimCase(f));
|
.map((f) => this.normalizeClaimCase(f));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -395,7 +432,245 @@ export class ExpertInsurerService {
|
|||||||
this.getClientBlameFiles(id),
|
this.getClientBlameFiles(id),
|
||||||
this.getClientClaimFiles(id),
|
this.getClientClaimFiles(id),
|
||||||
]);
|
]);
|
||||||
return { blameFiles, claimFiles };
|
|
||||||
|
const byPublicId = new Map<
|
||||||
|
string,
|
||||||
|
{
|
||||||
|
publicId: string;
|
||||||
|
fileType?: string;
|
||||||
|
parties?: Array<{
|
||||||
|
role?: string;
|
||||||
|
fullName?: string;
|
||||||
|
vehicle?: {
|
||||||
|
carName?: string;
|
||||||
|
carModel?: string;
|
||||||
|
plate?: string;
|
||||||
|
};
|
||||||
|
}>;
|
||||||
|
blame?: {
|
||||||
|
requestId?: string;
|
||||||
|
requestNo?: string;
|
||||||
|
status?: string;
|
||||||
|
parties?: Array<{
|
||||||
|
role?: string;
|
||||||
|
fullName?: string;
|
||||||
|
vehicle?: {
|
||||||
|
carName?: string;
|
||||||
|
carModel?: string;
|
||||||
|
plate?: string;
|
||||||
|
};
|
||||||
|
}>;
|
||||||
|
createdAt?: Date | string;
|
||||||
|
updatedAt?: Date | string;
|
||||||
|
};
|
||||||
|
claim?: {
|
||||||
|
requestId?: string;
|
||||||
|
requestNo?: string;
|
||||||
|
status?: string;
|
||||||
|
claimStatus?: string;
|
||||||
|
currentStep?: string;
|
||||||
|
createdAt?: Date | string;
|
||||||
|
updatedAt?: Date | string;
|
||||||
|
};
|
||||||
|
createdAt?: Date | string;
|
||||||
|
updatedAt?: Date | string;
|
||||||
|
}
|
||||||
|
>();
|
||||||
|
|
||||||
|
for (const b of blameFiles) {
|
||||||
|
const key = String((b as any).publicId || (b as any)._id || "");
|
||||||
|
if (!key) continue;
|
||||||
|
const prev = byPublicId.get(key) ?? { publicId: key };
|
||||||
|
const partiesPreview = this.buildPartiesPreview((b as any).parties);
|
||||||
|
prev.fileType = prev.fileType ?? (b as any).type;
|
||||||
|
prev.blame = {
|
||||||
|
requestId: (b as any)?._id?.toString?.() || String((b as any)?._id || ""),
|
||||||
|
requestNo: (b as any).requestNo || String((b as any).requestNumber || ""),
|
||||||
|
status: (b as any).status,
|
||||||
|
parties: partiesPreview,
|
||||||
|
createdAt: (b as any).createdAt,
|
||||||
|
updatedAt: (b as any).updatedAt,
|
||||||
|
};
|
||||||
|
prev.parties = prev.parties ?? partiesPreview;
|
||||||
|
prev.createdAt = prev.createdAt ?? (b as any).createdAt;
|
||||||
|
prev.updatedAt = (b as any).updatedAt ?? prev.updatedAt;
|
||||||
|
byPublicId.set(key, prev);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const c of claimFiles) {
|
||||||
|
const key = String((c as any).publicId || (c as any)._id || "");
|
||||||
|
if (!key) continue;
|
||||||
|
const prev = byPublicId.get(key) ?? { publicId: key };
|
||||||
|
const claimPartiesPreview = this.buildPartiesPreview((c as any)?.snapshot?.parties);
|
||||||
|
prev.fileType = prev.fileType ?? (c as any)?.snapshot?.accident?.type;
|
||||||
|
prev.claim = {
|
||||||
|
requestId: (c as any)?._id?.toString?.() || String((c as any)?._id || ""),
|
||||||
|
requestNo: (c as any).requestNo || String((c as any).requestNumber || ""),
|
||||||
|
status: (c as any).status,
|
||||||
|
claimStatus: (c as any).claimStatus,
|
||||||
|
currentStep: (c as any).currentStep,
|
||||||
|
createdAt: (c as any).createdAt,
|
||||||
|
updatedAt: (c as any).updatedAt,
|
||||||
|
};
|
||||||
|
prev.parties = prev.parties ?? claimPartiesPreview;
|
||||||
|
prev.createdAt = prev.createdAt ?? (c as any).createdAt;
|
||||||
|
prev.updatedAt = (c as any).updatedAt ?? prev.updatedAt;
|
||||||
|
byPublicId.set(key, prev);
|
||||||
|
}
|
||||||
|
|
||||||
|
const files = Array.from(byPublicId.values())
|
||||||
|
.map((f) => ({
|
||||||
|
publicId: f.publicId,
|
||||||
|
fileType: f.fileType,
|
||||||
|
hasClaim: !!f.claim,
|
||||||
|
parties: f.parties,
|
||||||
|
blame: f.blame,
|
||||||
|
claim: f.claim,
|
||||||
|
createdAt: f.createdAt,
|
||||||
|
updatedAt: f.updatedAt,
|
||||||
|
}))
|
||||||
|
.sort((a, b) => {
|
||||||
|
const at = a.updatedAt ? new Date(a.updatedAt).getTime() : 0;
|
||||||
|
const bt = b.updatedAt ? new Date(b.updatedAt).getTime() : 0;
|
||||||
|
return bt - at;
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
total: files.length,
|
||||||
|
files,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async retrieveFileDetailsByPublicId(insurerId: string, publicId: string) {
|
||||||
|
if (!publicId?.trim()) {
|
||||||
|
throw new BadRequestException("publicId is required");
|
||||||
|
}
|
||||||
|
|
||||||
|
const id = this.getClientId(insurerId);
|
||||||
|
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 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;
|
||||||
|
|
||||||
|
const overview = {
|
||||||
|
publicId,
|
||||||
|
requestNo:
|
||||||
|
(blame as any)?.requestNo ||
|
||||||
|
(claim as any)?.requestNo ||
|
||||||
|
(blame as any)?.requestNumber ||
|
||||||
|
(claim as any)?.requestNumber,
|
||||||
|
type: (blame as any)?.type,
|
||||||
|
hasClaim: !!claim,
|
||||||
|
blameStatus: (blame 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,
|
||||||
|
}
|
||||||
|
: 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,
|
||||||
|
}
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
return {
|
||||||
|
overview,
|
||||||
|
blame: blameDetails,
|
||||||
|
claim: claimDetails,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async addBranch(clientKey: string, branchDto: CreateBranchDto) {
|
async addBranch(clientKey: string, branchDto: CreateBranchDto) {
|
||||||
@@ -420,6 +695,112 @@ export class ExpertInsurerService {
|
|||||||
return newBranch;
|
return newBranch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async assertBranchBelongsToClient(
|
||||||
|
branchId: string,
|
||||||
|
clientId: Types.ObjectId,
|
||||||
|
) {
|
||||||
|
const branch = await this.branchDbService.findById(branchId);
|
||||||
|
if (!branch) {
|
||||||
|
throw new NotFoundException("Branch not found");
|
||||||
|
}
|
||||||
|
if (String(branch.clientKey) !== String(clientId)) {
|
||||||
|
throw new ForbiddenException(
|
||||||
|
"Selected branch does not belong to your insurance company.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return branch;
|
||||||
|
}
|
||||||
|
|
||||||
|
private sanitizeExpertResponse(doc: any) {
|
||||||
|
const obj = typeof doc?.toObject === "function" ? doc.toObject() : doc;
|
||||||
|
if (!obj) return obj;
|
||||||
|
const { password, otp, ...rest } = obj;
|
||||||
|
return rest;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async createExpertForInsurer(
|
||||||
|
insurerClientKey: string,
|
||||||
|
payload: CreateInsurerExpertDto,
|
||||||
|
role: RoleEnum.EXPERT | RoleEnum.DAMAGE_EXPERT,
|
||||||
|
) {
|
||||||
|
const clientObjectId = this.getClientId(insurerClientKey);
|
||||||
|
const branch = await this.assertBranchBelongsToClient(
|
||||||
|
payload.branchId,
|
||||||
|
clientObjectId,
|
||||||
|
);
|
||||||
|
|
||||||
|
const email = payload.email.trim().toLowerCase();
|
||||||
|
const existingByEmail = await this.expertDbService.findOne({ email });
|
||||||
|
const existingDamageByEmail = await this.damageExpertDbService.findOne({ email });
|
||||||
|
if (existingByEmail || existingDamageByEmail) {
|
||||||
|
throw new ConflictException("An expert with this email already exists.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const nationalCode = payload.nationalCode.trim();
|
||||||
|
const existingByNational = await this.expertDbService.findOne({ nationalCode });
|
||||||
|
const existingDamageByNational = await this.damageExpertDbService.findOne({
|
||||||
|
nationalCode,
|
||||||
|
});
|
||||||
|
if (existingByNational || existingDamageByNational) {
|
||||||
|
throw new ConflictException(
|
||||||
|
"An expert with this national code already exists.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const hashedPassword = await this.hashService.hash(payload.password);
|
||||||
|
const basePayload: any = {
|
||||||
|
...payload,
|
||||||
|
email,
|
||||||
|
nationalCode,
|
||||||
|
password: hashedPassword,
|
||||||
|
role,
|
||||||
|
userType: payload.userType ?? UserType.LEGAL,
|
||||||
|
clientKey:
|
||||||
|
role === RoleEnum.EXPERT ? String(clientObjectId) : clientObjectId,
|
||||||
|
branchId: new Types.ObjectId(payload.branchId),
|
||||||
|
insuActivityCo: String(clientObjectId),
|
||||||
|
};
|
||||||
|
|
||||||
|
const created =
|
||||||
|
role === RoleEnum.EXPERT
|
||||||
|
? await this.expertDbService.create(basePayload)
|
||||||
|
: await this.damageExpertDbService.create(basePayload);
|
||||||
|
|
||||||
|
return {
|
||||||
|
expert: this.sanitizeExpertResponse(created),
|
||||||
|
branch: {
|
||||||
|
_id: (branch as any)._id,
|
||||||
|
name: branch.name,
|
||||||
|
code: branch.code,
|
||||||
|
city: branch.city,
|
||||||
|
state: branch.state,
|
||||||
|
address: branch.address,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async addBlameExpert(
|
||||||
|
insurerClientKey: string,
|
||||||
|
payload: CreateBlameExpertByInsurerDto,
|
||||||
|
) {
|
||||||
|
return this.createExpertForInsurer(
|
||||||
|
insurerClientKey,
|
||||||
|
payload,
|
||||||
|
RoleEnum.EXPERT,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async addClaimExpert(
|
||||||
|
insurerClientKey: string,
|
||||||
|
payload: CreateClaimExpertByInsurerDto,
|
||||||
|
) {
|
||||||
|
return this.createExpertForInsurer(
|
||||||
|
insurerClientKey,
|
||||||
|
payload,
|
||||||
|
RoleEnum.DAMAGE_EXPERT,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get comprehensive statistics for all experts of a client
|
* Get comprehensive statistics for all experts of a client
|
||||||
* Returns:
|
* Returns:
|
||||||
@@ -549,4 +930,113 @@ export class ExpertInsurerService {
|
|||||||
async retrieveInsuranceBranches(insuranceId: string) {
|
async retrieveInsuranceBranches(insuranceId: string) {
|
||||||
return this.branchDbService.findAll(insuranceId);
|
return this.branchDbService.findAll(insuranceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private parseDateRange(from?: string, to?: string) {
|
||||||
|
const fromDate = from ? new Date(from) : undefined;
|
||||||
|
const toDate = to ? new Date(to) : undefined;
|
||||||
|
|
||||||
|
if (fromDate && Number.isNaN(fromDate.getTime())) {
|
||||||
|
throw new BadRequestException("Invalid 'from' date");
|
||||||
|
}
|
||||||
|
if (toDate && Number.isNaN(toDate.getTime())) {
|
||||||
|
throw new BadRequestException("Invalid 'to' date");
|
||||||
|
}
|
||||||
|
if (fromDate && toDate && fromDate > toDate) {
|
||||||
|
throw new BadRequestException("'from' must be before or equal to 'to'");
|
||||||
|
}
|
||||||
|
return { fromDate, toDate };
|
||||||
|
}
|
||||||
|
|
||||||
|
private isInDateRange(value: any, fromDate?: Date, toDate?: Date): boolean {
|
||||||
|
if (!fromDate && !toDate) return true;
|
||||||
|
if (!value) return false;
|
||||||
|
const d = new Date(value);
|
||||||
|
if (Number.isNaN(d.getTime())) return false;
|
||||||
|
if (fromDate && d < fromDate) return false;
|
||||||
|
if (toDate && d > toDate) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getInsurerFileStatusCounts(
|
||||||
|
actor: any,
|
||||||
|
from?: string,
|
||||||
|
to?: string,
|
||||||
|
): Promise<{
|
||||||
|
all: number;
|
||||||
|
completed: number;
|
||||||
|
under_review: number;
|
||||||
|
waiting_for_documents_resend: number;
|
||||||
|
}> {
|
||||||
|
const clientObjectId = this.getClientId(actor);
|
||||||
|
const { fromDate, toDate } = this.parseDateRange(from, to);
|
||||||
|
|
||||||
|
const [blameFilesRaw, claimFilesRaw] = await Promise.all([
|
||||||
|
this.getClientBlameFiles(clientObjectId),
|
||||||
|
this.getClientClaimFiles(clientObjectId),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const blameFiles = blameFilesRaw.filter((f) =>
|
||||||
|
this.isInDateRange((f as any)?.createdAt, fromDate, toDate),
|
||||||
|
);
|
||||||
|
const claimFiles = claimFilesRaw.filter((f) =>
|
||||||
|
this.isInDateRange((f as any)?.createdAt, fromDate, toDate),
|
||||||
|
);
|
||||||
|
|
||||||
|
const fileMap = new Map<
|
||||||
|
string,
|
||||||
|
{ blame?: any; claim?: any }
|
||||||
|
>();
|
||||||
|
|
||||||
|
for (const b of blameFiles) {
|
||||||
|
const key = String((b as any).publicId || (b as any)._id || "");
|
||||||
|
if (!key) continue;
|
||||||
|
const prev = fileMap.get(key) ?? {};
|
||||||
|
prev.blame = b;
|
||||||
|
fileMap.set(key, prev);
|
||||||
|
}
|
||||||
|
for (const c of claimFiles) {
|
||||||
|
const key = String((c as any).publicId || (c as any)._id || "");
|
||||||
|
if (!key) continue;
|
||||||
|
const prev = fileMap.get(key) ?? {};
|
||||||
|
prev.claim = c;
|
||||||
|
fileMap.set(key, prev);
|
||||||
|
}
|
||||||
|
|
||||||
|
let completed = 0;
|
||||||
|
let underReview = 0;
|
||||||
|
let waitingForDocumentsResend = 0;
|
||||||
|
|
||||||
|
for (const entry of fileMap.values()) {
|
||||||
|
const blameStatus = entry.blame?.status;
|
||||||
|
const claimStatus = entry.claim?.status;
|
||||||
|
|
||||||
|
if (claimStatus === ClaimCaseStatus.COMPLETED) {
|
||||||
|
completed += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const blameUnderReview =
|
||||||
|
blameStatus === CaseStatus.WAITING_FOR_EXPERT;
|
||||||
|
const claimUnderReview =
|
||||||
|
claimStatus === ClaimCaseStatus.WAITING_FOR_DAMAGE_EXPERT ||
|
||||||
|
claimStatus === ClaimCaseStatus.EXPERT_REVIEWING;
|
||||||
|
if (blameUnderReview || claimUnderReview) {
|
||||||
|
underReview += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const blameResend =
|
||||||
|
blameStatus === CaseStatus.WAITING_FOR_DOCUMENT_RESEND;
|
||||||
|
const claimResend =
|
||||||
|
claimStatus === ClaimCaseStatus.WAITING_FOR_USER_RESEND;
|
||||||
|
if (blameResend || claimResend) {
|
||||||
|
waitingForDocumentsResend += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
all: fileMap.size,
|
||||||
|
completed,
|
||||||
|
under_review: underReview,
|
||||||
|
waiting_for_documents_resend: waitingForDocumentsResend,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
export enum PanelModels {
|
|
||||||
BLAME = "blame",
|
|
||||||
CLAIM = "claim",
|
|
||||||
}
|
|
||||||
11
src/main.ts
11
src/main.ts
@@ -32,7 +32,6 @@ async function bootstrap() {
|
|||||||
.setVersion("1.0.0")
|
.setVersion("1.0.0")
|
||||||
.addServer(process.env.URL)
|
.addServer(process.env.URL)
|
||||||
.addServer(process.env.BASE_URL + "/api")
|
.addServer(process.env.BASE_URL + "/api")
|
||||||
.addServer("http://192.168.20.249:9001")
|
|
||||||
.addServer("http://192.168.20.170:9001")
|
.addServer("http://192.168.20.170:9001")
|
||||||
.addServer("http://localhost:9001")
|
.addServer("http://localhost:9001")
|
||||||
.addBearerAuth()
|
.addBearerAuth()
|
||||||
@@ -42,6 +41,16 @@ async function bootstrap() {
|
|||||||
swaggerOptions: {
|
swaggerOptions: {
|
||||||
persistAuthorization: true,
|
persistAuthorization: true,
|
||||||
docExpansion: "none",
|
docExpansion: "none",
|
||||||
|
tagsSorter: (a: string, b: string) => {
|
||||||
|
const priority: Record<string, number> = {
|
||||||
|
user: 0,
|
||||||
|
actor: 1,
|
||||||
|
};
|
||||||
|
const pa = priority[a] ?? 100;
|
||||||
|
const pb = priority[b] ?? 100;
|
||||||
|
if (pa !== pb) return pa - pb;
|
||||||
|
return a.localeCompare(b);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
await app.listen(process.env.PORT);
|
await app.listen(process.env.PORT);
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import {
|
|||||||
Param,
|
Param,
|
||||||
UseGuards,
|
UseGuards,
|
||||||
Get,
|
Get,
|
||||||
Query,
|
|
||||||
UseInterceptors,
|
UseInterceptors,
|
||||||
UploadedFile,
|
UploadedFile,
|
||||||
} from "@nestjs/common";
|
} from "@nestjs/common";
|
||||||
@@ -18,7 +17,6 @@ import {
|
|||||||
ApiBearerAuth,
|
ApiBearerAuth,
|
||||||
ApiBody,
|
ApiBody,
|
||||||
ApiParam,
|
ApiParam,
|
||||||
ApiQuery,
|
|
||||||
ApiTags,
|
ApiTags,
|
||||||
ApiOperation,
|
ApiOperation,
|
||||||
ApiResponse,
|
ApiResponse,
|
||||||
@@ -48,6 +46,7 @@ export class ExpertInitiatedController {
|
|||||||
private readonly claimRequestManagementService: ClaimRequestManagementService,
|
private readonly claimRequestManagementService: ClaimRequestManagementService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Get("my-files")
|
@Get("my-files")
|
||||||
@ApiOperation({
|
@ApiOperation({
|
||||||
summary: "List my expert-initiated files",
|
summary: "List my expert-initiated files",
|
||||||
@@ -61,6 +60,7 @@ export class ExpertInitiatedController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Post("complete-blame-data/:requestId")
|
@Post("complete-blame-data/:requestId")
|
||||||
@ApiOperation({
|
@ApiOperation({
|
||||||
summary: "Submit all blame-needed data",
|
summary: "Submit all blame-needed data",
|
||||||
@@ -86,6 +86,7 @@ export class ExpertInitiatedController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Post("complete-claim-data/:claimRequestId")
|
@Post("complete-claim-data/:claimRequestId")
|
||||||
@ApiOperation({
|
@ApiOperation({
|
||||||
summary: "Submit claim-needed data",
|
summary: "Submit claim-needed data",
|
||||||
@@ -111,6 +112,7 @@ export class ExpertInitiatedController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Post("create")
|
@Post("create")
|
||||||
@ApiOperation({
|
@ApiOperation({
|
||||||
summary: "Create expert-initiated blame file",
|
summary: "Create expert-initiated blame file",
|
||||||
@@ -138,6 +140,7 @@ export class ExpertInitiatedController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Post("complete-form-third-party/:requestId")
|
@Post("complete-form-third-party/:requestId")
|
||||||
@ApiOperation({
|
@ApiOperation({
|
||||||
summary: "Expert completes all information for IN_PERSON THIRD_PARTY file",
|
summary: "Expert completes all information for IN_PERSON THIRD_PARTY file",
|
||||||
@@ -167,6 +170,7 @@ export class ExpertInitiatedController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Post("complete-form-car-body/:requestId")
|
@Post("complete-form-car-body/:requestId")
|
||||||
@ApiOperation({
|
@ApiOperation({
|
||||||
summary: "Expert completes all information for IN_PERSON CAR_BODY file",
|
summary: "Expert completes all information for IN_PERSON CAR_BODY file",
|
||||||
@@ -196,6 +200,7 @@ export class ExpertInitiatedController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Post("upload-video/:requestId")
|
@Post("upload-video/:requestId")
|
||||||
@ApiOperation({
|
@ApiOperation({
|
||||||
summary: "Expert uploads video for expert-initiated file",
|
summary: "Expert uploads video for expert-initiated file",
|
||||||
@@ -252,6 +257,7 @@ export class ExpertInitiatedController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Post("upload-voice/:requestId")
|
@Post("upload-voice/:requestId")
|
||||||
@ApiOperation({
|
@ApiOperation({
|
||||||
summary: "Expert uploads voice for expert-initiated file",
|
summary: "Expert uploads voice for expert-initiated file",
|
||||||
@@ -309,6 +315,7 @@ export class ExpertInitiatedController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Post("add-accident-fields/:requestId")
|
@Post("add-accident-fields/:requestId")
|
||||||
@ApiOperation({
|
@ApiOperation({
|
||||||
summary: "Expert adds accident fields to expert-initiated file",
|
summary: "Expert adds accident fields to expert-initiated file",
|
||||||
|
|||||||
@@ -11,8 +11,6 @@ import {
|
|||||||
Req,
|
Req,
|
||||||
Put,
|
Put,
|
||||||
UploadedFiles,
|
UploadedFiles,
|
||||||
Patch,
|
|
||||||
Query,
|
|
||||||
} from "@nestjs/common";
|
} from "@nestjs/common";
|
||||||
import {
|
import {
|
||||||
FileFieldsInterceptor,
|
FileFieldsInterceptor,
|
||||||
@@ -23,8 +21,8 @@ import {
|
|||||||
ApiBody,
|
ApiBody,
|
||||||
ApiConsumes,
|
ApiConsumes,
|
||||||
ApiParam,
|
ApiParam,
|
||||||
ApiQuery,
|
|
||||||
ApiTags,
|
ApiTags,
|
||||||
|
ApiOperation,
|
||||||
} from "@nestjs/swagger";
|
} from "@nestjs/swagger";
|
||||||
import { Request } from "express";
|
import { Request } from "express";
|
||||||
import { diskStorage } from "multer";
|
import { diskStorage } from "multer";
|
||||||
@@ -44,7 +42,6 @@ import {
|
|||||||
LocationDto,
|
LocationDto,
|
||||||
} from "./dto/create-request-management.dto";
|
} from "./dto/create-request-management.dto";
|
||||||
import { RequestManagementService } from "./request-management.service";
|
import { RequestManagementService } from "./request-management.service";
|
||||||
import { InPersonVisitDto } from "src/claim-request-management/dto/in-person-visit.dto";
|
|
||||||
|
|
||||||
@Controller("blame-request-management")
|
@Controller("blame-request-management")
|
||||||
@ApiTags("blame-request-management")
|
@ApiTags("blame-request-management")
|
||||||
@@ -56,6 +53,7 @@ export class RequestManagementController {
|
|||||||
private readonly requestManagementService: RequestManagementService,
|
private readonly requestManagementService: RequestManagementService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Post()
|
@Post()
|
||||||
@UseGuards(GlobalGuard)
|
@UseGuards(GlobalGuard)
|
||||||
async createRequest(
|
async createRequest(
|
||||||
@@ -68,6 +66,7 @@ export class RequestManagementController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Post("/initial-form/:requestId")
|
@Post("/initial-form/:requestId")
|
||||||
@ApiParam({ name: "requestId" })
|
@ApiParam({ name: "requestId" })
|
||||||
@ApiBody({ type: InitialFormDto })
|
@ApiBody({ type: InitialFormDto })
|
||||||
@@ -115,6 +114,7 @@ export class RequestManagementController {
|
|||||||
)
|
)
|
||||||
@UseGuards(GlobalGuard)
|
@UseGuards(GlobalGuard)
|
||||||
@ApiParam({ name: "requestId" })
|
@ApiParam({ name: "requestId" })
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Post("upload-video/:requestId")
|
@Post("upload-video/:requestId")
|
||||||
@UseGuards(GlobalGuard)
|
@UseGuards(GlobalGuard)
|
||||||
async videoUploader(
|
async videoUploader(
|
||||||
@@ -129,6 +129,7 @@ export class RequestManagementController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Post("/add-plate/:requestId")
|
@Post("/add-plate/:requestId")
|
||||||
@ApiParam({ name: "requestId" })
|
@ApiParam({ name: "requestId" })
|
||||||
@ApiBody({ type: AddPlateDto })
|
@ApiBody({ type: AddPlateDto })
|
||||||
@@ -146,6 +147,7 @@ export class RequestManagementController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Post("/carbody-form/:requestId")
|
@Post("/carbody-form/:requestId")
|
||||||
@ApiParam({ name: "requestId" })
|
@ApiParam({ name: "requestId" })
|
||||||
@ApiBody({ type: CarBodyFormDto })
|
@ApiBody({ type: CarBodyFormDto })
|
||||||
@@ -163,6 +165,7 @@ export class RequestManagementController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Post("/carbody-secondform/:requestId")
|
@Post("/carbody-secondform/:requestId")
|
||||||
@ApiParam({ name: "requestId" })
|
@ApiParam({ name: "requestId" })
|
||||||
@ApiBody({ type: CarBodySecondForm })
|
@ApiBody({ type: CarBodySecondForm })
|
||||||
@@ -180,6 +183,7 @@ export class RequestManagementController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Post("/add-detail-location/:requestId")
|
@Post("/add-detail-location/:requestId")
|
||||||
@ApiParam({ name: "requestId" })
|
@ApiParam({ name: "requestId" })
|
||||||
@ApiBody({ type: LocationDto })
|
@ApiBody({ type: LocationDto })
|
||||||
@@ -228,6 +232,7 @@ export class RequestManagementController {
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@ApiParam({ name: "requestId" })
|
@ApiParam({ name: "requestId" })
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Post("upload-voice/:requestId")
|
@Post("upload-voice/:requestId")
|
||||||
@UseGuards(GlobalGuard)
|
@UseGuards(GlobalGuard)
|
||||||
async voiceUploader(
|
async voiceUploader(
|
||||||
@@ -242,6 +247,7 @@ export class RequestManagementController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Post("/add-detail-description/:requestId")
|
@Post("/add-detail-description/:requestId")
|
||||||
@ApiParam({ name: "requestId" })
|
@ApiParam({ name: "requestId" })
|
||||||
@ApiBody({ type: DescriptionDto })
|
@ApiBody({ type: DescriptionDto })
|
||||||
@@ -259,6 +265,7 @@ export class RequestManagementController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Post("add-second-party-details/:phoneNumber/:requestId/:frontendRoutes")
|
@Post("add-second-party-details/:phoneNumber/:requestId/:frontendRoutes")
|
||||||
@ApiParam({ name: "phoneNumber" })
|
@ApiParam({ name: "phoneNumber" })
|
||||||
@ApiParam({ name: "requestId" })
|
@ApiParam({ name: "requestId" })
|
||||||
@@ -280,17 +287,20 @@ export class RequestManagementController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Get("requests")
|
@Get("requests")
|
||||||
async requests(@CurrentUser() user) {
|
async requests(@CurrentUser() user) {
|
||||||
return await this.requestManagementService.myRequests(user.username);
|
return await this.requestManagementService.myRequests(user.username);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Get("creatable-claims")
|
@Get("creatable-claims")
|
||||||
@UseGuards(GlobalGuard)
|
@UseGuards(GlobalGuard)
|
||||||
async getCreatableClaims(@CurrentUser() user) {
|
async getCreatableClaims(@CurrentUser() user) {
|
||||||
return await this.requestManagementService.getCreatableClaims(user);
|
return await this.requestManagementService.getCreatableClaims(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Get("request/:requestId")
|
@Get("request/:requestId")
|
||||||
@ApiParam({ name: "requestId" })
|
@ApiParam({ name: "requestId" })
|
||||||
@Roles(RoleEnum.USER)
|
@Roles(RoleEnum.USER)
|
||||||
@@ -329,6 +339,7 @@ export class RequestManagementController {
|
|||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Put("request/reply/:requestId")
|
@Put("request/reply/:requestId")
|
||||||
@ApiParam({ name: "requestId" })
|
@ApiParam({ name: "requestId" })
|
||||||
@UseGuards(GlobalGuard)
|
@UseGuards(GlobalGuard)
|
||||||
@@ -385,6 +396,7 @@ export class RequestManagementController {
|
|||||||
)
|
)
|
||||||
@UseGuards(GlobalGuard)
|
@UseGuards(GlobalGuard)
|
||||||
@ApiParam({ name: "requestId" })
|
@ApiParam({ name: "requestId" })
|
||||||
|
@ApiOperation({ deprecated: true })
|
||||||
@Put("request/resend/:requestId")
|
@Put("request/resend/:requestId")
|
||||||
userResendReply(
|
userResendReply(
|
||||||
@Body("description") description: string,
|
@Body("description") description: string,
|
||||||
|
|||||||
@@ -6408,26 +6408,38 @@ export class RequestManagementService {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (updatedRequest.type === BlameRequestType.THIRD_PARTY) {
|
if (
|
||||||
const guiltyPartyId = updatedRequest.expert?.decision?.guiltyPartyId
|
updatedRequest.type === BlameRequestType.THIRD_PARTY ||
|
||||||
? String(updatedRequest.expert.decision.guiltyPartyId)
|
updatedRequest.type === BlameRequestType.CAR_BODY
|
||||||
: null;
|
) {
|
||||||
if (guiltyPartyId) {
|
let targetPhone: string | undefined;
|
||||||
const damagedParty = updatedRequest.parties?.find(
|
|
||||||
(p) => p.person?.userId && String(p.person.userId) !== guiltyPartyId,
|
if (updatedRequest.type === BlameRequestType.THIRD_PARTY) {
|
||||||
);
|
const guiltyPartyId = updatedRequest.expert?.decision?.guiltyPartyId
|
||||||
const phone = damagedParty?.person?.phoneNumber?.trim();
|
? String(updatedRequest.expert.decision.guiltyPartyId)
|
||||||
if (phone) {
|
: null;
|
||||||
const publicId = String(updatedRequest.publicId);
|
if (guiltyPartyId) {
|
||||||
const link = this.smsOrchestrationService.buildClaimLink(requestId);
|
const damagedParty = updatedRequest.parties?.find(
|
||||||
await this.smsOrchestrationService.sendThirdPartyDamagedPartyClaimLinkNotice(
|
(p) => p.person?.userId && String(p.person.userId) !== guiltyPartyId,
|
||||||
{
|
|
||||||
receptor: phone,
|
|
||||||
publicId,
|
|
||||||
link,
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
targetPhone = damagedParty?.person?.phoneNumber?.trim();
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// CAR_BODY has a single owner/damaged party: notify that user too.
|
||||||
|
const ownerParty = updatedRequest.parties?.[partyIndex] ?? updatedRequest.parties?.[0];
|
||||||
|
targetPhone = ownerParty?.person?.phoneNumber?.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetPhone) {
|
||||||
|
const publicId = String(updatedRequest.publicId);
|
||||||
|
const link = this.smsOrchestrationService.buildClaimLink(requestId);
|
||||||
|
await this.smsOrchestrationService.sendThirdPartyDamagedPartyClaimLinkNotice(
|
||||||
|
{
|
||||||
|
receptor: targetPhone,
|
||||||
|
publicId,
|
||||||
|
link,
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { extname } from "node:path";
|
import { extname } from "node:path";
|
||||||
|
import { mkdirSync } from "node:fs";
|
||||||
import {
|
import {
|
||||||
Body,
|
Body,
|
||||||
Controller,
|
Controller,
|
||||||
@@ -414,7 +415,9 @@ export class RequestManagementV2Controller {
|
|||||||
{
|
{
|
||||||
storage: diskStorage({
|
storage: diskStorage({
|
||||||
destination: (req, file, cb) => {
|
destination: (req, file, cb) => {
|
||||||
cb(null, "./uploads");
|
const uploadDir = "./files/blame-resend";
|
||||||
|
mkdirSync(uploadDir, { recursive: true });
|
||||||
|
cb(null, uploadDir);
|
||||||
},
|
},
|
||||||
filename: (req, file, cb) => {
|
filename: (req, file, cb) => {
|
||||||
const uniqueSuffix =
|
const uniqueSuffix =
|
||||||
|
|||||||
@@ -123,6 +123,9 @@ export class DamageExpertModel {
|
|||||||
@Prop({ type: Types.ObjectId })
|
@Prop({ type: Types.ObjectId })
|
||||||
clientKey?: Types.ObjectId;
|
clientKey?: Types.ObjectId;
|
||||||
|
|
||||||
|
@Prop({ type: Types.ObjectId, index: true })
|
||||||
|
branchId?: Types.ObjectId;
|
||||||
|
|
||||||
@Prop({ type: PersonalInfoSchema })
|
@Prop({ type: PersonalInfoSchema })
|
||||||
personalInfo?: PersonalInfo;
|
personalInfo?: PersonalInfo;
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ export class ExpertFileActivity {
|
|||||||
@Prop({ type: Date, default: Date.now, index: true })
|
@Prop({ type: Date, default: Date.now, index: true })
|
||||||
occurredAt: Date;
|
occurredAt: Date;
|
||||||
|
|
||||||
@Prop({ type: String, required: false, index: true })
|
@Prop({ type: String, required: false })
|
||||||
idempotencyKey?: string;
|
idempotencyKey?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose";
|
import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose";
|
||||||
|
import { Types } from "mongoose";
|
||||||
import { Degrees } from "src/Types&Enums/degrees.enum";
|
import { Degrees } from "src/Types&Enums/degrees.enum";
|
||||||
import { RoleEnum } from "src/Types&Enums/role.enum";
|
import { RoleEnum } from "src/Types&Enums/role.enum";
|
||||||
import { UserType } from "src/Types&Enums/userType.enum";
|
import { UserType } from "src/Types&Enums/userType.enum";
|
||||||
@@ -65,6 +66,9 @@ export class ExpertModel {
|
|||||||
@Prop({})
|
@Prop({})
|
||||||
clientKey?: string;
|
clientKey?: string;
|
||||||
|
|
||||||
|
@Prop({ type: Types.ObjectId, index: true })
|
||||||
|
branchId?: Types.ObjectId;
|
||||||
|
|
||||||
@Prop({ type: "string" })
|
@Prop({ type: "string" })
|
||||||
otp: string;
|
otp: string;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user