Added Field Expert flows, and deactivated inquiry

This commit is contained in:
2026-03-16 15:39:39 +03:30
parent b40270f058
commit fc599acf4a
10 changed files with 1067 additions and 88 deletions

View File

@@ -31,7 +31,11 @@ import { ExpertCompleteThirdPartyFormDto } from "./dto/expert-complete-third-par
import { ExpertCompleteCarBodyFormDto } from "./dto/expert-complete-car-body-form.dto";
import { ExpertAccidentFieldsDto } from "./dto/expert-accident-fields.dto";
import { ExpertCompleteClaimDataDto } from "./dto/expert-complete-claim-data.dto";
import { ExpertUploadPartySignatureDto } from "./dto/expert-upload-party-signature.dto";
import { VerifyPartyOtpsDto } from "./dto/verify-party-otps.dto";
import { SendPartyOtpsDto } from "./dto/send-party-otps.dto";
import { ClaimRequestManagementService } from "src/claim-request-management/claim-request-management.service";
import { PartyRole } from "./entities/schema/partyRole.enum";
/**
* V2 expert-initiated blame API: uses BlameRequest (workflow) model.
@@ -80,7 +84,51 @@ export class ExpertInitiatedV2Controller {
description:
"Creates a BlameRequest with workflow. LINK: parties identified by phone, expert sends link. IN_PERSON: expert fills form later.",
})
@ApiBody({ type: CreateExpertInitiatedFileDto })
@ApiBody({
type: CreateExpertInitiatedFileDto,
examples: {
thirdPartyInPerson: {
summary: "Third-party, IN_PERSON (both parties)",
description:
"Expert fills form on-site for both parties.",
value: {
type: "THIRD_PARTY",
creationMethod: "IN_PERSON",
firstPartyPhoneNumber: "09123456789",
secondPartyPhoneNumber: "09187654321",
},
},
thirdPartyLink: {
summary: "Third-party, LINK",
description:
"Expert sends link to first and second party by phone.",
value: {
type: "THIRD_PARTY",
creationMethod: "LINK",
firstPartyPhoneNumber: "09123456789",
},
},
carBodyInPerson: {
summary: "Car-body, IN_PERSON",
description: "Expert fills form on-site for single party (car body).",
value: {
type: "CAR_BODY",
creationMethod: "IN_PERSON",
firstPartyPhoneNumber: "09123456789",
},
},
carBodyLink: {
summary: "Car-body, LINK",
description:
"Expert sends link to party by phone. Only first party phone needed.",
value: {
type: "CAR_BODY",
creationMethod: "LINK",
firstPartyPhoneNumber: "09123456789",
},
},
},
})
@ApiResponse({
status: 201,
description: "File created",
@@ -103,18 +151,173 @@ export class ExpertInitiatedV2Controller {
);
}
@Post("send-link/:requestId")
@ApiOperation({
summary: "[V2] Send blame link to party/parties (LINK)",
description:
"For expert-initiated LINK files only. Sends the blame link to the first party (and second party for THIRD_PARTY). SMS delivery is mocked for now; first party opens the link and fills the form via the normal flow. Call after create when creationMethod is LINK.",
})
@ApiParam({ name: "requestId", description: "Blame request ID" })
@ApiResponse({
status: 200,
description: "Link sent (mocked); recipients can open the link to fill the form",
schema: {
type: "object",
properties: {
sent: { type: "boolean" },
linkUrl: { type: "string" },
sentTo: {
type: "array",
items: {
type: "object",
properties: {
role: { type: "string", enum: ["FIRST", "SECOND"] },
phoneNumber: { type: "string" },
},
},
},
},
},
})
async sendLinkV2(
@CurrentUser() expert: any,
@Param("requestId") requestId: string,
) {
return this.requestManagementService.sendLinkV2(expert, requestId);
}
@Post("send-party-otps/:requestId")
@ApiOperation({
summary: "[V2] Send OTP to party/parties (IN_PERSON)",
description:
"Sends OTP via SMS to first party (and second party for THIRD_PARTY) using the same flow as /user/send-otp. Parties receive the code; collect it from them and call verify-party-otps. Call this before filling the blame form.",
})
@ApiParam({ name: "requestId", description: "Blame request ID" })
@ApiBody({ type: SendPartyOtpsDto })
@ApiResponse({ status: 200, description: "OTP(s) sent; collect codes and call verify-party-otps" })
async sendPartyOtpsV2(
@CurrentUser() expert: any,
@Param("requestId") requestId: string,
@Body() dto: SendPartyOtpsDto,
) {
return this.requestManagementService.sendPartyOtpsV2(expert, requestId, dto);
}
@Post("verify-party-otps/:requestId")
@ApiOperation({
summary: "[V2] Verify party OTPs (IN_PERSON)",
description:
"After send-party-otps, parties receive SMS. They tell you the code; submit it here. Required before complete-blame-data.",
})
@ApiParam({ name: "requestId", description: "Blame request ID" })
@ApiBody({ type: VerifyPartyOtpsDto })
@ApiResponse({ status: 200, description: "OTPs verified; expert can proceed to complete-blame-data" })
async verifyPartyOtpsV2(
@CurrentUser() expert: any,
@Param("requestId") requestId: string,
@Body() dto: VerifyPartyOtpsDto,
) {
return this.requestManagementService.verifyPartyOtpsV2(expert, requestId, dto);
}
@Post("complete-blame-data/:requestId")
@ApiOperation({
summary: "[V2] Submit all blame data (IN_PERSON)",
description:
"For IN_PERSON files only. Send THIRD_PARTY or CAR_BODY form; completes the BlameRequest.",
"For IN_PERSON files only. Send THIRD_PARTY or CAR_BODY form. After this, status becomes WAITING_FOR_SIGNATURES; use upload-party-signature to collect party signatures.",
})
@ApiParam({ name: "requestId", description: "Blame request ID" })
@ApiBody({
description:
"ExpertCompleteThirdPartyFormDto for THIRD_PARTY or ExpertCompleteCarBodyFormDto for CAR_BODY",
description: "Choose THIRD_PARTY or CAR_BODY example according to the file type. All nested fields are listed so you can fill or test without guessing property names.",
examples: {
THIRD_PARTY: {
summary: "THIRD_PARTY",
description: "Use when the blame file type is THIRD_PARTY",
value: {
firstPartyPhoneNumber: "09123456789",
firstPartyInitialForm: {
expertOpinion: false,
imDamaged: false,
imGuilty: true,
},
firstPartyPlate: {
nationalCodeOfInsurer: "",
nationalCodeOfDriver: "",
insurerLicense: "",
driverLicense: "",
plate: { leftDigits: 12, centerAlphabet: "الف", centerDigits: 345, ir: 22 },
driverIsInsurer: true,
isNewCar: false,
userNoCertificate: false,
insurerBirthday: 13770624,
driverBirthday: "1370-01-01",
},
firstPartyLocation: { lat: 35.6892, lon: 51.389 },
firstPartyDescription: { desc: "توضیح حادثه طرف اول" },
secondParty: {
phoneNumber: "09187654321",
initialForm: {
expertOpinion: false,
imDamaged: true,
imGuilty: false,
},
plate: {
nationalCodeOfInsurer: "",
nationalCodeOfDriver: "",
insurerLicense: "",
driverLicense: "",
plate: { leftDigits: 91, centerAlphabet: "ن", centerDigits: 174, ir: 79 },
driverIsInsurer: true,
isNewCar: false,
userNoCertificate: false,
insurerBirthday: 13700720,
driverBirthday: "1370-01-01",
},
location: { lat: 35.6892, lon: 51.389 },
description: { desc: "توضیح حادثه طرف دوم" },
},
guiltyPartyPhoneNumber: "09123456789",
},
},
CAR_BODY: {
summary: "CAR_BODY",
description: "Use when the blame file type is CAR_BODY",
value: {
firstPartyPhoneNumber: "09123456789",
firstPartyInitialForm: {
expertOpinion: false,
imDamaged: false,
imGuilty: true,
},
carBodyForm: { car: true, object: false },
firstPartyPlate: {
plateId: "",
nationalCodeOfInsurer: "",
nationalCodeOfDriver: "",
insurerLicense: "",
driverLicense: "",
plate: { leftDigits: 12, centerAlphabet: "الف", centerDigits: 345, ir: 22 },
driverIsInsurer: true,
isNewCar: false,
userNoCertificate: false,
insurerBirthday: 1370,
driverBirthday: "1370-01-01",
},
firstPartyLocation: { lat: 35.6892, lon: 51.389 },
firstPartyDescription: {
desc: "توضیح حادثه",
accidentDate: "2025-01-15",
accidentTime: "14:30",
weatherCondition: "صاف",
roadCondition: "خشک",
lightCondition: "روز",
},
},
},
},
schema: { type: "object" },
})
@ApiResponse({ status: 200, description: "Blame form completed" })
@ApiResponse({ status: 200, description: "Blame form completed; next: upload party signature(s)" })
async completeBlameDataV2(
@CurrentUser() expert: any,
@Param("requestId") requestId: string,
@@ -223,6 +426,76 @@ export class ExpertInitiatedV2Controller {
);
}
@Post("upload-party-signature/:requestId")
@ApiOperation({
summary: "[V2] Expert uploads party signature (IN_PERSON)",
description:
"For IN_PERSON only. Upload a party's signature collected on-site. CAR_BODY: use partyRole FIRST once. THIRD_PARTY: upload FIRST then SECOND. When all required parties have signed, blame case completes.",
})
@ApiParam({ name: "requestId", description: "Blame request ID" })
@ApiConsumes("multipart/form-data")
@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: 10 * 1024 * 1024 },
storage: diskStorage({
destination: "./files/signs",
filename: (req, file, callback) => {
const unique = Date.now();
const ex = extname(file.originalname);
callback(null, `expert-party-${unique}${ex}`);
},
}),
}),
)
@ApiResponse({ status: 200, description: "Signature recorded" })
async uploadPartySignatureV2(
@CurrentUser() expert: any,
@Param("requestId") requestId: string,
@Body() body: { partyRole?: string; isAccept?: string | boolean },
@UploadedFile() sign?: Express.Multer.File,
) {
const partyRole = (body.partyRole === "FIRST" || body.partyRole === "SECOND")
? body.partyRole
: ("FIRST" as const);
const isAccept = body.isAccept === false || body.isAccept === "false" ? false : true;
return this.requestManagementService.expertUploadPartySignatureV2(
expert,
requestId,
partyRole as PartyRole,
isAccept,
sign!,
);
}
@Post("create-claim-from-blame/:blameRequestId")
@ApiOperation({
summary: "[V2] Create claim from expert-initiated IN_PERSON blame",
description:
"Field expert creates a claim on behalf of the damaged party. Blame must be COMPLETED (signatures collected). Then use claim v2 endpoints to fill parts, documents, and captures.",
})
@ApiParam({ name: "blameRequestId", description: "Completed blame request ID" })
@ApiResponse({ status: 201, description: "Claim created" })
async createClaimFromBlame(
@CurrentUser() expert: any,
@Param("blameRequestId") blameRequestId: string,
) {
return this.claimRequestManagementService.createClaimFromBlameForExpertV2(
blameRequestId,
expert,
);
}
@Post("complete-claim-data/:claimRequestId")
@ApiOperation({
summary: "Submit claim-needed data (expert-initiated IN_PERSON)",