forked from Yara724/api
Added fileMaker and fileReviewer for new flow
This commit is contained in:
368
src/request-management/file-maker-blame-v4.controller.ts
Normal file
368
src/request-management/file-maker-blame-v4.controller.ts
Normal file
@@ -0,0 +1,368 @@
|
||||
import { extname } from "node:path";
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Get,
|
||||
Param,
|
||||
Post,
|
||||
Put,
|
||||
UploadedFile,
|
||||
UseGuards,
|
||||
UseInterceptors,
|
||||
} from "@nestjs/common";
|
||||
import {
|
||||
ApiBearerAuth,
|
||||
ApiBody,
|
||||
ApiConsumes,
|
||||
ApiOperation,
|
||||
ApiParam,
|
||||
ApiTags,
|
||||
} from "@nestjs/swagger";
|
||||
import { FileInterceptor } from "@nestjs/platform-express";
|
||||
import { diskStorage } from "multer";
|
||||
import { LocalActorAuthGuard } from "src/auth/guards/actor-local.guard";
|
||||
import { RolesGuard } from "src/auth/guards/role.guard";
|
||||
import { Roles } from "src/decorators/roles.decorator";
|
||||
import { CurrentUser } from "src/decorators/user.decorator";
|
||||
import { MediaPolicyService } from "src/media-policy/media-policy.service";
|
||||
import { DEFAULT_MEDIA_MAX_BYTES } from "src/client/client.service";
|
||||
import { RoleEnum } from "src/Types&Enums/role.enum";
|
||||
import { CreationMethod } from "./entities/schema/request-management.schema";
|
||||
import { CreateExpertInitiatedFileDto } from "./dto/expert-initiated.dto";
|
||||
import { SendPartyOtpDto, VerifyPartyOtpDto } from "./dto/party-otp.dto";
|
||||
import { RunInquiriesV3Dto } from "./dto/run-inquiries-v3.dto";
|
||||
import {
|
||||
CarBodyFormDto,
|
||||
DescriptionDto,
|
||||
LocationDto,
|
||||
} from "./dto/create-request-management.dto";
|
||||
import { RequestManagementService } from "./request-management.service";
|
||||
import { PartyRole } from "./entities/schema/partyRole.enum";
|
||||
import { ClaimRequestManagementService } from "src/claim-request-management/claim-request-management.service";
|
||||
import {
|
||||
UploadRequiredDocumentV2Dto,
|
||||
UploadRequiredDocumentV2ResponseDto,
|
||||
} from "src/claim-request-management/dto/upload-document-v2.dto";
|
||||
|
||||
/**
|
||||
* V4 FileMaker flow — first half of the split blame workflow.
|
||||
*
|
||||
* The FileMaker is responsible for everything up to and including the final
|
||||
* party signature. After both signatures are collected the file is considered
|
||||
* "sealed" and a FileReviewer can pick it up.
|
||||
*
|
||||
* THIRD_PARTY sequence:
|
||||
* create → send-party-otp (guilty) → verify-party-otp (guilty)
|
||||
* → [car-body-form if CAR_BODY] → run-inquiries (guilty + auto claim)
|
||||
* → add-detail-location / add-detail-description / upload-voice (guilty)
|
||||
* → sign (guilty)
|
||||
* → send-party-otp (damaged) → verify-party-otp (damaged)
|
||||
* → run-inquiries (damaged)
|
||||
* → add-detail-location / add-detail-description / upload-voice (damaged)
|
||||
* → sign (damaged) ← FileMaker job done here
|
||||
*
|
||||
* CAR_BODY sequence: same but single party — omit second-party steps.
|
||||
*/
|
||||
@ApiTags("v4 FileMaker — blame file creation")
|
||||
@Controller("v4/file-maker/blame-request-management")
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(LocalActorAuthGuard, RolesGuard)
|
||||
@Roles(RoleEnum.FILE_MAKER)
|
||||
export class FileMakerBlameV4Controller {
|
||||
constructor(
|
||||
private readonly requestManagementService: RequestManagementService,
|
||||
private readonly mediaPolicyService: MediaPolicyService,
|
||||
private readonly claimRequestManagementService: ClaimRequestManagementService,
|
||||
) {}
|
||||
|
||||
// ─── File creation ───────────────────────────────────────────────────────────
|
||||
|
||||
@Post()
|
||||
@ApiOperation({ summary: "Create IN_PERSON blame file (FileMaker)" })
|
||||
@ApiBody({ type: CreateExpertInitiatedFileDto })
|
||||
async create(
|
||||
@CurrentUser() fileMaker: any,
|
||||
@Body() dto: CreateExpertInitiatedFileDto,
|
||||
) {
|
||||
return this.requestManagementService.createExpertInitiatedBlameV2(
|
||||
fileMaker,
|
||||
{ ...dto, creationMethod: CreationMethod.IN_PERSON },
|
||||
);
|
||||
}
|
||||
|
||||
@Get("claim-id/:requestId")
|
||||
@ApiParam({ name: "requestId", description: "Blame request ID" })
|
||||
@ApiOperation({
|
||||
summary: "Get linked claim ID",
|
||||
description:
|
||||
"Returns the claim auto-created during guilty-party run-inquiries. " +
|
||||
"Share this claim ID with the FileReviewer.",
|
||||
})
|
||||
async getLinkedClaimId(
|
||||
@Param("requestId") requestId: string,
|
||||
@CurrentUser() fileMaker: any,
|
||||
) {
|
||||
return this.requestManagementService.getV3LinkedClaimForExpert(
|
||||
fileMaker,
|
||||
requestId,
|
||||
);
|
||||
}
|
||||
|
||||
// ─── OTP ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
@Post("send-party-otp/:requestId")
|
||||
@ApiParam({ name: "requestId" })
|
||||
@ApiBody({ type: SendPartyOtpDto })
|
||||
@ApiOperation({
|
||||
summary: "Send OTP to one party",
|
||||
description:
|
||||
"Guilty party first; damaged party after guilty party has signed (THIRD_PARTY).",
|
||||
})
|
||||
async sendPartyOtp(
|
||||
@CurrentUser() fileMaker: any,
|
||||
@Param("requestId") requestId: string,
|
||||
@Body() dto: SendPartyOtpDto,
|
||||
) {
|
||||
return this.requestManagementService.sendPartyOtpV2(
|
||||
fileMaker,
|
||||
requestId,
|
||||
dto,
|
||||
);
|
||||
}
|
||||
|
||||
@Post("verify-party-otp/:requestId")
|
||||
@ApiParam({ name: "requestId" })
|
||||
@ApiBody({ type: VerifyPartyOtpDto })
|
||||
@ApiOperation({
|
||||
summary: "Verify one party OTP",
|
||||
description: "FIRST (guilty) then SECOND (damaged) on THIRD_PARTY files.",
|
||||
})
|
||||
async verifyPartyOtp(
|
||||
@CurrentUser() fileMaker: any,
|
||||
@Param("requestId") requestId: string,
|
||||
@Body() dto: VerifyPartyOtpDto,
|
||||
) {
|
||||
return this.requestManagementService.verifyPartyOtpV2(
|
||||
fileMaker,
|
||||
requestId,
|
||||
dto,
|
||||
);
|
||||
}
|
||||
|
||||
// ─── Pre-inquiry form ────────────────────────────────────────────────────────
|
||||
|
||||
@Post("car-body-form/:requestId")
|
||||
@ApiParam({ name: "requestId" })
|
||||
@ApiBody({ type: CarBodyFormDto })
|
||||
@ApiOperation({
|
||||
summary: "CAR_BODY only — accident type before inquiries",
|
||||
description: "Submit after guilty-party OTP verify, before run-inquiries.",
|
||||
})
|
||||
async carBodyForm(
|
||||
@Param("requestId") requestId: string,
|
||||
@Body() body: CarBodyFormDto,
|
||||
@CurrentUser() fileMaker: any,
|
||||
) {
|
||||
return this.requestManagementService.carBodyAccidentTypeFormV3(
|
||||
requestId,
|
||||
body,
|
||||
fileMaker,
|
||||
);
|
||||
}
|
||||
|
||||
@Post("run-inquiries/:requestId")
|
||||
@ApiParam({ name: "requestId" })
|
||||
@ApiBody({ type: RunInquiriesV3Dto })
|
||||
@ApiOperation({
|
||||
summary: "Run external inquiries for the current party",
|
||||
description:
|
||||
"1st call = guilty party (+ auto claim). 2nd call = damaged party (THIRD_PARTY, after guilty sign).",
|
||||
})
|
||||
async runInquiries(
|
||||
@CurrentUser() fileMaker: any,
|
||||
@Param("requestId") requestId: string,
|
||||
@Body() dto: RunInquiriesV3Dto,
|
||||
) {
|
||||
return this.requestManagementService.runInquiriesV3(
|
||||
requestId,
|
||||
fileMaker,
|
||||
dto,
|
||||
);
|
||||
}
|
||||
|
||||
// ─── Party details ────────────────────────────────────────────────────────────
|
||||
|
||||
@Post("add-detail-location/:requestId")
|
||||
@ApiParam({ name: "requestId" })
|
||||
@ApiBody({ type: LocationDto })
|
||||
@ApiOperation({
|
||||
summary: "Add location for current party (partyRole FIRST or SECOND)",
|
||||
})
|
||||
async addLocation(
|
||||
@Param("requestId") requestId: string,
|
||||
@Body() body: LocationDto,
|
||||
@CurrentUser() fileMaker: any,
|
||||
@Body("partyRole") partyRole?: string,
|
||||
) {
|
||||
return this.requestManagementService.addPartyLocationV3(
|
||||
requestId,
|
||||
fileMaker,
|
||||
body,
|
||||
partyRole,
|
||||
);
|
||||
}
|
||||
|
||||
@Post("add-detail-description/:requestId")
|
||||
@ApiParam({ name: "requestId" })
|
||||
@ApiBody({ type: DescriptionDto })
|
||||
@ApiOperation({ summary: "Add description for current party" })
|
||||
async addDescription(
|
||||
@Param("requestId") requestId: string,
|
||||
@Body() body: DescriptionDto,
|
||||
@CurrentUser() fileMaker: any,
|
||||
@Body("partyRole") partyRole?: string,
|
||||
) {
|
||||
return this.requestManagementService.addPartyDescriptionV3(
|
||||
requestId,
|
||||
fileMaker,
|
||||
body,
|
||||
partyRole,
|
||||
);
|
||||
}
|
||||
|
||||
@ApiBody({
|
||||
schema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
file: { type: "string", format: "binary" },
|
||||
partyRole: { type: "string", enum: ["FIRST", "SECOND"] },
|
||||
},
|
||||
required: ["file"],
|
||||
},
|
||||
})
|
||||
@ApiConsumes("multipart/form-data")
|
||||
@UseInterceptors(
|
||||
FileInterceptor("file", {
|
||||
limits: { fileSize: DEFAULT_MEDIA_MAX_BYTES },
|
||||
storage: diskStorage({
|
||||
destination: "./files/voice",
|
||||
filename: (req, file, callback) => {
|
||||
const unique = Date.now();
|
||||
const ex = extname(file.originalname);
|
||||
callback(null, `v4-filemaker-voice-${unique}${ex}`);
|
||||
},
|
||||
}),
|
||||
}),
|
||||
)
|
||||
@Post("upload-voice/:requestId")
|
||||
@ApiParam({ name: "requestId" })
|
||||
@ApiOperation({ summary: "Upload voice for current party" })
|
||||
async uploadVoice(
|
||||
@Param("requestId") requestId: string,
|
||||
@CurrentUser() fileMaker: any,
|
||||
@UploadedFile() voice: Express.Multer.File,
|
||||
@Body("partyRole") partyRole?: string,
|
||||
) {
|
||||
await this.mediaPolicyService.assertForBlame(voice, requestId, "voice");
|
||||
return this.requestManagementService.addPartyVoiceV3(
|
||||
requestId,
|
||||
fileMaker,
|
||||
voice,
|
||||
partyRole,
|
||||
);
|
||||
}
|
||||
|
||||
// ─── Signatures (final FileMaker step) ───────────────────────────────────────
|
||||
|
||||
@Put("sign/:requestId")
|
||||
@ApiParam({ name: "requestId" })
|
||||
@ApiConsumes("multipart/form-data")
|
||||
@ApiOperation({
|
||||
summary: "Party on-site signature (FileMaker final step)",
|
||||
description:
|
||||
"Collect FIRST (guilty) then SECOND (damaged) signatures. " +
|
||||
"After the last signature the file is ready for FileReviewer pickup.",
|
||||
})
|
||||
@ApiBody({
|
||||
schema: {
|
||||
type: "object",
|
||||
required: ["partyRole", "sign"],
|
||||
properties: {
|
||||
partyRole: { type: "string", enum: ["FIRST", "SECOND"] },
|
||||
isAccept: { type: "boolean", default: true },
|
||||
sign: { type: "string", format: "binary" },
|
||||
},
|
||||
},
|
||||
})
|
||||
@UseInterceptors(
|
||||
FileInterceptor("sign", {
|
||||
limits: { fileSize: DEFAULT_MEDIA_MAX_BYTES },
|
||||
storage: diskStorage({
|
||||
destination: "./files/signs",
|
||||
filename: (req, file, callback) => {
|
||||
const unique = Date.now();
|
||||
const ex = extname(file.originalname);
|
||||
callback(null, `v4-filemaker-party-${unique}${ex}`);
|
||||
},
|
||||
}),
|
||||
}),
|
||||
)
|
||||
async sign(
|
||||
@Param("requestId") requestId: string,
|
||||
@Body() body: { partyRole?: string; isAccept?: string | boolean },
|
||||
@CurrentUser() fileMaker: any,
|
||||
@UploadedFile() sign: Express.Multer.File,
|
||||
) {
|
||||
await this.mediaPolicyService.assertForBlame(sign, requestId, "image");
|
||||
const partyRole =
|
||||
body?.partyRole === "SECOND" ? PartyRole.SECOND : PartyRole.FIRST;
|
||||
const isAccept = !(body?.isAccept === false || body?.isAccept === "false");
|
||||
return this.requestManagementService.expertUploadPartySignatureV3(
|
||||
fileMaker,
|
||||
requestId,
|
||||
partyRole,
|
||||
isAccept,
|
||||
sign,
|
||||
);
|
||||
}
|
||||
|
||||
// ─── Claim document upload ────────────────────────────────────────────────────
|
||||
|
||||
@Post("upload-document/:claimRequestId")
|
||||
@ApiParam({ name: "claimRequestId" })
|
||||
@ApiConsumes("multipart/form-data")
|
||||
@UseInterceptors(
|
||||
FileInterceptor("file", {
|
||||
limits: { fileSize: DEFAULT_MEDIA_MAX_BYTES },
|
||||
storage: diskStorage({
|
||||
destination: "./files/claim-documents",
|
||||
filename: (req, file, callback) => {
|
||||
const unique = Date.now();
|
||||
const ex = extname(file.originalname);
|
||||
callback(null, `${file.originalname.split(".")[0]}-${unique}${ex}`);
|
||||
},
|
||||
}),
|
||||
}),
|
||||
)
|
||||
@ApiOperation({
|
||||
summary: "Upload one required claim document (FileMaker)",
|
||||
description:
|
||||
"Upload licenses and car cards against the auto-created claim. " +
|
||||
"Use claim-id/:requestId to obtain the claimRequestId after run-inquiries.",
|
||||
})
|
||||
async uploadDocument(
|
||||
@Param("claimRequestId") claimRequestId: string,
|
||||
@Body() body: UploadRequiredDocumentV2Dto,
|
||||
@UploadedFile() file: Express.Multer.File,
|
||||
@CurrentUser() fileMaker: any,
|
||||
): Promise<UploadRequiredDocumentV2ResponseDto> {
|
||||
await this.mediaPolicyService.assertForClaim(file, claimRequestId, "image");
|
||||
return this.claimRequestManagementService.uploadRequiredDocumentV3(
|
||||
claimRequestId,
|
||||
body,
|
||||
file,
|
||||
fileMaker.sub,
|
||||
fileMaker,
|
||||
);
|
||||
}
|
||||
}
|
||||
405
src/request-management/file-reviewer-blame-v4.controller.ts
Normal file
405
src/request-management/file-reviewer-blame-v4.controller.ts
Normal file
@@ -0,0 +1,405 @@
|
||||
import { extname } from "node:path";
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Get,
|
||||
Param,
|
||||
Patch,
|
||||
Post,
|
||||
UploadedFile,
|
||||
UseGuards,
|
||||
UseInterceptors,
|
||||
} from "@nestjs/common";
|
||||
import {
|
||||
ApiBearerAuth,
|
||||
ApiBody,
|
||||
ApiConsumes,
|
||||
ApiOperation,
|
||||
ApiParam,
|
||||
ApiTags,
|
||||
} from "@nestjs/swagger";
|
||||
import { FileInterceptor } from "@nestjs/platform-express";
|
||||
import { diskStorage } from "multer";
|
||||
import { LocalActorAuthGuard } from "src/auth/guards/actor-local.guard";
|
||||
import { RolesGuard } from "src/auth/guards/role.guard";
|
||||
import { Roles } from "src/decorators/roles.decorator";
|
||||
import { CurrentUser } from "src/decorators/user.decorator";
|
||||
import { MediaPolicyService } from "src/media-policy/media-policy.service";
|
||||
import { DEFAULT_MEDIA_MAX_BYTES } from "src/client/client.service";
|
||||
import { RoleEnum } from "src/Types&Enums/role.enum";
|
||||
import { ExpertAccidentFieldsDto } from "./dto/expert-accident-fields.dto";
|
||||
import { RequestManagementService } from "./request-management.service";
|
||||
import { ClaimRequestManagementService } from "src/claim-request-management/claim-request-management.service";
|
||||
import {
|
||||
SelectOuterPartsV2Dto,
|
||||
SelectOuterPartsV2ResponseDto,
|
||||
} from "src/claim-request-management/dto/select-outer-parts-v2.dto";
|
||||
import { SelectOtherPartsV2ResponseDto } from "src/claim-request-management/dto/select-other-parts-v2.dto";
|
||||
import { SelectOtherPartsV3Dto } from "src/claim-request-management/dto/select-other-parts-v3.dto";
|
||||
import {
|
||||
UploadRequiredDocumentV2Dto,
|
||||
UploadRequiredDocumentV2ResponseDto,
|
||||
} from "src/claim-request-management/dto/upload-document-v2.dto";
|
||||
import {
|
||||
CapturePartV2Dto,
|
||||
CapturePartV2ResponseDto,
|
||||
} from "src/claim-request-management/dto/capture-part-v2.dto";
|
||||
import { GetCaptureRequirementsV2ResponseDto } from "src/claim-request-management/dto/capture-requirements-v2.dto";
|
||||
|
||||
/**
|
||||
* V4 FileReviewer flow — second half of the split blame workflow.
|
||||
*
|
||||
* The FileReviewer picks up a sealed file (both signatures collected by the
|
||||
* FileMaker) and completes the damage-assessment portion: accident fields,
|
||||
* documents, part selection, photo capture, walk-around video, and the final
|
||||
* blame accident video that moves the file to WAITING_FOR_EXPERT.
|
||||
*
|
||||
* Sequence:
|
||||
* accident-fields
|
||||
* → capture-requirements (via linked claimRequestId)
|
||||
* → upload-document (licenses, car cards)
|
||||
* → select-outer-parts
|
||||
* → select-other-parts
|
||||
* → capture-part (damaged-part photos + four angles)
|
||||
* → upload-document (chassis / engine / metal plate — capture phase)
|
||||
* → car-capture (walk-around video)
|
||||
* → upload-video (blame accident video — final, moves to WAITING_FOR_EXPERT)
|
||||
*/
|
||||
@ApiTags("v4 FileReviewer — blame file review & capture")
|
||||
@Controller("v4/file-reviewer/blame-request-management")
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(LocalActorAuthGuard, RolesGuard)
|
||||
@Roles(RoleEnum.FILE_REVIEWER)
|
||||
export class FileReviewerBlameV4Controller {
|
||||
constructor(
|
||||
private readonly requestManagementService: RequestManagementService,
|
||||
private readonly claimRequestManagementService: ClaimRequestManagementService,
|
||||
private readonly mediaPolicyService: MediaPolicyService,
|
||||
) {}
|
||||
|
||||
// ─── Linked claim lookup ──────────────────────────────────────────────────────
|
||||
|
||||
@Get("claim-id/:requestId")
|
||||
@ApiParam({ name: "requestId", description: "Blame request ID" })
|
||||
@ApiOperation({
|
||||
summary: "Get linked claim ID",
|
||||
description:
|
||||
"Returns the claim auto-created during FileMaker's guilty-party run-inquiries. " +
|
||||
"Use this claim ID for all capture/document/parts endpoints below.",
|
||||
})
|
||||
async getLinkedClaimId(
|
||||
@Param("requestId") requestId: string,
|
||||
@CurrentUser() fileReviewer: any,
|
||||
) {
|
||||
return this.requestManagementService.getV3LinkedClaimForExpert(
|
||||
fileReviewer,
|
||||
requestId,
|
||||
);
|
||||
}
|
||||
|
||||
// ─── Accident fields ──────────────────────────────────────────────────────────
|
||||
|
||||
@Post("accident-fields/:requestId")
|
||||
@ApiParam({ name: "requestId" })
|
||||
@ApiBody({ type: ExpertAccidentFieldsDto })
|
||||
@ApiOperation({
|
||||
summary: "Accident type / fields (FileReviewer first step)",
|
||||
description:
|
||||
"Saves accident fields on the sealed file. " +
|
||||
"Does not move to WAITING_FOR_EXPERT — that happens after the final blame accident video.",
|
||||
})
|
||||
async addAccidentFields(
|
||||
@Param("requestId") requestId: string,
|
||||
@Body() fields: ExpertAccidentFieldsDto,
|
||||
@CurrentUser() fileReviewer: any,
|
||||
) {
|
||||
return this.requestManagementService.expertAddAccidentFieldsForBlameV3(
|
||||
fileReviewer,
|
||||
requestId,
|
||||
fields,
|
||||
);
|
||||
}
|
||||
|
||||
// ─── Capture requirements ─────────────────────────────────────────────────────
|
||||
|
||||
@Get("capture-requirements/:claimRequestId")
|
||||
@ApiParam({ name: "claimRequestId" })
|
||||
@ApiOperation({
|
||||
summary: "Capture requirements (step-aware)",
|
||||
description:
|
||||
"Initial documents phase: pre-capture docs only (no chassis/engine/metal plate). " +
|
||||
"CAPTURE_PART_DAMAGES phase: damaged parts + angles via capture-part, then chassis/engine/metal plate via upload-document. " +
|
||||
"Use `captureSequencePhase` and `captureSequenceHint` to drive the UI.",
|
||||
})
|
||||
async getCaptureRequirements(
|
||||
@Param("claimRequestId") claimRequestId: string,
|
||||
@CurrentUser() fileReviewer: any,
|
||||
): Promise<GetCaptureRequirementsV2ResponseDto> {
|
||||
return this.claimRequestManagementService.getCaptureRequirementsV3(
|
||||
claimRequestId,
|
||||
fileReviewer.sub,
|
||||
fileReviewer,
|
||||
);
|
||||
}
|
||||
|
||||
// ─── Document upload ──────────────────────────────────────────────────────────
|
||||
|
||||
@Post("upload-document/:claimRequestId")
|
||||
@ApiParam({ name: "claimRequestId" })
|
||||
@ApiConsumes("multipart/form-data")
|
||||
@UseInterceptors(
|
||||
FileInterceptor("file", {
|
||||
limits: { fileSize: DEFAULT_MEDIA_MAX_BYTES },
|
||||
storage: diskStorage({
|
||||
destination: "./files/claim-documents",
|
||||
filename: (req, file, callback) => {
|
||||
const unique = Date.now();
|
||||
const ex = extname(file.originalname);
|
||||
callback(null, `${file.originalname.split(".")[0]}-${unique}${ex}`);
|
||||
},
|
||||
}),
|
||||
}),
|
||||
)
|
||||
@ApiOperation({
|
||||
summary: "Upload one required claim document",
|
||||
description:
|
||||
"Initial phase (UPLOAD_REQUIRED_DOCUMENTS): licenses and car cards only. " +
|
||||
"Capture phase (CAPTURE_PART_DAMAGES, after parts + angles): chassis, engine, metal plate only.",
|
||||
})
|
||||
async uploadDocument(
|
||||
@Param("claimRequestId") claimRequestId: string,
|
||||
@Body() body: UploadRequiredDocumentV2Dto,
|
||||
@UploadedFile() file: Express.Multer.File,
|
||||
@CurrentUser() fileReviewer: any,
|
||||
): Promise<UploadRequiredDocumentV2ResponseDto> {
|
||||
await this.mediaPolicyService.assertForClaim(file, claimRequestId, "image");
|
||||
return this.claimRequestManagementService.uploadRequiredDocumentV3(
|
||||
claimRequestId,
|
||||
body,
|
||||
file,
|
||||
fileReviewer.sub,
|
||||
fileReviewer,
|
||||
);
|
||||
}
|
||||
|
||||
// ─── Part selection ───────────────────────────────────────────────────────────
|
||||
|
||||
@Patch("select-outer-parts/:claimRequestId")
|
||||
@ApiParam({ name: "claimRequestId" })
|
||||
@ApiBody({ type: SelectOuterPartsV2Dto })
|
||||
@ApiOperation({ summary: "Select damaged outer parts" })
|
||||
async selectOuterParts(
|
||||
@Param("claimRequestId") claimRequestId: string,
|
||||
@Body() body: SelectOuterPartsV2Dto,
|
||||
@CurrentUser() fileReviewer: any,
|
||||
): Promise<SelectOuterPartsV2ResponseDto> {
|
||||
return this.claimRequestManagementService.selectOuterPartsV3(
|
||||
claimRequestId,
|
||||
body,
|
||||
fileReviewer.sub,
|
||||
fileReviewer,
|
||||
);
|
||||
}
|
||||
|
||||
@Patch("select-other-parts/:claimRequestId")
|
||||
@ApiParam({ name: "claimRequestId", example: "507f1f77bcf86cd799439011" })
|
||||
@ApiOperation({
|
||||
summary: "Select other damaged parts (V4)",
|
||||
description:
|
||||
"Other parts only — sheba and national code were collected during FileMaker's run-inquiries. Optional car green card file.",
|
||||
})
|
||||
@ApiBody({
|
||||
description:
|
||||
"Other parts selection. Bank info is already on the claim from run-inquiries.",
|
||||
schema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
otherParts: {
|
||||
oneOf: [
|
||||
{
|
||||
type: "array",
|
||||
items: {
|
||||
type: "string",
|
||||
enum: [
|
||||
"engine",
|
||||
"suspension",
|
||||
"brake_system",
|
||||
"electrical",
|
||||
"radiator",
|
||||
"transmission",
|
||||
"exhaust",
|
||||
"headlight",
|
||||
"taillight",
|
||||
"mirror",
|
||||
"glass",
|
||||
],
|
||||
},
|
||||
},
|
||||
{ type: "string", description: "JSON string array for multipart" },
|
||||
],
|
||||
example: ["engine", "suspension"],
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
async selectOtherParts(
|
||||
@Param("claimRequestId") claimRequestId: string,
|
||||
@Body() body: SelectOtherPartsV3Dto,
|
||||
@CurrentUser() fileReviewer: any,
|
||||
): Promise<SelectOtherPartsV2ResponseDto> {
|
||||
return this.claimRequestManagementService.selectOtherPartsV3(
|
||||
claimRequestId,
|
||||
body,
|
||||
fileReviewer.sub,
|
||||
fileReviewer,
|
||||
);
|
||||
}
|
||||
|
||||
// ─── Photo capture ────────────────────────────────────────────────────────────
|
||||
|
||||
@Post("capture-part/:claimRequestId")
|
||||
@ApiParam({ name: "claimRequestId", example: "507f1f77bcf86cd799439011" })
|
||||
@ApiConsumes("multipart/form-data")
|
||||
@UseInterceptors(
|
||||
FileInterceptor("file", {
|
||||
limits: { fileSize: DEFAULT_MEDIA_MAX_BYTES },
|
||||
storage: diskStorage({
|
||||
destination: "./files/claim-captures",
|
||||
filename: (req, file, callback) => {
|
||||
const unique = Date.now();
|
||||
const ex = extname(file.originalname);
|
||||
callback(null, `v4-capture-${unique}${ex}`);
|
||||
},
|
||||
}),
|
||||
}),
|
||||
)
|
||||
@ApiOperation({
|
||||
summary: "Car angles and damaged-part photos",
|
||||
description:
|
||||
"CAPTURE_PART_DAMAGES: (1) all damaged-part photos, (2) four car angles, " +
|
||||
"(3) chassis/engine/metal-plate via upload-document. Then car-capture walk-around video.",
|
||||
})
|
||||
@ApiBody({
|
||||
schema: {
|
||||
type: "object",
|
||||
required: ["captureType", "captureKey", "file"],
|
||||
properties: {
|
||||
captureType: {
|
||||
type: "string",
|
||||
enum: ["angle", "part"],
|
||||
example: "angle",
|
||||
},
|
||||
captureKey: {
|
||||
type: "string",
|
||||
example: "front",
|
||||
description:
|
||||
"For angle: front/back/left/right. For part: hood/front_bumper/etc.",
|
||||
},
|
||||
file: { type: "string", format: "binary" },
|
||||
},
|
||||
},
|
||||
})
|
||||
async capturePart(
|
||||
@Param("claimRequestId") claimRequestId: string,
|
||||
@Body() body: CapturePartV2Dto,
|
||||
@UploadedFile() file: Express.Multer.File,
|
||||
@CurrentUser() fileReviewer: any,
|
||||
): Promise<CapturePartV2ResponseDto> {
|
||||
await this.mediaPolicyService.assertForClaim(file, claimRequestId, "image");
|
||||
return this.claimRequestManagementService.capturePartV3(
|
||||
claimRequestId,
|
||||
body,
|
||||
file,
|
||||
fileReviewer.sub,
|
||||
fileReviewer,
|
||||
);
|
||||
}
|
||||
|
||||
// ─── Walk-around video ────────────────────────────────────────────────────────
|
||||
|
||||
@Patch("car-capture/:claimRequestId")
|
||||
@ApiParam({ name: "claimRequestId", example: "507f1f77bcf86cd799439011" })
|
||||
@ApiConsumes("multipart/form-data")
|
||||
@UseInterceptors(
|
||||
FileInterceptor("file", {
|
||||
limits: { fileSize: DEFAULT_MEDIA_MAX_BYTES },
|
||||
storage: diskStorage({
|
||||
destination: "./files/car-capture-videos/",
|
||||
filename: (req, file, callback) => {
|
||||
const unique = Date.now();
|
||||
const ex = extname(file.originalname);
|
||||
callback(null, `v4-claim-video-${unique}${ex}`);
|
||||
},
|
||||
}),
|
||||
}),
|
||||
)
|
||||
@ApiBody({
|
||||
schema: {
|
||||
type: "object",
|
||||
required: ["file"],
|
||||
properties: { file: { type: "string", format: "binary" } },
|
||||
},
|
||||
})
|
||||
@ApiOperation({
|
||||
summary: "Walk-around video",
|
||||
description:
|
||||
"Upload after capture-part is complete (parts, angles, capture-phase docs). Required before the final blame accident video.",
|
||||
})
|
||||
async carCapture(
|
||||
@Param("claimRequestId") claimRequestId: string,
|
||||
@UploadedFile("file") file: Express.Multer.File,
|
||||
@CurrentUser() fileReviewer: any,
|
||||
) {
|
||||
await this.mediaPolicyService.assertForClaim(file, claimRequestId, "video");
|
||||
return this.claimRequestManagementService.setVideoCaptureV3(
|
||||
claimRequestId,
|
||||
file,
|
||||
fileReviewer.sub,
|
||||
fileReviewer,
|
||||
);
|
||||
}
|
||||
|
||||
// ─── Final blame video ────────────────────────────────────────────────────────
|
||||
|
||||
@ApiBody({
|
||||
schema: {
|
||||
type: "object",
|
||||
required: ["file"],
|
||||
properties: { file: { type: "string", format: "binary" } },
|
||||
},
|
||||
})
|
||||
@ApiConsumes("multipart/form-data")
|
||||
@UseInterceptors(
|
||||
FileInterceptor("file", {
|
||||
limits: { fileSize: DEFAULT_MEDIA_MAX_BYTES },
|
||||
storage: diskStorage({
|
||||
destination: "./files/video",
|
||||
filename: (req, file, callback) => {
|
||||
const unique = Date.now();
|
||||
const ex = extname(file.originalname);
|
||||
callback(null, `v4-blame-accident-${unique}${ex}`);
|
||||
},
|
||||
}),
|
||||
}),
|
||||
)
|
||||
@Post("upload-video/:requestId")
|
||||
@ApiParam({ name: "requestId" })
|
||||
@ApiOperation({
|
||||
summary: "Blame accident video (FileReviewer final step)",
|
||||
description:
|
||||
"Last step of the V4 flow. Requires claim walk-around video and completed capture. " +
|
||||
"Sets blame status to WAITING_FOR_EXPERT.",
|
||||
})
|
||||
async uploadBlameVideo(
|
||||
@Param("requestId") requestId: string,
|
||||
@CurrentUser() fileReviewer: any,
|
||||
@UploadedFile() file: Express.Multer.File,
|
||||
) {
|
||||
await this.mediaPolicyService.assertForBlame(file, requestId, "video");
|
||||
return this.requestManagementService.expertUploadBlameVideoV3(
|
||||
fileReviewer,
|
||||
requestId,
|
||||
file,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,8 @@ import { ExpertInitiatedBlameMirrorController } from "./expert-initiated-blame.m
|
||||
import { RegistrarInitiatedController } from "./registrar-initiated.controller";
|
||||
import { RegistrarBlameMirrorController } from "./registrar-blame.mirror.controller";
|
||||
import { ExpertInitiatedBlameV3MirrorController } from "./expert-initiated-blame-v3.mirror.controller";
|
||||
import { FileMakerBlameV4Controller } from "./file-maker-blame-v4.controller";
|
||||
import { FileReviewerBlameV4Controller } from "./file-reviewer-blame-v4.controller";
|
||||
import { InquiryRefreshController } from "./inquiry-refresh.controller";
|
||||
import { InquiryRefreshService } from "./inquiry-refresh.service";
|
||||
|
||||
@@ -81,6 +83,8 @@ import { InquiryRefreshService } from "./inquiry-refresh.service";
|
||||
ExpertInitiatedBlameV3MirrorController,
|
||||
RegistrarInitiatedController,
|
||||
RegistrarBlameMirrorController,
|
||||
FileMakerBlameV4Controller,
|
||||
FileReviewerBlameV4Controller,
|
||||
InquiryRefreshController,
|
||||
],
|
||||
providers: [
|
||||
|
||||
Reference in New Issue
Block a user