Files
yara724api/src/claim-request-management/claim-request-management.v2.controller.ts

610 lines
17 KiB
TypeScript

import {
Controller,
HttpException,
InternalServerErrorException,
Param,
Post,
Patch,
Body,
UseGuards,
Get,
UseInterceptors,
UploadedFile,
} from "@nestjs/common";
import { ApiBearerAuth, ApiParam, ApiTags, ApiOperation, ApiResponse, ApiBody, ApiConsumes } from "@nestjs/swagger";
import { FileInterceptor } from "@nestjs/platform-express";
import { diskStorage } from "multer";
import { extname } from "path";
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 { RoleEnum } from "src/Types&Enums/role.enum";
import { ClaimRequestManagementService } from "./claim-request-management.service";
import { SelectOuterPartsV2Dto, SelectOuterPartsV2ResponseDto } from "./dto/select-outer-parts-v2.dto";
import { SelectOtherPartsV2Dto, SelectOtherPartsV2ResponseDto } from "./dto/select-other-parts-v2.dto";
import { GetCaptureRequirementsV2ResponseDto } from "./dto/capture-requirements-v2.dto";
import { UploadRequiredDocumentV2Dto, UploadRequiredDocumentV2ResponseDto } from "./dto/upload-document-v2.dto";
import { CapturePartV2Dto, CapturePartV2ResponseDto } from "./dto/capture-part-v2.dto";
import { GetMyClaimsV2ResponseDto } from "./dto/my-claims-v2.dto";
import { ClaimDetailsV2ResponseDto } from "./dto/claim-details-v2.dto";
@ApiTags("claim-request-management (v2)")
@Controller("v2/claim-request-management")
@ApiBearerAuth()
@UseGuards(GlobalGuard, RolesGuard)
@Roles(RoleEnum.USER)
export class ClaimRequestManagementV2Controller {
constructor(
private readonly claimRequestManagementService: ClaimRequestManagementService,
) {}
@Get("requests")
@ApiOperation({
summary: "Get My Claims (V2)",
description: "Get list of all claim requests for the current user.",
})
@ApiResponse({
status: 200,
description: "List of user claims",
type: GetMyClaimsV2ResponseDto,
})
async getMyClaims(@CurrentUser() user: any): Promise<GetMyClaimsV2ResponseDto> {
try {
return await this.claimRequestManagementService.getMyClaimsV2(user.sub);
} catch (error) {
if (error instanceof HttpException) throw error;
throw new InternalServerErrorException(
error instanceof Error ? error.message : "Failed to get claims list",
);
}
}
@Get("request/:claimRequestId")
@ApiParam({
name: "claimRequestId",
description: "The claim case ID (MongoDB ObjectId)",
example: "507f1f77bcf86cd799439011",
})
@ApiOperation({
summary: "Get Claim Details (V2)",
description: "Get full details of a claim request. Only the claim owner can access.",
})
@ApiResponse({
status: 200,
description: "Claim details",
type: ClaimDetailsV2ResponseDto,
})
@ApiResponse({
status: 403,
description: "User is not the claim owner",
})
@ApiResponse({
status: 404,
description: "Claim not found",
})
async getClaimDetails(
@Param("claimRequestId") claimRequestId: string,
@CurrentUser() user: any,
): Promise<ClaimDetailsV2ResponseDto> {
try {
return await this.claimRequestManagementService.getClaimDetailsV2(
claimRequestId,
user.sub,
);
} catch (error) {
if (error instanceof HttpException) throw error;
throw new InternalServerErrorException(
error instanceof Error ? error.message : "Failed to get claim details",
);
}
}
@Post("create-from-blame/:blameRequestId")
@ApiParam({
name: "blameRequestId",
description: "ID of the completed blame case to create claim from",
})
async createClaimFromBlame(
@Param("blameRequestId") blameRequestId: string,
@CurrentUser() user: any,
) {
try {
return await this.claimRequestManagementService.createClaimFromBlameV2(
blameRequestId,
user.sub,
);
} catch (error) {
if (error instanceof HttpException) throw error;
throw new InternalServerErrorException(
error instanceof Error
? error.message
: "Failed to create claim request",
);
}
}
/**
* V2 API: Select damaged outer car parts (Step 2 of claim workflow)
*/
@Patch("select-outer-parts/:claimRequestId")
@ApiOperation({
summary: "Select Damaged Outer Car Parts (V2 - Step 2)",
description: `
**Workflow Step:** SELECT_OUTER_PARTS (Step 2 of Claim)
**Purpose:** User selects which outer car parts (body parts) were damaged in the accident.
**Validations:**
- Claim must exist
- User must be the claim owner (damaged party from blame case)
- Current workflow step must be CLAIM_CREATED
- Parts array must contain at least 1 part
- No duplicate parts allowed
- Parts cannot be re-selected once submitted
**Valid Outer Parts:**
- hood, trunk, roof
- front_right_door, front_left_door, rear_right_door, rear_left_door
- front_bumper, rear_bumper
- front_right_fender, front_left_fender, rear_right_fender, rear_left_fender
**After Success:**
- Workflow moves to: SELECT_OTHER_PARTS (Step 3)
- User can proceed to select non-body parts and provide bank info
`,
})
@ApiParam({
name: "claimRequestId",
description: "The claim case ID (MongoDB ObjectId)",
example: "507f1f77bcf86cd799439011",
})
@ApiBody({
type: SelectOuterPartsV2Dto,
description: "Array of selected damaged outer parts",
examples: {
example1: {
summary: "Minor front damage",
value: {
selectedParts: ["hood", "front_bumper", "front_right_fender"],
},
},
example2: {
summary: "Side impact damage",
value: {
selectedParts: [
"front_left_door",
"rear_left_door",
"front_left_fender",
"rear_left_fender",
],
},
},
example3: {
summary: "Rear-end collision",
value: {
selectedParts: ["rear_bumper", "trunk", "rear_right_fender"],
},
},
example4: {
summary: "Multiple damage areas",
value: {
selectedParts: [
"hood",
"front_bumper",
"front_right_door",
"rear_bumper",
"trunk",
],
},
},
},
})
@ApiResponse({
status: 200,
description: "Outer parts selected successfully",
type: SelectOuterPartsV2ResponseDto,
})
@ApiResponse({
status: 400,
description: "Invalid workflow step or validation failed",
})
@ApiResponse({
status: 403,
description: "User is not the claim owner",
})
@ApiResponse({
status: 404,
description: "Claim case not found",
})
@ApiResponse({
status: 409,
description: "Outer parts already selected",
})
async selectOuterParts(
@Param("claimRequestId") claimRequestId: string,
@Body() body: SelectOuterPartsV2Dto,
@CurrentUser() user: any,
): Promise<SelectOuterPartsV2ResponseDto> {
try {
return await this.claimRequestManagementService.selectOuterPartsV2(
claimRequestId,
body,
user.sub,
);
} catch (error) {
if (error instanceof HttpException) throw error;
throw new InternalServerErrorException(
error instanceof Error
? error.message
: "Failed to select outer parts",
);
}
}
/**
* V2 API: Select other damaged parts and provide bank information (Step 3 of claim workflow)
*/
@Patch("select-other-parts/:claimRequestId")
@ApiOperation({
summary: "Select Other Parts & Bank Information (V2 - Step 3)",
description: `
**Workflow Step:** SELECT_OTHER_PARTS (Step 3 of Claim)
**Purpose:** User selects non-body damaged parts and provides bank information for payment.
**Validations:**
- Claim must exist
- User must be the claim owner (damaged party from blame case)
- Current workflow step must be SELECT_OTHER_PARTS
- Bank information must not have been submitted previously
- Sheba number must be exactly 24 digits
- National code must be exactly 10 digits
**Valid Other Parts (Optional):**
- engine, suspension, brake_system, electrical
- radiator, transmission, exhaust
- headlight, taillight, mirror, glass
**After Success:**
- Workflow moves to: UPLOAD_REQUIRED_DOCUMENTS (Step 4)
- User can proceed to upload required documents
`,
})
@ApiParam({
name: "claimRequestId",
description: "The claim case ID (MongoDB ObjectId)",
example: "507f1f77bcf86cd799439011",
})
@ApiBody({
type: SelectOtherPartsV2Dto,
description: "Other parts selection and bank information",
examples: {
example1: {
summary: "Only bank info (no other parts damaged)",
value: {
otherParts: [],
shebaNumber: "123456789012345678901234",
nationalCodeOfOwner: "1234567890",
},
},
example2: {
summary: "Engine and suspension damage",
value: {
otherParts: ["engine", "suspension"],
shebaNumber: "123456789012345678901234",
nationalCodeOfOwner: "1234567890",
},
},
example3: {
summary: "Multiple systems damaged",
value: {
otherParts: ["engine", "brake_system", "electrical", "headlight"],
shebaNumber: "123456789012345678901234",
nationalCodeOfOwner: "1234567890",
},
},
example4: {
summary: "Lighting and glass damage",
value: {
otherParts: ["headlight", "taillight", "mirror", "glass"],
shebaNumber: "123456789012345678901234",
nationalCodeOfOwner: "1234567890",
},
},
},
})
@ApiResponse({
status: 200,
description: "Other parts and bank information saved successfully",
type: SelectOtherPartsV2ResponseDto,
})
@ApiResponse({
status: 400,
description: "Invalid workflow step or validation failed",
})
@ApiResponse({
status: 403,
description: "User is not the claim owner",
})
@ApiResponse({
status: 404,
description: "Claim case not found",
})
@ApiResponse({
status: 409,
description: "Bank information already submitted",
})
async selectOtherParts(
@Param("claimRequestId") claimRequestId: string,
@Body() body: SelectOtherPartsV2Dto,
@CurrentUser() user: any,
): Promise<SelectOtherPartsV2ResponseDto> {
try {
return await this.claimRequestManagementService.selectOtherPartsV2(
claimRequestId,
body,
user.sub,
);
} catch (error) {
if (error instanceof HttpException) throw error;
throw new InternalServerErrorException(
error instanceof Error
? error.message
: "Failed to select other parts",
);
}
}
/**
* V2 API: Get capture requirements (documents, angles, parts)
*/
@Get("capture-requirements/:claimRequestId")
@ApiOperation({
summary: "Get Capture Requirements (V2)",
description: `
**Get list of what needs to be captured:**
- Required documents (13 items)
- Car angles (4 items: front, back, left, right)
- Damaged parts (based on selected outer parts)
Returns status of each item (uploaded/captured or not).
`,
})
@ApiParam({
name: "claimRequestId",
description: "The claim case ID (MongoDB ObjectId)",
example: "507f1f77bcf86cd799439011",
})
@ApiResponse({
status: 200,
description: "Capture requirements retrieved successfully",
type: GetCaptureRequirementsV2ResponseDto,
})
@ApiResponse({
status: 403,
description: "User is not the claim owner",
})
@ApiResponse({
status: 404,
description: "Claim case not found",
})
async getCaptureRequirements(
@Param("claimRequestId") claimRequestId: string,
@CurrentUser() user: any,
): Promise<GetCaptureRequirementsV2ResponseDto> {
try {
return await this.claimRequestManagementService.getCaptureRequirementsV2(
claimRequestId,
user.sub,
);
} catch (error) {
if (error instanceof HttpException) throw error;
throw new InternalServerErrorException(
error instanceof Error
? error.message
: "Failed to get capture requirements",
);
}
}
/**
* V2 API: Upload required document
*/
@Post("upload-document/:claimRequestId")
@UseInterceptors(
FileInterceptor("file", {
limits: {
fileSize: 10 * 1024 * 1024, // 10MB
},
storage: diskStorage({
destination: "./files/claim-documents",
filename: (req, file, callback) => {
const unique = Date.now();
const ex = extname(file.originalname);
const filename = `${file.originalname.split(".")[0]}-${unique}${ex}`;
callback(null, filename);
},
}),
}),
)
@ApiConsumes("multipart/form-data")
@ApiOperation({
summary: "Upload Required Document (V2 - Step 4)",
description: `
**Workflow Step:** UPLOAD_REQUIRED_DOCUMENTS (Step 4 of Claim)
**Upload one of the 13 required documents:**
- car_green_card
- damaged_driving_license_front/back
- damaged_chassis_number, damaged_engine_photo
- damaged_car_card_front/back, damaged_metal_plate
- guilty_driving_license_front/back
- guilty_car_card_front/back, guilty_metal_plate
**When all 13 documents are uploaded:**
- Workflow automatically moves to: CAPTURE_PART_DAMAGES (Step 5)
`,
})
@ApiParam({
name: "claimRequestId",
description: "The claim case ID",
example: "507f1f77bcf86cd799439011",
})
@ApiBody({
schema: {
type: "object",
required: ["documentKey", "file"],
properties: {
documentKey: {
type: "string",
enum: [
"car_green_card",
"damaged_driving_license_front",
"damaged_driving_license_back",
"damaged_chassis_number",
"damaged_engine_photo",
"damaged_car_card_front",
"damaged_car_card_back",
"damaged_metal_plate",
"guilty_driving_license_front",
"guilty_driving_license_back",
"guilty_car_card_front",
"guilty_car_card_back",
"guilty_metal_plate",
],
example: "car_green_card",
},
file: {
type: "string",
format: "binary",
},
},
},
})
@ApiResponse({
status: 200,
description: "Document uploaded successfully",
type: UploadRequiredDocumentV2ResponseDto,
})
@ApiResponse({
status: 400,
description: "Invalid workflow step",
})
@ApiResponse({
status: 409,
description: "Document already uploaded",
})
async uploadDocument(
@Param("claimRequestId") claimRequestId: string,
@Body() body: UploadRequiredDocumentV2Dto,
@UploadedFile() file: Express.Multer.File,
@CurrentUser() user: any,
): Promise<UploadRequiredDocumentV2ResponseDto> {
try {
return await this.claimRequestManagementService.uploadRequiredDocumentV2(
claimRequestId,
body,
file,
user.sub,
);
} catch (error) {
if (error instanceof HttpException) throw error;
throw new InternalServerErrorException(
error instanceof Error ? error.message : "Failed to upload document",
);
}
}
/**
* V2 API: Capture car angle or damaged part
*/
@Post("capture-part/:claimRequestId")
@UseInterceptors(
FileInterceptor("file", {
limits: {
fileSize: 10 * 1024 * 1024, // 10MB
},
storage: diskStorage({
destination: "./files/claim-captures",
filename: (req, file, callback) => {
const unique = Date.now();
const ex = extname(file.originalname);
const filename = `${file.originalname.split(".")[0]}-${unique}${ex}`;
callback(null, filename);
},
}),
}),
)
@ApiConsumes("multipart/form-data")
@ApiOperation({
summary: "Capture Car Angle or Damaged Part (V2 - Step 5)",
description: `
**Workflow Step:** CAPTURE_PART_DAMAGES (Step 5 of Claim)
**Capture types:**
1. **angle**: Car angles (front, back, left, right) - 4 required
2. **part**: Damaged parts based on selectedParts from Step 2
**All captures must be completed before user can submit claim.**
`,
})
@ApiParam({
name: "claimRequestId",
description: "The claim case ID",
example: "507f1f77bcf86cd799439011",
})
@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",
},
},
},
})
@ApiResponse({
status: 200,
description: "Capture saved successfully",
type: CapturePartV2ResponseDto,
})
@ApiResponse({
status: 400,
description: "Invalid workflow step",
})
async capturePart(
@Param("claimRequestId") claimRequestId: string,
@Body() body: CapturePartV2Dto,
@UploadedFile() file: Express.Multer.File,
@CurrentUser() user: any,
): Promise<CapturePartV2ResponseDto> {
try {
return await this.claimRequestManagementService.capturePartV2(
claimRequestId,
body,
file,
user.sub,
);
} catch (error) {
if (error instanceof HttpException) throw error;
throw new InternalServerErrorException(
error instanceof Error ? error.message : "Failed to capture part",
);
}
}
}