forked from Yara724/api
update the expert claim dto
This commit is contained in:
@@ -8,6 +8,8 @@ import {
|
||||
ValidateNested,
|
||||
} from 'class-validator';
|
||||
import { Type } from 'class-transformer';
|
||||
import { ClaimRequiredDocumentType } from 'src/Types&Enums/claim-request-management/required-document-type.enum';
|
||||
import { DamagedPartItem } from 'src/claim-request-management/dto/capture-requirements-v2.dto';
|
||||
|
||||
export class CarPartDamageV2Dto {
|
||||
@ApiProperty({ example: 'left' })
|
||||
@@ -68,3 +70,14 @@ export class SubmitExpertReplyV2Dto {
|
||||
@Type(() => PartPricingV2Dto)
|
||||
parts: PartPricingV2Dto[];
|
||||
}
|
||||
|
||||
export class ClaimSubmitResendV2Dto {
|
||||
@ApiProperty({ required: true })
|
||||
resendDescription: string;
|
||||
|
||||
@ApiProperty({ required: true, examples: ClaimRequiredDocumentType })
|
||||
resendDocuments: ClaimRequiredDocumentType[];
|
||||
|
||||
@ApiProperty({ required: true, type: [DamagedPartItem] })
|
||||
resendCarParts: DamagedPartItem[];
|
||||
}
|
||||
|
||||
@@ -42,6 +42,8 @@ import { ClaimRequiredDocumentDbService } from "src/claim-request-management/ent
|
||||
import { FactorStatus } from "src/Types&Enums/claim-request-management/factor-status.enum";
|
||||
import { DaghiOption } from "src/Types&Enums/claim-request-management/daghi-option.enum";
|
||||
import { BranchDbService } from "src/client/entities/db-service/branch.db.service";
|
||||
import { ClaimSubmitResendV2Dto } from "./dto/expert-claim-v2.dto";
|
||||
import { BlameRequestDbService } from "src/request-management/entities/db-service/blame-request.db.service";
|
||||
|
||||
@Injectable()
|
||||
export class ExpertClaimService {
|
||||
@@ -154,13 +156,14 @@ export class ExpertClaimService {
|
||||
private readonly claimVideoCaptureDbService: VideoCaptureDbService,
|
||||
private readonly httpService: HttpService,
|
||||
private readonly claimCaseDbService: ClaimCaseDbService,
|
||||
private readonly blameRequestDbService: BlameRequestDbService,
|
||||
private readonly sandHubService: SandHubService,
|
||||
private readonly damageExpertDbService: DamageExpertDbService,
|
||||
private readonly clientDbService: ClientDbService,
|
||||
private readonly claimFactorsImageDbService: ClaimFactorsImageDbService,
|
||||
private readonly claimRequiredDocumentDbService: ClaimRequiredDocumentDbService,
|
||||
private readonly branchDbService: BranchDbService,
|
||||
) {}
|
||||
) { }
|
||||
|
||||
private isVisibleToClientType(client: any, actor: any): boolean {
|
||||
if (actor.userType === UserType.GENUINE) {
|
||||
@@ -474,7 +477,7 @@ export class ExpertClaimService {
|
||||
|
||||
async calculatePriceDrop(request: any, carPartsAi: any, query?: any) {
|
||||
const requestId = request?._id?.toString() || 'unknown';
|
||||
|
||||
|
||||
try {
|
||||
// Step 1: Check SandHub data
|
||||
if (!request?.blameFile?.sanHubId) {
|
||||
@@ -532,7 +535,7 @@ export class ExpertClaimService {
|
||||
} else if (severityValues.length > 0) {
|
||||
// Only fetch car prices if we have severity values to calculate
|
||||
const carPrices = await this.fetchCarPrices();
|
||||
|
||||
|
||||
if (!carPrices || carPrices.length === 0) {
|
||||
this.logger.error(
|
||||
`[PriceDrop] Request ${requestId}: Failed to fetch car prices or car prices list is empty. Cannot find matching car.`,
|
||||
@@ -542,9 +545,9 @@ export class ExpertClaimService {
|
||||
`[PriceDrop] Request ${requestId}: Fetched ${carPrices.length} car prices. Searching for: "${request.carDetail.carName}"`,
|
||||
);
|
||||
|
||||
const closestCar = this.findClosestCar(
|
||||
request.carDetail.carName,
|
||||
carPrices,
|
||||
const closestCar = this.findClosestCar(
|
||||
request.carDetail.carName,
|
||||
carPrices,
|
||||
);
|
||||
|
||||
if (!closestCar) {
|
||||
@@ -558,11 +561,11 @@ export class ExpertClaimService {
|
||||
} else {
|
||||
this.logger.debug(
|
||||
`[PriceDrop] Request ${requestId}: Found matching car "${closestCar.carName}" with marketPrice: ${closestCar.marketPrice}`,
|
||||
);
|
||||
autoCalculatedData = {
|
||||
);
|
||||
autoCalculatedData = {
|
||||
carPrice: closestCar.marketPrice,
|
||||
carValue: severityValues,
|
||||
};
|
||||
carValue: severityValues,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -600,10 +603,10 @@ export class ExpertClaimService {
|
||||
}
|
||||
|
||||
// Step 6: Save to database
|
||||
await this.claimRequestManagementDbService.findOneAndUpdate(
|
||||
{ _id: new Types.ObjectId(request._id) },
|
||||
{ $set: { priceDrop: finalPriceDropData } },
|
||||
);
|
||||
await this.claimRequestManagementDbService.findOneAndUpdate(
|
||||
{ _id: new Types.ObjectId(request._id) },
|
||||
{ $set: { priceDrop: finalPriceDropData } },
|
||||
);
|
||||
|
||||
this.logger.log(
|
||||
`[PriceDrop] Request ${requestId}: Price drop calculation completed and saved.`,
|
||||
@@ -671,7 +674,7 @@ export class ExpertClaimService {
|
||||
|
||||
const severityValue =
|
||||
this.priceDropPart[originalPriceDropKey]?.[
|
||||
damagedPartInfo.severity
|
||||
damagedPartInfo.severity
|
||||
];
|
||||
|
||||
if (severityValue !== undefined) {
|
||||
@@ -968,13 +971,13 @@ export class ExpertClaimService {
|
||||
|
||||
private isCurrentUserAllowed(request: any, currentUser: any): boolean {
|
||||
// Check if locked by current user and lock is still active
|
||||
const isLockedByCurrentUser =
|
||||
const isLockedByCurrentUser =
|
||||
String(request?.actorLocked?.actorId) === currentUser.sub &&
|
||||
request.lockFile;
|
||||
|
||||
|
||||
if (!isLockedByCurrentUser) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Also check if lock has expired (unlockTime has passed)
|
||||
if (request.unlockTime) {
|
||||
@@ -986,7 +989,7 @@ export class ExpertClaimService {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -994,7 +997,7 @@ export class ExpertClaimService {
|
||||
if (!request.lockFile || request.claimStatus !== ReqClaimStatus.ReviewRequest) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Check if lock has expired
|
||||
if (request.unlockTime) {
|
||||
const unlockTime = new Date(request.unlockTime).getTime();
|
||||
@@ -1004,15 +1007,15 @@ export class ExpertClaimService {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// If currentUser is provided, allow access if they are the one who locked it
|
||||
if (currentUser) {
|
||||
const isLockedByCurrentUser =
|
||||
const isLockedByCurrentUser =
|
||||
String(request?.actorLocked?.actorId) === currentUser.sub;
|
||||
// Return false (not locked) if locked by current user, true if locked by someone else
|
||||
return !isLockedByCurrentUser;
|
||||
}
|
||||
|
||||
|
||||
// If no currentUser provided, treat as locked
|
||||
return true;
|
||||
}
|
||||
@@ -1035,18 +1038,18 @@ export class ExpertClaimService {
|
||||
) {
|
||||
throw new ForbiddenException("Access denied to this request");
|
||||
}
|
||||
|
||||
|
||||
// Check if lock has expired (unlockTime has passed)
|
||||
if (request?.unlockTime && !request?.objection) {
|
||||
const unlockTime = new Date(request.unlockTime).getTime();
|
||||
const now = Date.now();
|
||||
if (now >= unlockTime) {
|
||||
throw new ForbiddenException("Your time has expired");
|
||||
}
|
||||
throw new ForbiddenException("Your time has expired");
|
||||
}
|
||||
} else if (request?.unlockTime == null && !request?.objection) {
|
||||
throw new ForbiddenException("Your time has expired");
|
||||
}
|
||||
|
||||
|
||||
if (!request.lockFile && !request?.objection) {
|
||||
throw new ForbiddenException(
|
||||
"For submit reply you must lock the request",
|
||||
@@ -1255,6 +1258,66 @@ export class ExpertClaimService {
|
||||
return request;
|
||||
}
|
||||
|
||||
async streamServiceV2(requestId, query, res, header) {
|
||||
const request = await this.claimCaseDbService.findById(requestId);
|
||||
console.log(request)
|
||||
// const blameCase = await this.blameCaseDbService
|
||||
return request;
|
||||
// let videoPath: string = "";
|
||||
// switch (query) {
|
||||
// case "accident":
|
||||
// videoPath = await this.getAccidentVideoPath(
|
||||
// String(
|
||||
// request?.blameFile?.firstPartyDetails?.firstPartyFile
|
||||
// ?.firstPartyVideoId,
|
||||
// ),
|
||||
// );
|
||||
// break;
|
||||
// case "car-capture":
|
||||
// videoPath = await this.getCarCapture(requestId);
|
||||
// break;
|
||||
// default:
|
||||
// throw new NotFoundException("Invalid query type");
|
||||
// }
|
||||
// if (!videoPath) throw new NotFoundException("video_not_found");
|
||||
|
||||
// const absolutePath = join(process.cwd(), videoPath.replace(/^\/+/, ""));
|
||||
|
||||
// if (!existsSync(absolutePath)) {
|
||||
// throw new NotFoundException(
|
||||
// `Video file not found at path: ${absolutePath}`,
|
||||
// );
|
||||
// }
|
||||
|
||||
// const { size } = statSync(absolutePath);
|
||||
// const range = header.range;
|
||||
|
||||
// if (range) {
|
||||
// const [startStr, endStr] = range.replace(/bytes=/, "").split("-");
|
||||
// const start = parseInt(startStr, 10);
|
||||
// const end = endStr ? parseInt(endStr, 10) : size - 1;
|
||||
// const chunkSize = end - start + 1;
|
||||
|
||||
// const file = createReadStream(absolutePath, { start, end });
|
||||
|
||||
// res.writeHead(206, {
|
||||
// "Content-Range": `bytes ${start}-${end}/${size}`,
|
||||
// "Accept-Ranges": "bytes",
|
||||
// "Content-Length": chunkSize,
|
||||
// "Content-Type": "video/mp4",
|
||||
// });
|
||||
|
||||
// file.pipe(res);
|
||||
// } else {
|
||||
// res.writeHead(200, {
|
||||
// "Content-Length": size,
|
||||
// "Content-Type": "video/mp4",
|
||||
// });
|
||||
|
||||
// createReadStream(absolutePath).pipe(res);
|
||||
// }
|
||||
}
|
||||
|
||||
async streamService(requestId, query, res, header) {
|
||||
const request =
|
||||
await this.claimRequestManagementDbService.findOne(requestId);
|
||||
@@ -1635,6 +1698,18 @@ export class ExpertClaimService {
|
||||
};
|
||||
}
|
||||
|
||||
async submitResendDocsV2(
|
||||
claimRequestId: string,
|
||||
reply: ClaimSubmitResendV2Dto,
|
||||
actor: any,
|
||||
) {
|
||||
try {
|
||||
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* V2: Submit damage expert reply for a claim.
|
||||
*
|
||||
@@ -1839,16 +1914,16 @@ export class ExpertClaimService {
|
||||
locked: c.workflow?.locked || false,
|
||||
lockedBy: c.workflow?.lockedBy
|
||||
? {
|
||||
actorId: c.workflow.lockedBy.actorId?.toString(),
|
||||
actorName: c.workflow.lockedBy.actorName,
|
||||
}
|
||||
actorId: c.workflow.lockedBy.actorId?.toString(),
|
||||
actorName: c.workflow.lockedBy.actorName,
|
||||
}
|
||||
: undefined,
|
||||
vehicle: c.vehicle
|
||||
? {
|
||||
carName: c.vehicle.carName,
|
||||
carModel: c.vehicle.carModel,
|
||||
carType: c.vehicle.carType,
|
||||
}
|
||||
carName: c.vehicle.carName,
|
||||
carModel: c.vehicle.carModel,
|
||||
carType: c.vehicle.carType,
|
||||
}
|
||||
: undefined,
|
||||
createdAt: c.createdAt,
|
||||
})) as ClaimListItemV2Dto[];
|
||||
@@ -1944,16 +2019,16 @@ export class ExpertClaimService {
|
||||
locked: claim.workflow?.locked || false,
|
||||
lockedBy: claim.workflow?.lockedBy
|
||||
? {
|
||||
actorId: claim.workflow.lockedBy.actorId?.toString(),
|
||||
actorName: claim.workflow.lockedBy.actorName,
|
||||
lockedAt: (claim.workflow as any).lockedAt?.toISOString(),
|
||||
}
|
||||
actorId: claim.workflow.lockedBy.actorId?.toString(),
|
||||
actorName: claim.workflow.lockedBy.actorName,
|
||||
lockedAt: (claim.workflow as any).lockedAt?.toISOString(),
|
||||
}
|
||||
: undefined,
|
||||
owner: claim.owner
|
||||
? {
|
||||
userId: claim.owner.userId?.toString(),
|
||||
fullName: claim.owner.fullName,
|
||||
}
|
||||
userId: claim.owner.userId?.toString(),
|
||||
fullName: claim.owner.fullName,
|
||||
}
|
||||
: undefined,
|
||||
vehicle: claim.vehicle,
|
||||
blameRequestId: claim.blameRequestId?.toString(),
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { Body, Controller, Get, Param, Patch, Put, UseGuards } from "@nestjs/common";
|
||||
import { Body, Controller, Get, Header, Param, Headers, Patch, Put, UseGuards, Query, Res } from "@nestjs/common";
|
||||
import {
|
||||
ApiBearerAuth,
|
||||
ApiBody,
|
||||
ApiOperation,
|
||||
ApiParam,
|
||||
ApiPropertyOptional,
|
||||
ApiQuery,
|
||||
ApiTags,
|
||||
} from "@nestjs/swagger";
|
||||
import { IsOptional, IsString } from "class-validator";
|
||||
@@ -14,7 +15,7 @@ import { Roles } from "src/decorators/roles.decorator";
|
||||
import { CurrentUser } from "src/decorators/user.decorator";
|
||||
import { RoleEnum } from "src/Types&Enums/role.enum";
|
||||
import { ExpertClaimService } from "./expert-claim.service";
|
||||
import { SubmitExpertReplyV2Dto } from "./dto/expert-claim-v2.dto";
|
||||
import { ClaimSubmitResendV2Dto, SubmitExpertReplyV2Dto } from "./dto/expert-claim-v2.dto";
|
||||
import { UpdateClaimDamagedPartsV2Dto } from "./dto/update-claim-damaged-parts-v2.dto";
|
||||
|
||||
class InPersonVisitV2Dto {
|
||||
@@ -30,7 +31,7 @@ class InPersonVisitV2Dto {
|
||||
@UseGuards(LocalActorAuthGuard, RolesGuard)
|
||||
@Roles(RoleEnum.DAMAGE_EXPERT)
|
||||
export class ExpertClaimV2Controller {
|
||||
constructor(private readonly expertClaimService: ExpertClaimService) {}
|
||||
constructor(private readonly expertClaimService: ExpertClaimService) { }
|
||||
|
||||
@Get("requests")
|
||||
@ApiOperation({
|
||||
@@ -89,6 +90,22 @@ export class ExpertClaimV2Controller {
|
||||
return await this.expertClaimService.submitExpertReplyV2(claimRequestId, body, actor);
|
||||
}
|
||||
|
||||
@Put("reply/resend/:claimRequestId")
|
||||
@ApiOperation({
|
||||
summary: "Submit expert damage resend request. ",
|
||||
description:
|
||||
"Claim must be locked by this expert (status EXPERT_REVIEWING). Submitting unlocks the claim. If any part has factorNeeded=true the claim moves to EXPERT_COST_EVALUATION; otherwise moves to INSURER_REVIEW. Total payment cap is 30,000,000.",
|
||||
})
|
||||
@ApiParam({ name: "claimRequestId" })
|
||||
@ApiBody({ type: ClaimSubmitResendV2Dto })
|
||||
async submitExpertResendV2(
|
||||
@Param("claimRequestId") claimRequestId: string,
|
||||
@Body() body: ClaimSubmitResendV2Dto,
|
||||
@CurrentUser() actor,
|
||||
) {
|
||||
return await this.expertClaimService.submitResendDocsV2(claimRequestId, body, actor);
|
||||
}
|
||||
|
||||
@Patch(":claimRequestId/visit")
|
||||
@ApiOperation({
|
||||
summary: "Request in-person visit for claim",
|
||||
@@ -128,4 +145,21 @@ export class ExpertClaimV2Controller {
|
||||
actor,
|
||||
);
|
||||
}
|
||||
|
||||
@ApiQuery({
|
||||
name: "query",
|
||||
enum: ["car-capture", "accident"],
|
||||
required: true,
|
||||
})
|
||||
@Get("stream/:id/video")
|
||||
@Header("Accept-Ranges", "bytes")
|
||||
@Header("Content-Type", "video/mp4")
|
||||
async claimStream(
|
||||
@Headers() headers,
|
||||
@Param("id") id: string,
|
||||
@Query("query") query: "car-capture" | "accident",
|
||||
@Res() res,
|
||||
) {
|
||||
return this.expertClaimService.streamServiceV2(id, query, res, headers);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user