forked from Yara724/api
Merge pull request 'YARA-1115, YARA-1117, YARA-1119' (#188) from s.yahyaee/yara724-api:main into main
Reviewed-on: Yara724/api#188
This commit is contained in:
@@ -161,6 +161,7 @@ import {
|
||||
getClaimCaptureProgress,
|
||||
isCapturePhaseDamagedPartyDocKey,
|
||||
isClaimCaptureStepComplete,
|
||||
OPTIONAL_CAPTURE_PHASE_DOC_KEYS_V4V5,
|
||||
} from "src/helpers/claim-capture-phase";
|
||||
import { HttpService } from "@nestjs/axios";
|
||||
import { firstValueFrom } from "rxjs";
|
||||
@@ -8138,7 +8139,7 @@ export class ClaimRequestManagementService {
|
||||
file: Express.Multer.File,
|
||||
currentUserId: string,
|
||||
actor?: { sub: string; role?: string },
|
||||
options?: { v3InPersonFlow?: boolean },
|
||||
options?: { v3InPersonFlow?: boolean; skipMetalPlate?: boolean },
|
||||
): Promise<UploadRequiredDocumentV2ResponseDto> {
|
||||
try {
|
||||
const claimCase = await this.claimCaseDbService.findById(claimRequestId);
|
||||
@@ -8315,12 +8316,16 @@ export class ClaimRequestManagementService {
|
||||
k === body.documentKey ||
|
||||
this.isRequiredDocumentUploadedOnClaim(claimCase, k);
|
||||
remaining = CAPTURE_PHASE_DAMAGED_PARTY_DOC_KEYS.filter(
|
||||
(k) => !afterThis(k),
|
||||
(k) => {
|
||||
if (options?.skipMetalPlate && OPTIONAL_CAPTURE_PHASE_DOC_KEYS_V4V5.includes(k as any)) return false;
|
||||
return !afterThis(k);
|
||||
},
|
||||
).length;
|
||||
|
||||
if (remaining === 0) {
|
||||
const progressAfterDoc = getClaimCaptureProgress(claimCase, {
|
||||
assumeCapturePhaseDocKey: body.documentKey,
|
||||
skipMetalPlate: options?.skipMetalPlate,
|
||||
});
|
||||
|
||||
if (
|
||||
@@ -10196,9 +10201,10 @@ export class ClaimRequestManagementService {
|
||||
|
||||
private shapeCaptureRequirementsForV3(
|
||||
claimCase: any,
|
||||
_blame: any,
|
||||
blame: any,
|
||||
base: GetCaptureRequirementsV2ResponseDto,
|
||||
): GetCaptureRequirementsV2ResponseDto {
|
||||
const skipMetalPlate = !!(blame as any)?.isMadeByFileMaker;
|
||||
const step = claimCase.workflow?.currentStep;
|
||||
const capturePartDone = this.claimV3StepCompleted(
|
||||
claimCase,
|
||||
@@ -10258,7 +10264,9 @@ export class ClaimRequestManagementService {
|
||||
|
||||
if (step === ClaimWorkflowStep.CAPTURE_PART_DAMAGES) {
|
||||
const capturePhaseDocs = base.requiredDocuments.filter(
|
||||
(d) => d.preferUploadDuringCapture,
|
||||
(d) =>
|
||||
d.preferUploadDuringCapture &&
|
||||
!(skipMetalPlate && OPTIONAL_CAPTURE_PHASE_DOC_KEYS_V4V5.includes(d.key as any)),
|
||||
);
|
||||
return {
|
||||
...base,
|
||||
@@ -10298,6 +10306,7 @@ export class ClaimRequestManagementService {
|
||||
);
|
||||
}
|
||||
const blame = await this.assertV3InPersonClaim(claimCase);
|
||||
const skipMetalPlate = !!(blame as any).isMadeByFileMaker;
|
||||
|
||||
const isCaptureDoc = isCapturePhaseDamagedPartyDocKey(body.documentKey);
|
||||
|
||||
@@ -10306,7 +10315,7 @@ export class ClaimRequestManagementService {
|
||||
this.assertV3ClaimWorkflowStep(
|
||||
claimCase,
|
||||
ClaimWorkflowStep.CAPTURE_PART_DAMAGES,
|
||||
"Upload chassis, engine, and metal plate photos during capture after damaged-part photos and car angles.",
|
||||
"Upload chassis and engine photos during capture after damaged-part photos and car angles.",
|
||||
);
|
||||
if (
|
||||
!this.claimV3StepCompleted(
|
||||
@@ -10318,15 +10327,15 @@ export class ClaimRequestManagementService {
|
||||
"Complete outer and other part selection before capture-phase vehicle documents.",
|
||||
);
|
||||
}
|
||||
const captureProgress = getClaimCaptureProgress(claimCase);
|
||||
const captureProgress = getClaimCaptureProgress(claimCase, { skipMetalPlate });
|
||||
if (!captureProgress.partsComplete) {
|
||||
throw new BadRequestException(
|
||||
"Upload photos for all selected damaged parts before chassis, engine, or metal plate photos.",
|
||||
"Upload photos for all selected damaged parts before chassis or engine photos.",
|
||||
);
|
||||
}
|
||||
if (!captureProgress.anglesComplete) {
|
||||
throw new BadRequestException(
|
||||
"Capture all four car angles before chassis, engine, or metal plate photos.",
|
||||
"Capture all four car angles before chassis or engine photos.",
|
||||
);
|
||||
}
|
||||
} else {
|
||||
@@ -10346,7 +10355,7 @@ export class ClaimRequestManagementService {
|
||||
file,
|
||||
currentUserId,
|
||||
actor,
|
||||
{ v3InPersonFlow: true },
|
||||
{ v3InPersonFlow: true, skipMetalPlate },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -10524,7 +10533,8 @@ export class ClaimRequestManagementService {
|
||||
`Claim case with ID ${claimRequestId} not found`,
|
||||
);
|
||||
}
|
||||
await this.assertV3InPersonClaim(claimCase);
|
||||
const blame = await this.assertV3InPersonClaim(claimCase);
|
||||
const skipMetalPlate = !!(blame as any).isMadeByFileMaker;
|
||||
|
||||
if (!fileDetail) {
|
||||
throw new BadRequestException("Video file is required.");
|
||||
@@ -10544,7 +10554,7 @@ export class ClaimRequestManagementService {
|
||||
);
|
||||
}
|
||||
|
||||
const captureProgress = getClaimCaptureProgress(claimCase);
|
||||
const captureProgress = getClaimCaptureProgress(claimCase, { skipMetalPlate });
|
||||
if (!captureProgress.partsComplete) {
|
||||
throw new BadRequestException(
|
||||
"Upload photos for all selected damaged parts before the walk-around video.",
|
||||
@@ -10555,9 +10565,11 @@ export class ClaimRequestManagementService {
|
||||
"Capture all four car angles before the walk-around video.",
|
||||
);
|
||||
}
|
||||
if (!isClaimCaptureStepComplete(claimCase)) {
|
||||
if (!isClaimCaptureStepComplete(claimCase, { skipMetalPlate })) {
|
||||
throw new BadRequestException(
|
||||
"Upload capture-phase vehicle documents (chassis, engine, metal plate) before the walk-around video.",
|
||||
skipMetalPlate
|
||||
? "Upload capture-phase vehicle documents (chassis and engine) before the walk-around video."
|
||||
: "Upload capture-phase vehicle documents (chassis, engine, metal plate) before the walk-around video.",
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -3530,6 +3530,7 @@ export class ExpertClaimService {
|
||||
|
||||
await this.claimCaseDbService.findByIdAndUpdate(claimRequestId, {
|
||||
claimStatus: ClaimStatus.NEEDS_REVISION,
|
||||
status: ClaimCaseStatus.COMPLETED,
|
||||
"workflow.locked": false,
|
||||
$unset: {
|
||||
"workflow.lockedAt": "",
|
||||
@@ -3567,7 +3568,7 @@ export class ExpertClaimService {
|
||||
|
||||
return {
|
||||
claimRequestId,
|
||||
status: ClaimWorkflowStep.CLAIM_COMPLETED,
|
||||
status: ClaimCaseStatus.COMPLETED,
|
||||
claimStatus: ClaimStatus.NEEDS_REVISION,
|
||||
message: "In-person visit requested. User will be notified.",
|
||||
};
|
||||
@@ -4266,6 +4267,7 @@ export class ExpertClaimService {
|
||||
const blameIds = makerBlames.map((b) => b._id);
|
||||
const claims = (await this.claimCaseDbService.find({
|
||||
blameRequestId: { $in: blameIds },
|
||||
requiresFileMakerApproval: true,
|
||||
})) as any[];
|
||||
|
||||
const blameById = new Map<string, any>(
|
||||
@@ -4690,7 +4692,8 @@ export class ExpertClaimService {
|
||||
const isFactorValidationPending =
|
||||
claimIsAwaitingExpertFactorValidationV2(claim);
|
||||
|
||||
// FileMaker: can always view their own V4 files at any status.
|
||||
// FileMaker: can only view V5 files (requiresFileMakerApproval: true) that they created.
|
||||
// V4 files do not require FileMaker approval and must not appear in their panel.
|
||||
if (actor.role === RoleEnum.FILE_MAKER) {
|
||||
const isOwn = claimCaseInitiatedByFieldExpert(claim, actor, linkedBlame);
|
||||
if (!isOwn) {
|
||||
@@ -4698,6 +4701,11 @@ export class ExpertClaimService {
|
||||
"FileMakers can only view files they created.",
|
||||
);
|
||||
}
|
||||
if (!(claim as any).requiresFileMakerApproval) {
|
||||
throw new ForbiddenException(
|
||||
"This file does not require FileMaker approval.",
|
||||
);
|
||||
}
|
||||
// Fall through to the detail build below
|
||||
} else if (
|
||||
// Field experts and FileReviewers can view IN_PERSON expert-initiated claims
|
||||
|
||||
@@ -16,6 +16,12 @@ export const CAPTURE_PHASE_DAMAGED_PARTY_DOC_KEYS = [
|
||||
"damaged_metal_plate",
|
||||
] as const;
|
||||
|
||||
/** Metal-plate keys that are optional in the V4/V5 FileMaker flow. */
|
||||
export const OPTIONAL_CAPTURE_PHASE_DOC_KEYS_V4V5 = [
|
||||
"damaged_metal_plate",
|
||||
"guilty_metal_plate",
|
||||
] as const;
|
||||
|
||||
export type CapturePhaseSequence =
|
||||
| "parts"
|
||||
| "angles"
|
||||
@@ -52,7 +58,7 @@ function isRequiredDocumentUploadedOnClaim(
|
||||
|
||||
export function getClaimCaptureProgress(
|
||||
claimCase: any,
|
||||
options?: { assumeCapturePhaseDocKey?: string },
|
||||
options?: { assumeCapturePhaseDocKey?: string; skipMetalPlate?: boolean },
|
||||
): ClaimCaptureProgress {
|
||||
const carType = claimCase?.vehicle?.carType as ClaimVehicleTypeV2 | undefined;
|
||||
const selectedNorm = normalizeDamageSelectedParts(
|
||||
@@ -83,6 +89,7 @@ export function getClaimCaptureProgress(
|
||||
|
||||
const capturePhaseDocsRemaining = CAPTURE_PHASE_DAMAGED_PARTY_DOC_KEYS.filter(
|
||||
(k) => {
|
||||
if (options?.skipMetalPlate && OPTIONAL_CAPTURE_PHASE_DOC_KEYS_V4V5.includes(k as any)) return false;
|
||||
if (k === options?.assumeCapturePhaseDocKey) return false;
|
||||
return !isRequiredDocumentUploadedOnClaim(claimCase, k);
|
||||
},
|
||||
@@ -111,8 +118,11 @@ export function getClaimCaptureProgress(
|
||||
};
|
||||
}
|
||||
|
||||
export function isClaimCaptureStepComplete(claimCase: any): boolean {
|
||||
const p = getClaimCaptureProgress(claimCase);
|
||||
export function isClaimCaptureStepComplete(
|
||||
claimCase: any,
|
||||
options?: { skipMetalPlate?: boolean },
|
||||
): boolean {
|
||||
const p = getClaimCaptureProgress(claimCase, options);
|
||||
return (
|
||||
p.partsComplete && p.anglesComplete && p.capturePhaseDocsComplete
|
||||
);
|
||||
|
||||
@@ -364,7 +364,6 @@ export class FileReviewerBlameV4Controller {
|
||||
@ApiBody({
|
||||
schema: {
|
||||
type: "object",
|
||||
required: ["file"],
|
||||
properties: { file: { type: "string", format: "binary" } },
|
||||
},
|
||||
})
|
||||
@@ -385,17 +384,19 @@ export class FileReviewerBlameV4Controller {
|
||||
@Post("upload-video/:requestId")
|
||||
@ApiParam({ name: "requestId" })
|
||||
@ApiOperation({
|
||||
summary: "Blame accident video (FileReviewer final step)",
|
||||
summary: "Blame accident video (FileReviewer final step — optional)",
|
||||
description:
|
||||
"Last step of the V4 flow. Requires claim walk-around video and completed capture. " +
|
||||
"Sets blame status to WAITING_FOR_EXPERT.",
|
||||
"File is optional — omit it to complete the blame without attaching a video.",
|
||||
})
|
||||
async uploadBlameVideo(
|
||||
@Param("requestId") requestId: string,
|
||||
@CurrentUser() fileReviewer: any,
|
||||
@UploadedFile() file: Express.Multer.File,
|
||||
) {
|
||||
await this.mediaPolicyService.assertForBlame(file, requestId, "video");
|
||||
if (file) {
|
||||
await this.mediaPolicyService.assertForBlame(file, requestId, "video");
|
||||
}
|
||||
return this.requestManagementService.expertUploadBlameVideoV3(
|
||||
fileReviewer,
|
||||
requestId,
|
||||
|
||||
@@ -365,7 +365,6 @@ export class FileReviewerBlameV5Controller {
|
||||
@ApiBody({
|
||||
schema: {
|
||||
type: "object",
|
||||
required: ["file"],
|
||||
properties: { file: { type: "string", format: "binary" } },
|
||||
},
|
||||
})
|
||||
@@ -386,17 +385,19 @@ export class FileReviewerBlameV5Controller {
|
||||
@Post("upload-video/:requestId")
|
||||
@ApiParam({ name: "requestId" })
|
||||
@ApiOperation({
|
||||
summary: "Blame accident video (FileReviewer final step — V5)",
|
||||
summary: "Blame accident video (FileReviewer final step — V5, optional)",
|
||||
description:
|
||||
"Last step of the V5 FileReviewer flow. Requires claim walk-around video and completed capture. " +
|
||||
"Sets blame status to WAITING_FOR_FINANCIAL_EXPERT (pending FinancialExpert approval).",
|
||||
"File is optional — omit it to complete the blame without attaching a video.",
|
||||
})
|
||||
async uploadBlameVideo(
|
||||
@Param("requestId") requestId: string,
|
||||
@CurrentUser() fileReviewer: any,
|
||||
@UploadedFile() file: Express.Multer.File,
|
||||
) {
|
||||
await this.mediaPolicyService.assertForBlame(file, requestId, "video");
|
||||
if (file) {
|
||||
await this.mediaPolicyService.assertForBlame(file, requestId, "video");
|
||||
}
|
||||
return this.requestManagementService.expertUploadBlameVideoV5(
|
||||
fileReviewer,
|
||||
requestId,
|
||||
|
||||
@@ -8695,13 +8695,16 @@ export class RequestManagementService {
|
||||
partyRole: PartyRole,
|
||||
actor: any,
|
||||
): void {
|
||||
// V4/V5 FileMaker files skip the location step — it is auto-completed here.
|
||||
const skipLocation = !!(req as any).isMadeByFileMaker;
|
||||
|
||||
if (partyRole === PartyRole.FIRST) {
|
||||
this.pushWorkflowSteps(req, [
|
||||
WorkflowStep.CREATED,
|
||||
WorkflowStep.FIRST_INITIAL_FORM,
|
||||
]);
|
||||
req.workflow.currentStep = WorkflowStep.FIRST_LOCATION;
|
||||
req.workflow.nextStep = WorkflowStep.FIRST_VOICE;
|
||||
const completedSteps = skipLocation
|
||||
? [WorkflowStep.CREATED, WorkflowStep.FIRST_INITIAL_FORM, WorkflowStep.FIRST_LOCATION]
|
||||
: [WorkflowStep.CREATED, WorkflowStep.FIRST_INITIAL_FORM];
|
||||
this.pushWorkflowSteps(req, completedSteps);
|
||||
req.workflow.currentStep = skipLocation ? WorkflowStep.FIRST_VOICE : WorkflowStep.FIRST_LOCATION;
|
||||
req.workflow.nextStep = skipLocation ? WorkflowStep.FIRST_DESCRIPTION : WorkflowStep.FIRST_VOICE;
|
||||
|
||||
const firstIdx = this.getPartyIndex(req, PartyRole.FIRST);
|
||||
const guiltyUserId = req.parties?.[firstIdx]?.person?.userId;
|
||||
@@ -8716,9 +8719,12 @@ export class RequestManagementService {
|
||||
(req as any).markModified("expert");
|
||||
}
|
||||
} else {
|
||||
this.pushWorkflowSteps(req, [WorkflowStep.SECOND_INITIAL_FORM]);
|
||||
req.workflow.currentStep = WorkflowStep.SECOND_LOCATION;
|
||||
req.workflow.nextStep = WorkflowStep.SECOND_VOICE;
|
||||
const completedSteps = skipLocation
|
||||
? [WorkflowStep.SECOND_INITIAL_FORM, WorkflowStep.SECOND_LOCATION]
|
||||
: [WorkflowStep.SECOND_INITIAL_FORM];
|
||||
this.pushWorkflowSteps(req, completedSteps);
|
||||
req.workflow.currentStep = skipLocation ? WorkflowStep.SECOND_VOICE : WorkflowStep.SECOND_LOCATION;
|
||||
req.workflow.nextStep = skipLocation ? WorkflowStep.SECOND_DESCRIPTION : WorkflowStep.SECOND_VOICE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9319,21 +9325,31 @@ export class RequestManagementService {
|
||||
async expertUploadBlameVideoV3(
|
||||
expert: any,
|
||||
requestId: string,
|
||||
file: Express.Multer.File,
|
||||
file: Express.Multer.File | undefined,
|
||||
): Promise<{
|
||||
requestId: string;
|
||||
publicId: string;
|
||||
videoId: string;
|
||||
videoId: string | undefined;
|
||||
status: string;
|
||||
message: string;
|
||||
}> {
|
||||
if (!file) throw new BadRequestException("Video file is required");
|
||||
|
||||
const req = await this.blameRequestDbService.findById(requestId);
|
||||
if (!req) throw new NotFoundException("Request not found");
|
||||
this.assertBlameV3ExpertInPerson(req);
|
||||
await this.verifyExpertAccessForBlameV2(req, expert);
|
||||
|
||||
// Idempotent: if blame is already completed (car-capture auto-completed it
|
||||
// for V4/V5, or a previous call already ran), return success immediately.
|
||||
if ((req as any).status === CaseStatus.COMPLETED) {
|
||||
return {
|
||||
requestId: String(req._id),
|
||||
publicId: req.publicId,
|
||||
videoId: undefined,
|
||||
status: (req as any).status,
|
||||
message: "File already completed.",
|
||||
};
|
||||
}
|
||||
|
||||
if (!(req.expert?.decision as any)?.fields?.accidentWay) {
|
||||
throw new BadRequestException(
|
||||
"Submit accident fields before uploading the blame accident video.",
|
||||
@@ -9375,19 +9391,23 @@ export class RequestManagementService {
|
||||
const firstIdx = this.getPartyIndex(req, PartyRole.FIRST);
|
||||
if (firstIdx === -1) throw new BadRequestException("First party not found");
|
||||
const firstParty = req.parties[firstIdx];
|
||||
if (firstParty?.evidence?.videoId) {
|
||||
throw new ConflictException("Blame accident video already uploaded");
|
||||
|
||||
// Store video if provided; skip silently for V4/V5 where it is optional.
|
||||
let videoId: string | undefined;
|
||||
if (file) {
|
||||
if (firstParty?.evidence?.videoId) {
|
||||
throw new ConflictException("Blame accident video already uploaded");
|
||||
}
|
||||
const videoDocument = await this.blameVideoDbService.create({
|
||||
fileName: file.filename,
|
||||
path: file.path,
|
||||
requestId: new Types.ObjectId(requestId),
|
||||
} as any);
|
||||
if (!firstParty.evidence) firstParty.evidence = {} as any;
|
||||
firstParty.evidence.videoId = String((videoDocument as any)._id);
|
||||
videoId = firstParty.evidence.videoId;
|
||||
}
|
||||
|
||||
const videoDocument = await this.blameVideoDbService.create({
|
||||
fileName: file.filename,
|
||||
path: file.path,
|
||||
requestId: new Types.ObjectId(requestId),
|
||||
} as any);
|
||||
|
||||
if (!firstParty.evidence) firstParty.evidence = {} as any;
|
||||
firstParty.evidence.videoId = String((videoDocument as any)._id);
|
||||
|
||||
this.pushWorkflowSteps(req, [
|
||||
WorkflowStep.FIRST_VIDEO,
|
||||
WorkflowStep.WAITING_FOR_GUILT_DECISION,
|
||||
@@ -9404,7 +9424,7 @@ export class RequestManagementService {
|
||||
actorName: `${expert.firstName || ""} ${expert.lastName || ""}`.trim(),
|
||||
actorType: "field_expert",
|
||||
},
|
||||
metadata: { videoId: firstParty.evidence.videoId },
|
||||
metadata: { videoId: videoId ?? null },
|
||||
} as any);
|
||||
|
||||
if (typeof (req as any).markModified === "function") {
|
||||
@@ -9443,10 +9463,11 @@ export class RequestManagementService {
|
||||
return {
|
||||
requestId: String(req._id),
|
||||
publicId: req.publicId,
|
||||
videoId: firstParty.evidence.videoId,
|
||||
videoId,
|
||||
status: req.status,
|
||||
message:
|
||||
"Blame accident video uploaded. File is now waiting for expert review.",
|
||||
message: file
|
||||
? "Blame accident video uploaded. File is now waiting for expert review."
|
||||
: "File completed. Claim is now ready for damage expert review.",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -9558,6 +9579,17 @@ export class RequestManagementService {
|
||||
const req = await this.blameRequestDbService.findById(requestId);
|
||||
if (!req) throw new NotFoundException("Request not found");
|
||||
await this.verifyExpertAccessForBlameV2(req, actor);
|
||||
|
||||
// V4/V5: location step is skipped — store whatever is sent but do not
|
||||
// validate against workflow phase (the step was auto-completed at inquiry time).
|
||||
if ((req as any).isMadeByFileMaker) {
|
||||
const role = this.resolvePartyRoleV3(req, partyRole);
|
||||
const idx = this.getPartyIndex(req, role);
|
||||
if (idx !== -1 && body) req.parties[idx].location = body as any;
|
||||
await (req as any).save();
|
||||
return { requestId: req._id, publicId: req.publicId, partyRole: role };
|
||||
}
|
||||
|
||||
const role = this.resolvePartyRoleV3(req, partyRole);
|
||||
this.assertBlameV3PartyDetailPhase(req, role);
|
||||
|
||||
@@ -9677,21 +9709,31 @@ export class RequestManagementService {
|
||||
async expertUploadBlameVideoV5(
|
||||
expert: any,
|
||||
requestId: string,
|
||||
file: Express.Multer.File,
|
||||
file: Express.Multer.File | undefined,
|
||||
): Promise<{
|
||||
requestId: string;
|
||||
publicId: string;
|
||||
videoId: string;
|
||||
videoId: string | undefined;
|
||||
status: string;
|
||||
message: string;
|
||||
}> {
|
||||
if (!file) throw new BadRequestException("Video file is required");
|
||||
|
||||
const req = await this.blameRequestDbService.findById(requestId);
|
||||
if (!req) throw new NotFoundException("Request not found");
|
||||
this.assertBlameV3ExpertInPerson(req);
|
||||
await this.verifyExpertAccessForBlameV2(req, expert);
|
||||
|
||||
// Idempotent: blame already completed (car-capture auto-completed it for
|
||||
// V4/V5, or a previous call already ran).
|
||||
if ((req as any).status === CaseStatus.COMPLETED) {
|
||||
return {
|
||||
requestId: String(req._id),
|
||||
publicId: req.publicId,
|
||||
videoId: undefined,
|
||||
status: (req as any).status,
|
||||
message: "File already completed.",
|
||||
};
|
||||
}
|
||||
|
||||
if (!(req.expert?.decision as any)?.fields?.accidentWay) {
|
||||
throw new BadRequestException(
|
||||
"Submit accident fields before uploading the blame accident video.",
|
||||
@@ -9733,19 +9775,23 @@ export class RequestManagementService {
|
||||
const firstIdx = this.getPartyIndex(req, PartyRole.FIRST);
|
||||
if (firstIdx === -1) throw new BadRequestException("First party not found");
|
||||
const firstParty = req.parties[firstIdx];
|
||||
if (firstParty?.evidence?.videoId) {
|
||||
throw new ConflictException("Blame accident video already uploaded");
|
||||
|
||||
// Store video if provided; skip silently for V4/V5 where it is optional.
|
||||
let videoId: string | undefined;
|
||||
if (file) {
|
||||
if (firstParty?.evidence?.videoId) {
|
||||
throw new ConflictException("Blame accident video already uploaded");
|
||||
}
|
||||
const videoDocument = await this.blameVideoDbService.create({
|
||||
fileName: file.filename,
|
||||
path: file.path,
|
||||
requestId: new Types.ObjectId(requestId),
|
||||
} as any);
|
||||
if (!firstParty.evidence) firstParty.evidence = {} as any;
|
||||
firstParty.evidence.videoId = String((videoDocument as any)._id);
|
||||
videoId = firstParty.evidence.videoId;
|
||||
}
|
||||
|
||||
const videoDocument = await this.blameVideoDbService.create({
|
||||
fileName: file.filename,
|
||||
path: file.path,
|
||||
requestId: new Types.ObjectId(requestId),
|
||||
} as any);
|
||||
|
||||
if (!firstParty.evidence) firstParty.evidence = {} as any;
|
||||
firstParty.evidence.videoId = String((videoDocument as any)._id);
|
||||
|
||||
this.pushWorkflowSteps(req, [
|
||||
WorkflowStep.FIRST_VIDEO,
|
||||
WorkflowStep.WAITING_FOR_GUILT_DECISION,
|
||||
@@ -9762,7 +9808,7 @@ export class RequestManagementService {
|
||||
actorName: `${expert.firstName || ""} ${expert.lastName || ""}`.trim(),
|
||||
actorType: "file_reviewer",
|
||||
},
|
||||
metadata: { videoId: firstParty.evidence.videoId },
|
||||
metadata: { videoId: videoId ?? null },
|
||||
} as any);
|
||||
|
||||
if (typeof (req as any).markModified === "function") {
|
||||
@@ -9808,11 +9854,11 @@ export class RequestManagementService {
|
||||
return {
|
||||
requestId: String(req._id),
|
||||
publicId: req.publicId,
|
||||
videoId: firstParty.evidence.videoId,
|
||||
videoId,
|
||||
status: req.status,
|
||||
message:
|
||||
"Blame accident video uploaded. File is now in expert review queue. " +
|
||||
"After the full claim flow completes and the owner signs, the FileMaker must approve before fanavaran submission.",
|
||||
message: file
|
||||
? "Blame accident video uploaded. File is now in expert review queue. After the full claim flow completes and the owner signs, the FileMaker must approve before fanavaran submission."
|
||||
: "File completed. Claim is now ready for damage expert review.",
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user