forked from Yara724/api
fanavaran stage by stage implemented i am so bored to send smart commit sorry MR sina
This commit is contained in:
1
package-lock.json
generated
1
package-lock.json
generated
@@ -23,6 +23,7 @@
|
|||||||
"class-validator": "^0.15.1",
|
"class-validator": "^0.15.1",
|
||||||
"express": "^5.2.1",
|
"express": "^5.2.1",
|
||||||
"fastest-levenshtein": "^1.0.16",
|
"fastest-levenshtein": "^1.0.16",
|
||||||
|
"form-data": "^4.0.6",
|
||||||
"joi": "^18.2.1",
|
"joi": "^18.2.1",
|
||||||
"mongoose": "^8.9.2",
|
"mongoose": "^8.9.2",
|
||||||
"pdfkit": "^0.19.1",
|
"pdfkit": "^0.19.1",
|
||||||
|
|||||||
@@ -39,6 +39,7 @@
|
|||||||
"class-validator": "^0.15.1",
|
"class-validator": "^0.15.1",
|
||||||
"express": "^5.2.1",
|
"express": "^5.2.1",
|
||||||
"fastest-levenshtein": "^1.0.16",
|
"fastest-levenshtein": "^1.0.16",
|
||||||
|
"form-data": "^4.0.6",
|
||||||
"joi": "^18.2.1",
|
"joi": "^18.2.1",
|
||||||
"mongoose": "^8.9.2",
|
"mongoose": "^8.9.2",
|
||||||
"pdfkit": "^0.19.1",
|
"pdfkit": "^0.19.1",
|
||||||
|
|||||||
@@ -54,11 +54,13 @@ import { JwtModule } from "@nestjs/jwt";
|
|||||||
import { MediaPolicyModule } from "src/media-policy/media-policy.module";
|
import { MediaPolicyModule } from "src/media-policy/media-policy.module";
|
||||||
import { HttpModule } from "@nestjs/axios";
|
import { HttpModule } from "@nestjs/axios";
|
||||||
import { FanavaranAuditModule } from "src/fanavaran/fanavaran-audit.module";
|
import { FanavaranAuditModule } from "src/fanavaran/fanavaran-audit.module";
|
||||||
|
import { FanavaranLookupModule } from "src/fanavaran/fanavaran-lookup.module";
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
HttpModule,
|
HttpModule,
|
||||||
FanavaranAuditModule,
|
FanavaranAuditModule,
|
||||||
|
FanavaranLookupModule,
|
||||||
PublicIdModule,
|
PublicIdModule,
|
||||||
UsersModule,
|
UsersModule,
|
||||||
RequestManagementModule,
|
RequestManagementModule,
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,34 +1,34 @@
|
|||||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
|
||||||
import { IsEnum, IsNotEmpty, IsString } from 'class-validator';
|
import { IsEnum, IsNotEmpty, IsString } from "class-validator";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* V2 DTO for capturing car angle or damaged part
|
* V2 DTO for capturing car angle or damaged part
|
||||||
*/
|
*/
|
||||||
export class CapturePartV2Dto {
|
export class CapturePartV2Dto {
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'Type of capture: angle or part',
|
description: "Type of capture: angle or part",
|
||||||
example: 'angle',
|
example: "angle",
|
||||||
enum: ['angle', 'part'],
|
enum: ["angle", "part"],
|
||||||
})
|
})
|
||||||
@IsNotEmpty({ message: 'Capture type is required' })
|
@IsNotEmpty({ message: "Capture type is required" })
|
||||||
@IsEnum(['angle', 'part'], {
|
@IsEnum(["angle", "part"], {
|
||||||
message: 'Capture type must be either "angle" or "part"',
|
message: 'Capture type must be either "angle" or "part"',
|
||||||
})
|
})
|
||||||
captureType: 'angle' | 'part';
|
captureType: "angle" | "part";
|
||||||
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description:
|
description:
|
||||||
'When captureType is angle: front | back | left | right. When part: catalog id as string (e.g. "101"), 0-based index (e.g. "0"), or full catalog key (e.g. left_backfender). Prefer id or index for parts.',
|
'When captureType is angle: front | back | left | right. When part: catalog id as string (e.g. "101"), 0-based index (e.g. "0"), or full catalog key (e.g. left_backfender). Prefer id or index for parts.',
|
||||||
example: 'front',
|
example: "front",
|
||||||
})
|
})
|
||||||
@IsNotEmpty({ message: 'Capture key is required' })
|
@IsNotEmpty({ message: "Capture key is required" })
|
||||||
@IsString({ message: 'Capture key must be a string' })
|
@IsString({ message: "Capture key must be a string" })
|
||||||
captureKey: string;
|
captureKey: string;
|
||||||
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
type: 'string',
|
type: "string",
|
||||||
format: 'binary',
|
format: "binary",
|
||||||
description: 'Image file (JPG, PNG)',
|
description: "Image file (JPG, PNG)",
|
||||||
})
|
})
|
||||||
file: Express.Multer.File;
|
file: Express.Multer.File;
|
||||||
}
|
}
|
||||||
@@ -38,51 +38,58 @@ export class CapturePartV2Dto {
|
|||||||
*/
|
*/
|
||||||
export class CapturePartV2ResponseDto {
|
export class CapturePartV2ResponseDto {
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'Claim request ID',
|
description: "Claim request ID",
|
||||||
example: '507f1f77bcf86cd799439011',
|
example: "507f1f77bcf86cd799439011",
|
||||||
})
|
})
|
||||||
claimRequestId: string;
|
claimRequestId: string;
|
||||||
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'Type of capture',
|
description: "Type of capture",
|
||||||
example: 'angle',
|
example: "angle",
|
||||||
})
|
})
|
||||||
captureType: string;
|
captureType: string;
|
||||||
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'Key of what was captured',
|
description: "Key of what was captured",
|
||||||
example: 'front',
|
example: "front",
|
||||||
})
|
})
|
||||||
captureKey: string;
|
captureKey: string;
|
||||||
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'File URL',
|
description: "File URL",
|
||||||
example: 'http://localhost:3000/files/captures/front-1234567890.jpg',
|
example: "http://localhost:3000/files/captures/front-1234567890.jpg",
|
||||||
})
|
})
|
||||||
fileUrl: string;
|
fileUrl: string;
|
||||||
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'Whether all captures are now complete',
|
description: "Whether all captures are now complete",
|
||||||
example: false,
|
example: false,
|
||||||
})
|
})
|
||||||
allCapturesComplete: boolean;
|
allCapturesComplete: boolean;
|
||||||
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'Current workflow step',
|
description: "Current workflow step",
|
||||||
example: 'CAPTURE_PART_DAMAGES',
|
example: "CAPTURE_PART_DAMAGES",
|
||||||
})
|
})
|
||||||
currentStep: string;
|
currentStep: string;
|
||||||
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'Success message',
|
description: "Success message",
|
||||||
example: 'Angle captured successfully. 6 captures remaining.',
|
example: "Angle captured successfully. 6 captures remaining.",
|
||||||
})
|
})
|
||||||
message: string;
|
message: string;
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
@ApiPropertyOptional({
|
||||||
description: 'True when expert-requested part resends are complete and the claim returned to the expert queue.',
|
description:
|
||||||
|
"True when expert-requested part resends are complete and the claim returned to the expert queue.",
|
||||||
})
|
})
|
||||||
expertResendComplete?: boolean;
|
expertResendComplete?: boolean;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
description:
|
||||||
|
"Best-effort Fanavaran attachment upload result. Local capture still succeeds when this contains a warning.",
|
||||||
|
})
|
||||||
|
fanavaranAttachment?: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -90,20 +97,20 @@ export class CapturePartV2ResponseDto {
|
|||||||
*/
|
*/
|
||||||
export class VideoCaptureV2ResponseDto {
|
export class VideoCaptureV2ResponseDto {
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'Claim case ID',
|
description: "Claim case ID",
|
||||||
example: '507f1f77bcf86cd799439011',
|
example: "507f1f77bcf86cd799439011",
|
||||||
})
|
})
|
||||||
claimRequestId: string;
|
claimRequestId: string;
|
||||||
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'ID of the stored video document (claim-video-capture)',
|
description: "ID of the stored video document (claim-video-capture)",
|
||||||
example: '507f1f77bcf86cd799439012',
|
example: "507f1f77bcf86cd799439012",
|
||||||
})
|
})
|
||||||
videoId: string;
|
videoId: string;
|
||||||
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'Success message',
|
description: "Success message",
|
||||||
example: 'Video capture uploaded successfully.',
|
example: "Video capture uploaded successfully.",
|
||||||
})
|
})
|
||||||
message: string;
|
message: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { ApiProperty } from "@nestjs/swagger";
|
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
|
||||||
|
|
||||||
export class CreateClaimFromBlameResponseDto {
|
export class CreateClaimFromBlameResponseDto {
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
@@ -23,4 +23,10 @@ export class CreateClaimFromBlameResponseDto {
|
|||||||
example: "Claim request created successfully",
|
example: "Claim request created successfully",
|
||||||
})
|
})
|
||||||
message: string;
|
message: string;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
description:
|
||||||
|
"Best-effort Fanavaran early submit result. Claim creation still succeeds when this contains a warning.",
|
||||||
|
})
|
||||||
|
fanavaran?: unknown;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,13 @@
|
|||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
|
||||||
import { IsArray, IsEnum, IsNotEmpty, ArrayMinSize, ArrayUnique, IsOptional, IsInt } from 'class-validator';
|
import {
|
||||||
|
IsArray,
|
||||||
|
IsEnum,
|
||||||
|
IsNotEmpty,
|
||||||
|
ArrayMinSize,
|
||||||
|
ArrayUnique,
|
||||||
|
IsOptional,
|
||||||
|
IsInt,
|
||||||
|
} from "class-validator";
|
||||||
import {
|
import {
|
||||||
ClaimVehicleTypeV2,
|
ClaimVehicleTypeV2,
|
||||||
OuterPartSideV2,
|
OuterPartSideV2,
|
||||||
@@ -12,27 +20,27 @@ import { DamageSelectedPartV2BodyDto } from "./damage-selected-part-v2.dto";
|
|||||||
*/
|
*/
|
||||||
export enum OuterCarPart {
|
export enum OuterCarPart {
|
||||||
// Hood
|
// Hood
|
||||||
HOOD = 'hood',
|
HOOD = "hood",
|
||||||
|
|
||||||
// Doors
|
// Doors
|
||||||
FRONT_RIGHT_DOOR = 'front_right_door',
|
FRONT_RIGHT_DOOR = "front_right_door",
|
||||||
FRONT_LEFT_DOOR = 'front_left_door',
|
FRONT_LEFT_DOOR = "front_left_door",
|
||||||
REAR_RIGHT_DOOR = 'rear_right_door',
|
REAR_RIGHT_DOOR = "rear_right_door",
|
||||||
REAR_LEFT_DOOR = 'rear_left_door',
|
REAR_LEFT_DOOR = "rear_left_door",
|
||||||
|
|
||||||
// Bumpers
|
// Bumpers
|
||||||
FRONT_BUMPER = 'front_bumper',
|
FRONT_BUMPER = "front_bumper",
|
||||||
REAR_BUMPER = 'rear_bumper',
|
REAR_BUMPER = "rear_bumper",
|
||||||
|
|
||||||
// Fenders
|
// Fenders
|
||||||
FRONT_RIGHT_FENDER = 'front_right_fender',
|
FRONT_RIGHT_FENDER = "front_right_fender",
|
||||||
FRONT_LEFT_FENDER = 'front_left_fender',
|
FRONT_LEFT_FENDER = "front_left_fender",
|
||||||
REAR_RIGHT_FENDER = 'rear_right_fender',
|
REAR_RIGHT_FENDER = "rear_right_fender",
|
||||||
REAR_LEFT_FENDER = 'rear_left_fender',
|
REAR_LEFT_FENDER = "rear_left_fender",
|
||||||
|
|
||||||
// Trunk & Roof
|
// Trunk & Roof
|
||||||
TRUNK = 'trunk',
|
TRUNK = "trunk",
|
||||||
ROOF = 'roof',
|
ROOF = "roof",
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -41,38 +49,38 @@ export enum OuterCarPart {
|
|||||||
*/
|
*/
|
||||||
export class SelectOuterPartsV2Dto {
|
export class SelectOuterPartsV2Dto {
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'Array of selected damaged outer car parts',
|
description: "Array of selected damaged outer car parts",
|
||||||
example: ['hood', 'front_right_door', 'rear_bumper', 'roof'],
|
example: ["hood", "front_right_door", "rear_bumper", "roof"],
|
||||||
enum: OuterCarPart,
|
enum: OuterCarPart,
|
||||||
isArray: true,
|
isArray: true,
|
||||||
minItems: 1,
|
minItems: 1,
|
||||||
maxItems: 13,
|
maxItems: 13,
|
||||||
})
|
})
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsArray({ message: 'selectedParts must be an array' })
|
@IsArray({ message: "selectedParts must be an array" })
|
||||||
@ArrayMinSize(1, { message: 'At least one damaged part must be selected' })
|
@ArrayMinSize(1, { message: "At least one damaged part must be selected" })
|
||||||
@ArrayUnique({ message: 'Duplicate parts are not allowed' })
|
@ArrayUnique({ message: "Duplicate parts are not allowed" })
|
||||||
@IsEnum(OuterCarPart, {
|
@IsEnum(OuterCarPart, {
|
||||||
each: true,
|
each: true,
|
||||||
message: 'Invalid part name. Must be one of the valid outer car parts',
|
message: "Invalid part name. Must be one of the valid outer car parts",
|
||||||
})
|
})
|
||||||
selectedParts?: OuterCarPart[];
|
selectedParts?: OuterCarPart[];
|
||||||
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'Selected outer part IDs from catalog',
|
description: "Selected outer part IDs from catalog",
|
||||||
example: [9, 10, 4],
|
example: [9, 10, 4],
|
||||||
type: [Number],
|
type: [Number],
|
||||||
required: false,
|
required: false,
|
||||||
})
|
})
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsArray({ message: 'selectedPartIds must be an array' })
|
@IsArray({ message: "selectedPartIds must be an array" })
|
||||||
@ArrayMinSize(1, { message: 'At least one part ID must be selected' })
|
@ArrayMinSize(1, { message: "At least one part ID must be selected" })
|
||||||
@ArrayUnique({ message: 'Duplicate part IDs are not allowed' })
|
@ArrayUnique({ message: "Duplicate part IDs are not allowed" })
|
||||||
@IsInt({ each: true, message: 'Each selected part ID must be an integer' })
|
@IsInt({ each: true, message: "Each selected part ID must be an integer" })
|
||||||
selectedPartIds?: number[];
|
selectedPartIds?: number[];
|
||||||
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'Vehicle type for validating available outer parts',
|
description: "Vehicle type for validating available outer parts",
|
||||||
enum: ClaimVehicleTypeV2,
|
enum: ClaimVehicleTypeV2,
|
||||||
required: true,
|
required: true,
|
||||||
})
|
})
|
||||||
@@ -86,14 +94,14 @@ export class SelectOuterPartsV2Dto {
|
|||||||
*/
|
*/
|
||||||
export class SelectOuterPartsV2ResponseDto {
|
export class SelectOuterPartsV2ResponseDto {
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'Claim request ID',
|
description: "Claim request ID",
|
||||||
example: '507f1f77bcf86cd799439011',
|
example: "507f1f77bcf86cd799439011",
|
||||||
})
|
})
|
||||||
claimRequestId: string;
|
claimRequestId: string;
|
||||||
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'Public ID shared across blame and claim',
|
description: "Public ID shared across blame and claim",
|
||||||
example: 'A14235',
|
example: "A14235",
|
||||||
})
|
})
|
||||||
publicId: string;
|
publicId: string;
|
||||||
|
|
||||||
@@ -105,29 +113,36 @@ export class SelectOuterPartsV2ResponseDto {
|
|||||||
selectedParts: DamageSelectedPartV2BodyDto[];
|
selectedParts: DamageSelectedPartV2BodyDto[];
|
||||||
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'Selected part IDs',
|
description: "Selected part IDs",
|
||||||
example: [9, 7, 11],
|
example: [9, 7, 11],
|
||||||
type: [Number],
|
type: [Number],
|
||||||
})
|
})
|
||||||
selectedPartIds: number[];
|
selectedPartIds: number[];
|
||||||
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'Current workflow step',
|
description: "Current workflow step",
|
||||||
example: 'SELECT_OUTER_PARTS',
|
example: "SELECT_OUTER_PARTS",
|
||||||
})
|
})
|
||||||
currentStep: string;
|
currentStep: string;
|
||||||
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'Next possible workflow step',
|
description: "Next possible workflow step",
|
||||||
example: 'SELECT_OTHER_PARTS',
|
example: "SELECT_OTHER_PARTS",
|
||||||
})
|
})
|
||||||
nextStep: string;
|
nextStep: string;
|
||||||
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'Success message',
|
description: "Success message",
|
||||||
example: 'Outer parts selected successfully. Please proceed to select other parts.',
|
example:
|
||||||
|
"Outer parts selected successfully. Please proceed to select other parts.",
|
||||||
})
|
})
|
||||||
message: string;
|
message: string;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
description:
|
||||||
|
"Best-effort Fanavaran damage-case submit result. Selecting parts still succeeds when this contains a warning.",
|
||||||
|
})
|
||||||
|
fanavaranDamageCase?: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SetClaimVehicleTypeV2Dto {
|
export class SetClaimVehicleTypeV2Dto {
|
||||||
|
|||||||
@@ -1,26 +1,26 @@
|
|||||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
|
||||||
import { IsEnum, IsNotEmpty, IsString } from 'class-validator';
|
import { IsEnum, IsNotEmpty, IsString } from "class-validator";
|
||||||
import { ClaimRequiredDocumentType } from 'src/Types&Enums/claim-request-management/required-document-type.enum';
|
import { ClaimRequiredDocumentType } from "src/Types&Enums/claim-request-management/required-document-type.enum";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* V2 DTO for uploading required document
|
* V2 DTO for uploading required document
|
||||||
*/
|
*/
|
||||||
export class UploadRequiredDocumentV2Dto {
|
export class UploadRequiredDocumentV2Dto {
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'Document type/key',
|
description: "Document type/key",
|
||||||
example: 'car_green_card',
|
example: "car_green_card",
|
||||||
enum: ClaimRequiredDocumentType,
|
enum: ClaimRequiredDocumentType,
|
||||||
})
|
})
|
||||||
@IsNotEmpty({ message: 'Document key is required' })
|
@IsNotEmpty({ message: "Document key is required" })
|
||||||
@IsEnum(ClaimRequiredDocumentType, {
|
@IsEnum(ClaimRequiredDocumentType, {
|
||||||
message: 'Invalid document type',
|
message: "Invalid document type",
|
||||||
})
|
})
|
||||||
documentKey: ClaimRequiredDocumentType;
|
documentKey: ClaimRequiredDocumentType;
|
||||||
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
type: 'string',
|
type: "string",
|
||||||
format: 'binary',
|
format: "binary",
|
||||||
description: 'Image file (JPG, PNG, PDF)',
|
description: "Image file (JPG, PNG, PDF)",
|
||||||
})
|
})
|
||||||
file: Express.Multer.File;
|
file: Express.Multer.File;
|
||||||
}
|
}
|
||||||
@@ -30,43 +30,51 @@ export class UploadRequiredDocumentV2Dto {
|
|||||||
*/
|
*/
|
||||||
export class UploadRequiredDocumentV2ResponseDto {
|
export class UploadRequiredDocumentV2ResponseDto {
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'Claim request ID',
|
description: "Claim request ID",
|
||||||
example: '507f1f77bcf86cd799439011',
|
example: "507f1f77bcf86cd799439011",
|
||||||
})
|
})
|
||||||
claimRequestId: string;
|
claimRequestId: string;
|
||||||
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'Document key that was uploaded',
|
description: "Document key that was uploaded",
|
||||||
example: 'car_green_card',
|
example: "car_green_card",
|
||||||
})
|
})
|
||||||
documentKey: string;
|
documentKey: string;
|
||||||
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'File URL',
|
description: "File URL",
|
||||||
example: 'http://localhost:3000/files/documents/car-green-card-1234567890.jpg',
|
example:
|
||||||
|
"http://localhost:3000/files/documents/car-green-card-1234567890.jpg",
|
||||||
})
|
})
|
||||||
fileUrl: string;
|
fileUrl: string;
|
||||||
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'Whether all required documents are now uploaded',
|
description: "Whether all required documents are now uploaded",
|
||||||
example: false,
|
example: false,
|
||||||
})
|
})
|
||||||
allDocumentsUploaded: boolean;
|
allDocumentsUploaded: boolean;
|
||||||
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'Current workflow step',
|
description: "Current workflow step",
|
||||||
example: 'UPLOAD_REQUIRED_DOCUMENTS',
|
example: "UPLOAD_REQUIRED_DOCUMENTS",
|
||||||
})
|
})
|
||||||
currentStep: string;
|
currentStep: string;
|
||||||
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'Success message',
|
description: "Success message",
|
||||||
example: 'Document uploaded successfully. 12 documents remaining.',
|
example: "Document uploaded successfully. 12 documents remaining.",
|
||||||
})
|
})
|
||||||
message: string;
|
message: string;
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
@ApiPropertyOptional({
|
||||||
description: 'True when the owner finished every damage-expert resend requirement and the claim is back in the expert queue.',
|
description:
|
||||||
|
"True when the owner finished every damage-expert resend requirement and the claim is back in the expert queue.",
|
||||||
})
|
})
|
||||||
expertResendComplete?: boolean;
|
expertResendComplete?: boolean;
|
||||||
|
|
||||||
|
@ApiPropertyOptional({
|
||||||
|
description:
|
||||||
|
"Best-effort Fanavaran attachment upload result. Local document upload still succeeds when this contains a warning.",
|
||||||
|
})
|
||||||
|
fanavaranAttachment?: unknown;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,6 +58,55 @@ export class RequiredDocumentRef {
|
|||||||
export const RequiredDocumentRefSchema =
|
export const RequiredDocumentRefSchema =
|
||||||
SchemaFactory.createForClass(RequiredDocumentRef);
|
SchemaFactory.createForClass(RequiredDocumentRef);
|
||||||
|
|
||||||
|
@Schema({ _id: false })
|
||||||
|
export class FanavaranSyncStage {
|
||||||
|
@Prop({ type: String })
|
||||||
|
status?: "pending" | "success" | "failed" | "skipped";
|
||||||
|
|
||||||
|
@Prop({ type: Date })
|
||||||
|
lastTriedAt?: Date;
|
||||||
|
|
||||||
|
@Prop({ type: String })
|
||||||
|
lastError?: string;
|
||||||
|
|
||||||
|
@Prop({ type: Number })
|
||||||
|
claimId?: number;
|
||||||
|
|
||||||
|
@Prop({ type: Number })
|
||||||
|
claimNo?: number;
|
||||||
|
|
||||||
|
@Prop({ type: Number })
|
||||||
|
dmgCaseId?: number;
|
||||||
|
|
||||||
|
@Prop({ type: Number })
|
||||||
|
expertiseId?: number;
|
||||||
|
|
||||||
|
@Prop({ type: [MongooseSchema.Types.Mixed], default: [] })
|
||||||
|
files?: unknown[];
|
||||||
|
|
||||||
|
@Prop({ type: MongooseSchema.Types.Mixed })
|
||||||
|
response?: unknown;
|
||||||
|
}
|
||||||
|
export const FanavaranSyncStageSchema =
|
||||||
|
SchemaFactory.createForClass(FanavaranSyncStage);
|
||||||
|
|
||||||
|
@Schema({ _id: false })
|
||||||
|
export class FanavaranSyncState {
|
||||||
|
@Prop({ type: FanavaranSyncStageSchema })
|
||||||
|
baseClaim?: FanavaranSyncStage;
|
||||||
|
|
||||||
|
@Prop({ type: FanavaranSyncStageSchema })
|
||||||
|
damageCase?: FanavaranSyncStage;
|
||||||
|
|
||||||
|
@Prop({ type: FanavaranSyncStageSchema })
|
||||||
|
attachments?: FanavaranSyncStage;
|
||||||
|
|
||||||
|
@Prop({ type: FanavaranSyncStageSchema })
|
||||||
|
expertise?: FanavaranSyncStage;
|
||||||
|
}
|
||||||
|
export const FanavaranSyncStateSchema =
|
||||||
|
SchemaFactory.createForClass(FanavaranSyncState);
|
||||||
|
|
||||||
@Schema({
|
@Schema({
|
||||||
collection: "claimCases",
|
collection: "claimCases",
|
||||||
timestamps: true,
|
timestamps: true,
|
||||||
@@ -142,6 +191,15 @@ export class ClaimCase {
|
|||||||
@Prop({ type: Number })
|
@Prop({ type: Number })
|
||||||
claimId?: number;
|
claimId?: number;
|
||||||
|
|
||||||
|
@Prop({ type: Number })
|
||||||
|
dmgCaseId?: number;
|
||||||
|
|
||||||
|
@Prop({ type: Number })
|
||||||
|
expertiseId?: number;
|
||||||
|
|
||||||
|
@Prop({ type: FanavaranSyncStateSchema, default: () => ({}) })
|
||||||
|
fanavaranSync?: FanavaranSyncState;
|
||||||
|
|
||||||
@Prop({ type: ClaimDamageSelectionSchema, default: () => ({}) })
|
@Prop({ type: ClaimDamageSelectionSchema, default: () => ({}) })
|
||||||
damage?: ClaimDamageSelection;
|
damage?: ClaimDamageSelection;
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,9 @@ export const FANAVARAN_CLIENT_KEYS: readonly FanavaranClientKey[] = [
|
|||||||
"tejaratno",
|
"tejaratno",
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export function isFanavaranClientKey(value: string): value is FanavaranClientKey {
|
export function isFanavaranClientKey(
|
||||||
|
value: string,
|
||||||
|
): value is FanavaranClientKey {
|
||||||
const normalized = value?.trim().toLowerCase();
|
const normalized = value?.trim().toLowerCase();
|
||||||
return normalized === "parsian" || normalized === "tejaratno";
|
return normalized === "parsian" || normalized === "tejaratno";
|
||||||
}
|
}
|
||||||
@@ -38,6 +40,13 @@ export interface FanavaranPayloadDefaults {
|
|||||||
CompensationReferenceId: number;
|
CompensationReferenceId: number;
|
||||||
CulpritLicenceTypeId: number;
|
CulpritLicenceTypeId: number;
|
||||||
CulpritTypeId: number;
|
CulpritTypeId: number;
|
||||||
|
DmgCaseTypeId: number;
|
||||||
|
DmgHistoryStatus: number;
|
||||||
|
PlaqueKindId: number;
|
||||||
|
PlaqueSampleId: number;
|
||||||
|
DriverIsOwner: number;
|
||||||
|
FaultPercent: number;
|
||||||
|
ClaimFileTypeId: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FanavaranClientProfile {
|
export interface FanavaranClientProfile {
|
||||||
@@ -69,6 +78,13 @@ const FANAVARAN_CLIENT_PROFILES: Record<
|
|||||||
CompensationReferenceId: 167,
|
CompensationReferenceId: 167,
|
||||||
CulpritLicenceTypeId: 2,
|
CulpritLicenceTypeId: 2,
|
||||||
CulpritTypeId: 337,
|
CulpritTypeId: 337,
|
||||||
|
DmgCaseTypeId: 175,
|
||||||
|
DmgHistoryStatus: 5214,
|
||||||
|
PlaqueKindId: 8,
|
||||||
|
PlaqueSampleId: 10,
|
||||||
|
DriverIsOwner: 0,
|
||||||
|
FaultPercent: 100,
|
||||||
|
ClaimFileTypeId: 70,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
parsian: {
|
parsian: {
|
||||||
@@ -90,6 +106,13 @@ const FANAVARAN_CLIENT_PROFILES: Record<
|
|||||||
CompensationReferenceId: 167,
|
CompensationReferenceId: 167,
|
||||||
CulpritLicenceTypeId: 2,
|
CulpritLicenceTypeId: 2,
|
||||||
CulpritTypeId: 337,
|
CulpritTypeId: 337,
|
||||||
|
DmgCaseTypeId: 175,
|
||||||
|
DmgHistoryStatus: 5214,
|
||||||
|
PlaqueKindId: 8,
|
||||||
|
PlaqueSampleId: 10,
|
||||||
|
DriverIsOwner: 0,
|
||||||
|
FaultPercent: 100,
|
||||||
|
ClaimFileTypeId: 70,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -127,14 +150,14 @@ export function fanavaranPreviewPath(
|
|||||||
clientKey: FanavaranClientKey,
|
clientKey: FanavaranClientKey,
|
||||||
claimCaseId: string,
|
claimCaseId: string,
|
||||||
): string {
|
): string {
|
||||||
return `/v2/fanavaran/${clientKey}/claim-cases/${claimCaseId}`;
|
return `/v2/fanavaran/${clientKey}/claim-cases/${claimCaseId}/base-claim/preview`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function fanavaranSubmitPath(
|
export function fanavaranSubmitPath(
|
||||||
clientKey: FanavaranClientKey,
|
clientKey: FanavaranClientKey,
|
||||||
claimCaseId: string,
|
claimCaseId: string,
|
||||||
): string {
|
): string {
|
||||||
return `/v2/fanavaran/${clientKey}/claim-cases/${claimCaseId}`;
|
return `/v2/fanavaran/${clientKey}/claim-cases/${claimCaseId}/base-claim/submit`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function fanavaranManualSubmitPath(claimCaseId: string): string {
|
export function fanavaranManualSubmitPath(claimCaseId: string): string {
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import { ClaimRequestManagementDbService } from "src/claim-request-management/en
|
|||||||
import {
|
import {
|
||||||
ClaimRequestManagementService,
|
ClaimRequestManagementService,
|
||||||
FanavaranAutoSubmitResult,
|
FanavaranAutoSubmitResult,
|
||||||
|
FanavaranExpertiseSubmitResult,
|
||||||
} from "src/claim-request-management/claim-request-management.service";
|
} from "src/claim-request-management/claim-request-management.service";
|
||||||
import { ClaimSignDbService } from "src/claim-request-management/entites/db-service/claim-sign.db.service";
|
import { ClaimSignDbService } from "src/claim-request-management/entites/db-service/claim-sign.db.service";
|
||||||
import { DamageImageDbService } from "src/claim-request-management/entites/db-service/damage-image.db.service";
|
import { DamageImageDbService } from "src/claim-request-management/entites/db-service/damage-image.db.service";
|
||||||
@@ -289,7 +290,7 @@ export class ExpertClaimService {
|
|||||||
|
|
||||||
private appendFanavaranAutoSubmitToMessage(
|
private appendFanavaranAutoSubmitToMessage(
|
||||||
baseMessage: string,
|
baseMessage: string,
|
||||||
fanavaran: FanavaranAutoSubmitResult,
|
fanavaran: FanavaranAutoSubmitResult | FanavaranExpertiseSubmitResult,
|
||||||
): string {
|
): string {
|
||||||
if (fanavaran.submitted) {
|
if (fanavaran.submitted) {
|
||||||
return `${baseMessage} The claim was sent to Fanavaran successfully.`;
|
return `${baseMessage} The claim was sent to Fanavaran successfully.`;
|
||||||
@@ -2371,16 +2372,24 @@ export class ExpertClaimService {
|
|||||||
await this.claimRequestManagementService.autoSubmitToFanavaranV2OnClaimCompleted(
|
await this.claimRequestManagementService.autoSubmitToFanavaranV2OnClaimCompleted(
|
||||||
claimRequestId,
|
claimRequestId,
|
||||||
);
|
);
|
||||||
|
const fanavaranExpertise =
|
||||||
|
await this.claimRequestManagementService.autoSubmitFanavaranExpertiseOnExpertReply(
|
||||||
|
claimRequestId,
|
||||||
|
);
|
||||||
return {
|
return {
|
||||||
message: this.appendFanavaranAutoSubmitToMessage(
|
message: this.appendFanavaranAutoSubmitToMessage(
|
||||||
|
this.appendFanavaranAutoSubmitToMessage(
|
||||||
"Factors were reviewed with expert repricing on rejected lines. The claim is completed without an owner signature (temporary policy; may require owner acceptance later).",
|
"Factors were reviewed with expert repricing on rejected lines. The claim is completed without an owner signature (temporary policy; may require owner acceptance later).",
|
||||||
fanavaran,
|
fanavaran,
|
||||||
),
|
),
|
||||||
|
fanavaranExpertise,
|
||||||
|
),
|
||||||
claimRequestId,
|
claimRequestId,
|
||||||
claimStatus: ClaimStatus.APPROVED,
|
claimStatus: ClaimStatus.APPROVED,
|
||||||
caseStatus: ClaimCaseStatus.COMPLETED,
|
caseStatus: ClaimCaseStatus.COMPLETED,
|
||||||
outcome: "REJECTED_REPRICED_AUTO_COMPLETED",
|
outcome: "REJECTED_REPRICED_AUTO_COMPLETED",
|
||||||
fanavaran,
|
fanavaran,
|
||||||
|
fanavaranExpertise,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2397,17 +2406,25 @@ export class ExpertClaimService {
|
|||||||
await this.claimRequestManagementService.autoSubmitToFanavaranV2OnClaimCompleted(
|
await this.claimRequestManagementService.autoSubmitToFanavaranV2OnClaimCompleted(
|
||||||
claimRequestId,
|
claimRequestId,
|
||||||
);
|
);
|
||||||
|
const fanavaranExpertise =
|
||||||
|
await this.claimRequestManagementService.autoSubmitFanavaranExpertiseOnExpertReply(
|
||||||
|
claimRequestId,
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
message: this.appendFanavaranAutoSubmitToMessage(
|
message: this.appendFanavaranAutoSubmitToMessage(
|
||||||
|
this.appendFanavaranAutoSubmitToMessage(
|
||||||
"All factors were approved by the expert. The claim is completed without an additional owner signature.",
|
"All factors were approved by the expert. The claim is completed without an additional owner signature.",
|
||||||
fanavaran,
|
fanavaran,
|
||||||
),
|
),
|
||||||
|
fanavaranExpertise,
|
||||||
|
),
|
||||||
claimRequestId,
|
claimRequestId,
|
||||||
claimStatus: ClaimStatus.APPROVED,
|
claimStatus: ClaimStatus.APPROVED,
|
||||||
caseStatus: ClaimCaseStatus.COMPLETED,
|
caseStatus: ClaimCaseStatus.COMPLETED,
|
||||||
outcome: "ALL_APPROVED_AUTO_COMPLETED",
|
outcome: "ALL_APPROVED_AUTO_COMPLETED",
|
||||||
fanavaran,
|
fanavaran,
|
||||||
|
fanavaranExpertise,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3239,6 +3256,18 @@ export class ExpertClaimService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fanavaranExpertise = !needsFactorUpload
|
||||||
|
? await this.claimRequestManagementService.autoSubmitFanavaranExpertiseOnExpertReply(
|
||||||
|
claimRequestId,
|
||||||
|
)
|
||||||
|
: {
|
||||||
|
attempted: false,
|
||||||
|
submitted: false,
|
||||||
|
skipped: true,
|
||||||
|
skipReason:
|
||||||
|
"Expertise submit waits until factor-needed repair lines are validated.",
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
claimRequestId,
|
claimRequestId,
|
||||||
status: nextCaseStatus,
|
status: nextCaseStatus,
|
||||||
@@ -3251,6 +3280,7 @@ export class ExpertClaimService {
|
|||||||
mixedPricingAndFactors: mixedFactorAndPrice,
|
mixedPricingAndFactors: mixedFactorAndPrice,
|
||||||
allPartsFactorNeeded: !!needsFactorUpload && !!allFactorLines,
|
allPartsFactorNeeded: !!needsFactorUpload && !!allFactorLines,
|
||||||
isFinalReplyAfterObjection,
|
isFinalReplyAfterObjection,
|
||||||
|
fanavaranExpertise,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,31 @@ export const FANAVARAN_REMOTE_LOOKUPS: FanavaranRemoteLookupDefinition[] = [
|
|||||||
url: `${FANAVARAN_LOOKUP_BASE_URL}/car/code-list/accident-culprit-type`,
|
url: `${FANAVARAN_LOOKUP_BASE_URL}/car/code-list/accident-culprit-type`,
|
||||||
cacheFile: "accident-culprit-type.json",
|
cacheFile: "accident-culprit-type.json",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "inspection-place",
|
||||||
|
url: `${FANAVARAN_LOOKUP_BASE_URL}/car/code-list/inspection-place`,
|
||||||
|
cacheFile: "inspection-place.json",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "drop-amount-status",
|
||||||
|
url: `${FANAVARAN_LOOKUP_BASE_URL}/car/code-list/drop-amount-status`,
|
||||||
|
cacheFile: "drop-amount-status.json",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "car-components",
|
||||||
|
url: `${FANAVARAN_LOOKUP_BASE_URL}/car/base-info/car-components`,
|
||||||
|
cacheFile: "car-components.json",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "accident-level",
|
||||||
|
url: `${FANAVARAN_LOOKUP_BASE_URL}/car/code-list/accident-level`,
|
||||||
|
cacheFile: "accident-level.json",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "expert-status",
|
||||||
|
url: `${FANAVARAN_LOOKUP_BASE_URL}/car/code-list/expert-status`,
|
||||||
|
cacheFile: "expert-status.json",
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const TEJARAT_STATIC_ACCIDENT_FILES = {
|
export const TEJARAT_STATIC_ACCIDENT_FILES = {
|
||||||
|
|||||||
@@ -54,11 +54,11 @@ export class FanavaranController {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get(":client/claim-cases/:claimCaseId")
|
@Get(":client/claim-cases/:claimCaseId/base-claim/preview")
|
||||||
@ApiOperation({
|
@ApiOperation({
|
||||||
summary: "Preview Fanavaran submit payload (V2)",
|
summary: "Preview Fanavaran base claim create payload",
|
||||||
description:
|
description:
|
||||||
"Builds the third-party-car-financial-claims body from claimCases + blameCases without calling Fanavaran.",
|
"Builds the GEN.03 third-party-car-financial-claims payload from local claimCases + blameCases without creating a Fanavaran claim.",
|
||||||
})
|
})
|
||||||
@ApiParam({
|
@ApiParam({
|
||||||
name: "client",
|
name: "client",
|
||||||
@@ -87,11 +87,11 @@ export class FanavaranController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post(":client/claim-cases/:claimCaseId")
|
@Post(":client/claim-cases/:claimCaseId/base-claim/submit")
|
||||||
@ApiOperation({
|
@ApiOperation({
|
||||||
summary: "Submit claim to Fanavaran (V2)",
|
summary: "Submit Fanavaran base claim create request",
|
||||||
description:
|
description:
|
||||||
"Authenticates with the selected client credentials and submits the mapped claimCases + blameCases payload.",
|
"Authenticates with the selected tenant credentials and submits the GEN.03 base claim create request. Stores returned Id as claimId and ClaimNo when present.",
|
||||||
})
|
})
|
||||||
@ApiParam({
|
@ApiParam({
|
||||||
name: "client",
|
name: "client",
|
||||||
@@ -113,6 +113,162 @@ export class FanavaranController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Get(":client/claim-cases/:claimCaseId/damage-case/preview")
|
||||||
|
@ApiOperation({
|
||||||
|
summary: "Preview Fanavaran damage-case payload",
|
||||||
|
description:
|
||||||
|
"Builds the GEN.12 damaged vehicle/person case payload without calling Fanavaran. Requires selected damaged parts; submit requires a Fanavaran claimId.",
|
||||||
|
})
|
||||||
|
@ApiParam({
|
||||||
|
name: "client",
|
||||||
|
description: "Fanavaran tenant key",
|
||||||
|
enum: ["parsian", "tejaratno"],
|
||||||
|
})
|
||||||
|
@ApiParam({
|
||||||
|
name: "claimCaseId",
|
||||||
|
description: "Claim case MongoDB ObjectId",
|
||||||
|
})
|
||||||
|
async previewDamageCase(
|
||||||
|
@Param("client") client: string,
|
||||||
|
@Param("claimCaseId") claimCaseId: string,
|
||||||
|
) {
|
||||||
|
const clientKey = this.parseClientParam(client);
|
||||||
|
return await this.claimRequestManagementService.previewFanavaranDamageCaseV2(
|
||||||
|
claimCaseId,
|
||||||
|
clientKey,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post(":client/claim-cases/:claimCaseId/damage-case/submit")
|
||||||
|
@ApiOperation({
|
||||||
|
summary: "Submit Fanavaran damage-case request",
|
||||||
|
description:
|
||||||
|
"Submits the GEN.12 dmg-cases request for the already-created Fanavaran claim and stores returned Id as local dmgCaseId.",
|
||||||
|
})
|
||||||
|
@ApiParam({
|
||||||
|
name: "client",
|
||||||
|
description: "Fanavaran tenant key",
|
||||||
|
enum: ["parsian", "tejaratno"],
|
||||||
|
})
|
||||||
|
@ApiParam({
|
||||||
|
name: "claimCaseId",
|
||||||
|
description: "Claim case MongoDB ObjectId",
|
||||||
|
})
|
||||||
|
async submitDamageCase(
|
||||||
|
@Param("client") client: string,
|
||||||
|
@Param("claimCaseId") claimCaseId: string,
|
||||||
|
) {
|
||||||
|
const clientKey = this.parseClientParam(client);
|
||||||
|
return await this.claimRequestManagementService.submitFanavaranDamageCaseV2(
|
||||||
|
claimCaseId,
|
||||||
|
clientKey,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get(":client/claim-cases/:claimCaseId/attachments/preview")
|
||||||
|
@ApiOperation({
|
||||||
|
summary: "Preview Fanavaran attachment upload plan",
|
||||||
|
description:
|
||||||
|
"Lists local required-document and captured damage images that would be sent to GEN.07. Shows which images are already recorded as uploaded to Fanavaran.",
|
||||||
|
})
|
||||||
|
@ApiParam({
|
||||||
|
name: "client",
|
||||||
|
description: "Fanavaran tenant key",
|
||||||
|
enum: ["parsian", "tejaratno"],
|
||||||
|
})
|
||||||
|
@ApiParam({
|
||||||
|
name: "claimCaseId",
|
||||||
|
description: "Claim case MongoDB ObjectId",
|
||||||
|
})
|
||||||
|
async previewAttachments(
|
||||||
|
@Param("client") client: string,
|
||||||
|
@Param("claimCaseId") claimCaseId: string,
|
||||||
|
) {
|
||||||
|
const clientKey = this.parseClientParam(client);
|
||||||
|
return await this.claimRequestManagementService.previewFanavaranAttachmentsV2(
|
||||||
|
claimCaseId,
|
||||||
|
clientKey,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post(":client/claim-cases/:claimCaseId/attachments/submit")
|
||||||
|
@ApiOperation({
|
||||||
|
summary: "Submit missing Fanavaran attachments",
|
||||||
|
description:
|
||||||
|
"Uploads every local image that does not already have a recorded successful GEN.07 Fanavaran file upload. Uses one multipart request per image.",
|
||||||
|
})
|
||||||
|
@ApiParam({
|
||||||
|
name: "client",
|
||||||
|
description: "Fanavaran tenant key",
|
||||||
|
enum: ["parsian", "tejaratno"],
|
||||||
|
})
|
||||||
|
@ApiParam({
|
||||||
|
name: "claimCaseId",
|
||||||
|
description: "Claim case MongoDB ObjectId",
|
||||||
|
})
|
||||||
|
async submitAttachments(
|
||||||
|
@Param("client") client: string,
|
||||||
|
@Param("claimCaseId") claimCaseId: string,
|
||||||
|
) {
|
||||||
|
const clientKey = this.parseClientParam(client);
|
||||||
|
return await this.claimRequestManagementService.submitFanavaranAttachmentsV2(
|
||||||
|
claimCaseId,
|
||||||
|
clientKey,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get(":client/claim-cases/:claimCaseId/expertise/preview")
|
||||||
|
@ApiOperation({
|
||||||
|
summary: "Preview Fanavaran expertise payload",
|
||||||
|
description:
|
||||||
|
"Builds the GEN.08 expertise payload from the active damage expert reply, price-drop data, and Fanavaran lookup mappings without calling Fanavaran.",
|
||||||
|
})
|
||||||
|
@ApiParam({
|
||||||
|
name: "client",
|
||||||
|
description: "Fanavaran tenant key",
|
||||||
|
enum: ["parsian", "tejaratno"],
|
||||||
|
})
|
||||||
|
@ApiParam({
|
||||||
|
name: "claimCaseId",
|
||||||
|
description: "Claim case MongoDB ObjectId",
|
||||||
|
})
|
||||||
|
async previewExpertise(
|
||||||
|
@Param("client") client: string,
|
||||||
|
@Param("claimCaseId") claimCaseId: string,
|
||||||
|
) {
|
||||||
|
const clientKey = this.parseClientParam(client);
|
||||||
|
return await this.claimRequestManagementService.previewFanavaranExpertiseV2(
|
||||||
|
claimCaseId,
|
||||||
|
clientKey,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post(":client/claim-cases/:claimCaseId/expertise/submit")
|
||||||
|
@ApiOperation({
|
||||||
|
summary: "Submit Fanavaran expertise request",
|
||||||
|
description:
|
||||||
|
"Submits the GEN.08 expertise payload for a claim with existing Fanavaran claimId and dmgCaseId. Stores returned Id as local expertiseId.",
|
||||||
|
})
|
||||||
|
@ApiParam({
|
||||||
|
name: "client",
|
||||||
|
description: "Fanavaran tenant key",
|
||||||
|
enum: ["parsian", "tejaratno"],
|
||||||
|
})
|
||||||
|
@ApiParam({
|
||||||
|
name: "claimCaseId",
|
||||||
|
description: "Claim case MongoDB ObjectId",
|
||||||
|
})
|
||||||
|
async submitExpertise(
|
||||||
|
@Param("client") client: string,
|
||||||
|
@Param("claimCaseId") claimCaseId: string,
|
||||||
|
) {
|
||||||
|
const clientKey = this.parseClientParam(client);
|
||||||
|
return await this.claimRequestManagementService.submitFanavaranExpertiseV2(
|
||||||
|
claimCaseId,
|
||||||
|
clientKey,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private parseClientParam(client: string) {
|
private parseClientParam(client: string) {
|
||||||
if (!isFanavaranClientKey(client)) {
|
if (!isFanavaranClientKey(client)) {
|
||||||
throw new BadRequestException(
|
throw new BadRequestException(
|
||||||
|
|||||||
@@ -8,6 +8,9 @@ export enum FanavaranAuditStep {
|
|||||||
POLICY_INQUIRY = "POLICY_INQUIRY",
|
POLICY_INQUIRY = "POLICY_INQUIRY",
|
||||||
BUILD_PAYLOAD = "BUILD_PAYLOAD",
|
BUILD_PAYLOAD = "BUILD_PAYLOAD",
|
||||||
SUBMIT_CLAIM = "SUBMIT_CLAIM",
|
SUBMIT_CLAIM = "SUBMIT_CLAIM",
|
||||||
|
SUBMIT_DAMAGE_CASE = "SUBMIT_DAMAGE_CASE",
|
||||||
|
SUBMIT_ATTACHMENT = "SUBMIT_ATTACHMENT",
|
||||||
|
SUBMIT_EXPERTISE = "SUBMIT_EXPERTISE",
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum FanavaranAuditStatus {
|
export enum FanavaranAuditStatus {
|
||||||
@@ -31,13 +34,23 @@ export class FanavaranAuditLog {
|
|||||||
@Prop({ type: String, required: true, enum: FanavaranAuditStep, index: true })
|
@Prop({ type: String, required: true, enum: FanavaranAuditStep, index: true })
|
||||||
step: FanavaranAuditStep;
|
step: FanavaranAuditStep;
|
||||||
|
|
||||||
@Prop({ type: String, required: true, enum: FanavaranAuditStatus, index: true })
|
@Prop({
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
enum: FanavaranAuditStatus,
|
||||||
|
index: true,
|
||||||
|
})
|
||||||
status: FanavaranAuditStatus;
|
status: FanavaranAuditStatus;
|
||||||
|
|
||||||
@Prop({ type: String, required: true, index: true })
|
@Prop({ type: String, required: true, index: true })
|
||||||
clientKey: FanavaranClientKey;
|
clientKey: FanavaranClientKey;
|
||||||
|
|
||||||
@Prop({ type: String, required: true, enum: FanavaranAuditSource, index: true })
|
@Prop({
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
enum: FanavaranAuditSource,
|
||||||
|
index: true,
|
||||||
|
})
|
||||||
source: FanavaranAuditSource;
|
source: FanavaranAuditSource;
|
||||||
|
|
||||||
@Prop({ type: Types.ObjectId, required: false, index: true })
|
@Prop({ type: Types.ObjectId, required: false, index: true })
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
import { Controller, Get } from "@nestjs/common";
|
import { Controller, Get, Param } from "@nestjs/common";
|
||||||
import { ApiOkResponse, ApiTags } from "@nestjs/swagger";
|
import {
|
||||||
|
ApiOkResponse,
|
||||||
|
ApiOperation,
|
||||||
|
ApiParam,
|
||||||
|
ApiTags,
|
||||||
|
} from "@nestjs/swagger";
|
||||||
import { LookupsService } from "./lookups.service";
|
import { LookupsService } from "./lookups.service";
|
||||||
|
|
||||||
@ApiTags("lookups")
|
@ApiTags("lookups")
|
||||||
@@ -96,9 +101,113 @@ export class LookupsController {
|
|||||||
return await this.lookupsService.getAccidentCulpritType();
|
return await this.lookupsService.getAccidentCulpritType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Get("inspection-place")
|
||||||
|
@ApiOperation({
|
||||||
|
summary: "Fanavaran GEN.08 inspection place lookup",
|
||||||
|
description:
|
||||||
|
"Returns values for expertise payload field InspectionPlaceId from car/code-list/inspection-place.",
|
||||||
|
})
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: "Returns Fanavaran inspection place lookup data",
|
||||||
|
schema: { type: "array", items: { type: "object" } },
|
||||||
|
})
|
||||||
|
async getInspectionPlace() {
|
||||||
|
return await this.lookupsService.getInspectionPlace();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get("drop-amount-status")
|
||||||
|
@ApiOperation({
|
||||||
|
summary: "Fanavaran GEN.08 drop amount status lookup",
|
||||||
|
description:
|
||||||
|
"Returns values for expertise payload field DropAmountStatus from car/code-list/drop-amount-status.",
|
||||||
|
})
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: "Returns Fanavaran drop amount status lookup data",
|
||||||
|
schema: { type: "array", items: { type: "object" } },
|
||||||
|
})
|
||||||
|
async getDropAmountStatus() {
|
||||||
|
return await this.lookupsService.getDropAmountStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get("car-components")
|
||||||
|
@ApiOperation({
|
||||||
|
summary: "Fanavaran GEN.08 damaged section lookup",
|
||||||
|
description:
|
||||||
|
"Returns values for DmgSections[].DmgSectionId from car/base-info/car-components.",
|
||||||
|
})
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: "Returns Fanavaran car component lookup data",
|
||||||
|
schema: { type: "array", items: { type: "object" } },
|
||||||
|
})
|
||||||
|
async getCarComponents() {
|
||||||
|
return await this.lookupsService.getCarComponents();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get("accident-level")
|
||||||
|
@ApiOperation({
|
||||||
|
summary: "Fanavaran GEN.08 accident level lookup",
|
||||||
|
description:
|
||||||
|
"Returns values for DmgSections[].AccidentLevel from car/code-list/accident-level.",
|
||||||
|
})
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: "Returns Fanavaran accident level lookup data",
|
||||||
|
schema: { type: "array", items: { type: "object" } },
|
||||||
|
})
|
||||||
|
async getAccidentLevel() {
|
||||||
|
return await this.lookupsService.getAccidentLevel();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get("expert-status")
|
||||||
|
@ApiOperation({
|
||||||
|
summary: "Fanavaran GEN.08 expert status lookup",
|
||||||
|
description:
|
||||||
|
"Returns values for expertise response field Status from car/code-list/expert-status.",
|
||||||
|
})
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: "Returns Fanavaran expert status lookup data",
|
||||||
|
schema: { type: "array", items: { type: "object" } },
|
||||||
|
})
|
||||||
|
async getExpertStatus() {
|
||||||
|
return await this.lookupsService.getExpertStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get("fanavaran")
|
||||||
|
@ApiOperation({
|
||||||
|
summary: "List configured Fanavaran remote lookups",
|
||||||
|
description:
|
||||||
|
"Returns the lookup names available through /lookups/fanavaran/{lookupName}.",
|
||||||
|
})
|
||||||
|
async listFanavaranRemoteLookups() {
|
||||||
|
return this.lookupsService.listFanavaranRemoteLookups().map((lookup) => ({
|
||||||
|
name: lookup.name,
|
||||||
|
cacheFile: lookup.cacheFile,
|
||||||
|
url: lookup.url,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get("fanavaran/:lookupName")
|
||||||
|
@ApiOperation({
|
||||||
|
summary: "Fetch a Fanavaran remote lookup by name",
|
||||||
|
description:
|
||||||
|
"Generic cached Fanavaran lookup fetcher for configured lookup names, including GEN.08 expertise lookups.",
|
||||||
|
})
|
||||||
|
@ApiParam({
|
||||||
|
name: "lookupName",
|
||||||
|
description:
|
||||||
|
"Configured Fanavaran lookup name, for example inspection-place, drop-amount-status, car-components, accident-level, expert-status",
|
||||||
|
})
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: "Returns cached or live Fanavaran lookup data",
|
||||||
|
schema: { type: "array", items: { type: "object" } },
|
||||||
|
})
|
||||||
|
async getFanavaranRemoteLookup(@Param("lookupName") lookupName: string) {
|
||||||
|
return await this.lookupsService.getClientRemoteLookup(lookupName);
|
||||||
|
}
|
||||||
|
|
||||||
@Get("accident-way")
|
@Get("accident-way")
|
||||||
@ApiOkResponse({
|
@ApiOkResponse({
|
||||||
description: "Returns accident way options for the add-accident-fields step",
|
description:
|
||||||
|
"Returns accident way options for the add-accident-fields step",
|
||||||
schema: {
|
schema: {
|
||||||
type: "array",
|
type: "array",
|
||||||
items: {
|
items: {
|
||||||
@@ -205,4 +314,3 @@ export class LookupsController {
|
|||||||
return await this.lookupsService.getAccidentFields();
|
return await this.lookupsService.getAccidentFields();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { Injectable, Logger, NotFoundException } from "@nestjs/common";
|
|||||||
import { resolveFanavaranClientKey } from "src/core/config/fanavaran-client.config";
|
import { resolveFanavaranClientKey } from "src/core/config/fanavaran-client.config";
|
||||||
import {
|
import {
|
||||||
FANAVARAN_REMOTE_LOOKUPS,
|
FANAVARAN_REMOTE_LOOKUPS,
|
||||||
|
type FanavaranRemoteLookupDefinition,
|
||||||
TEJARAT_STATIC_ACCIDENT_FILES,
|
TEJARAT_STATIC_ACCIDENT_FILES,
|
||||||
} from "src/fanavaran/fanavaran-lookup.config";
|
} from "src/fanavaran/fanavaran-lookup.config";
|
||||||
import { FanavaranLookupService } from "src/fanavaran/fanavaran-lookup.service";
|
import { FanavaranLookupService } from "src/fanavaran/fanavaran-lookup.service";
|
||||||
@@ -31,10 +32,14 @@ export class LookupsService {
|
|||||||
return resolveFanavaranClientKey();
|
return resolveFanavaranClientKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
listFanavaranRemoteLookups(): FanavaranRemoteLookupDefinition[] {
|
||||||
|
return FANAVARAN_REMOTE_LOOKUPS;
|
||||||
|
}
|
||||||
|
|
||||||
private findRemoteLookup(name: string) {
|
private findRemoteLookup(name: string) {
|
||||||
const lookup = FANAVARAN_REMOTE_LOOKUPS.find((item) => item.name === name);
|
const lookup = FANAVARAN_REMOTE_LOOKUPS.find((item) => item.name === name);
|
||||||
if (!lookup) {
|
if (!lookup) {
|
||||||
throw new Error(`Unknown Fanavaran remote lookup: ${name}`);
|
throw new NotFoundException(`Unknown Fanavaran remote lookup: ${name}`);
|
||||||
}
|
}
|
||||||
return lookup;
|
return lookup;
|
||||||
}
|
}
|
||||||
@@ -52,7 +57,7 @@ export class LookupsService {
|
|||||||
return doc.response;
|
return doc.response;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getClientRemoteLookup(lookupName: string): Promise<unknown> {
|
async getClientRemoteLookup(lookupName: string): Promise<unknown> {
|
||||||
const clientKey = this.activeClientKey();
|
const clientKey = this.activeClientKey();
|
||||||
const definition = this.findRemoteLookup(lookupName);
|
const definition = this.findRemoteLookup(lookupName);
|
||||||
|
|
||||||
@@ -90,6 +95,26 @@ export class LookupsService {
|
|||||||
return await this.getClientRemoteLookup("accident-culprit-type");
|
return await this.getClientRemoteLookup("accident-culprit-type");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getInspectionPlace(): Promise<any> {
|
||||||
|
return await this.getClientRemoteLookup("inspection-place");
|
||||||
|
}
|
||||||
|
|
||||||
|
async getDropAmountStatus(): Promise<any> {
|
||||||
|
return await this.getClientRemoteLookup("drop-amount-status");
|
||||||
|
}
|
||||||
|
|
||||||
|
async getCarComponents(): Promise<any> {
|
||||||
|
return await this.getClientRemoteLookup("car-components");
|
||||||
|
}
|
||||||
|
|
||||||
|
async getAccidentLevel(): Promise<any> {
|
||||||
|
return await this.getClientRemoteLookup("accident-level");
|
||||||
|
}
|
||||||
|
|
||||||
|
async getExpertStatus(): Promise<any> {
|
||||||
|
return await this.getClientRemoteLookup("expert-status");
|
||||||
|
}
|
||||||
|
|
||||||
async getAccidentWay(): Promise<{ id: number; label: string }[]> {
|
async getAccidentWay(): Promise<{ id: number; label: string }[]> {
|
||||||
const clientKey = this.activeClientKey();
|
const clientKey = this.activeClientKey();
|
||||||
const fileName = TEJARAT_STATIC_ACCIDENT_FILES.accidentWay;
|
const fileName = TEJARAT_STATIC_ACCIDENT_FILES.accidentWay;
|
||||||
@@ -129,8 +154,7 @@ export class LookupsService {
|
|||||||
|
|
||||||
if (clientKey === "parsian") {
|
if (clientKey === "parsian") {
|
||||||
const cacheFile = "accident-reason-options.json";
|
const cacheFile = "accident-reason-options.json";
|
||||||
const cached =
|
const cached = await this.fanavaranLookupService.readCacheFile<
|
||||||
await this.fanavaranLookupService.readCacheFile<
|
|
||||||
{ id: number; label: string; fanavaran: number }[]
|
{ id: number; label: string; fanavaran: number }[]
|
||||||
>(clientKey, cacheFile);
|
>(clientKey, cacheFile);
|
||||||
if (cached) {
|
if (cached) {
|
||||||
|
|||||||
Reference in New Issue
Block a user