merge upstream

This commit is contained in:
2026-07-28 16:58:26 +03:30
16 changed files with 749 additions and 50 deletions

View File

@@ -10841,11 +10841,6 @@ export class ClaimRequestManagementService {
const shebaNumber = claimCase.money?.sheba;
const nationalCode = claimCase.money?.nationalCodeOfInsurer;
if (!shebaNumber || !nationalCode) {
throw new BadRequestException(
"Bank information is missing on the claim. Complete run-inquiries for both parties first.",
);
}
const updatePayload: any = {
"damage.otherParts": otherParts.length > 0 ? otherParts : undefined,
@@ -10884,14 +10879,22 @@ export class ClaimRequestManagementService {
claimRequestId: updatedClaim._id.toString(),
publicId: updatedClaim.publicId,
otherParts,
shebaNumber: String(shebaNumber).replace(
/^(.{4})(.*)(.{4})$/,
"IR$1************$3",
),
nationalCodeOfOwner: String(nationalCode).replace(
/^(.{2})(.*)(.{2})$/,
"$1******$3",
),
...(shebaNumber
? {
shebaNumber: String(shebaNumber).replace(
/^(.{4})(.*)(.{4})$/,
"IR$1************$3",
),
}
: {}),
...(nationalCode
? {
nationalCodeOfOwner: String(nationalCode).replace(
/^(.{2})(.*)(.{2})$/,
"$1******$3",
),
}
: {}),
currentStep: ClaimWorkflowStep.CAPTURE_PART_DAMAGES,
nextStep: ClaimWorkflowStep.UPLOAD_REQUIRED_DOCUMENTS,
message:

View File

@@ -106,14 +106,16 @@ export class SelectOtherPartsV2ResponseDto {
@ApiProperty({
description: 'Sheba number (masked for security)',
example: 'IR12************1234',
required: false,
})
shebaNumber: string;
shebaNumber?: string;
@ApiProperty({
description: 'National code of owner (masked)',
example: '12******90',
required: false,
})
nationalCodeOfOwner: string;
nationalCodeOfOwner?: string;
@ApiProperty({
description: 'Current workflow step',

View File

@@ -265,6 +265,21 @@ export class ClaimCase {
@Prop({ type: Types.ObjectId, index: true })
fileMakerApprovalActorId?: Types.ObjectId;
/**
* V5 split flow: number of times the FileMaker has rejected this claim.
* Capped at 2; on the 3rd attempt the reject endpoint returns 422 and
* the FileMaker must approve instead.
*/
@Prop({ type: Number, default: 0 })
fileMakerRejectionCount?: number;
/**
* V5 split flow: reason text from the most recent FileMaker rejection.
* Overwritten on each rejection.
*/
@Prop({ type: String })
fileMakerRejectionReason?: string;
/**
* Legacy fields kept optional to simplify progressive migration.
* If you choose to migrate later, we can remove these.

View File

@@ -53,6 +53,7 @@ import {
CapturePartV2ResponseDto,
} from "./dto/capture-part-v2.dto";
import { GetCaptureRequirementsV2ResponseDto } from "./dto/capture-requirements-v2.dto";
import { Role } from "src/common/auth/enums";
/**
* Expert-initiated claim flow that mirrors the normal user claim API
@@ -71,7 +72,7 @@ import { GetCaptureRequirementsV2ResponseDto } from "./dto/capture-requirements-
@Controller("v2/expert-initiated/claim-request-management")
@ApiBearerAuth()
@UseGuards(LocalActorAuthGuard, RolesGuard)
@Roles(RoleEnum.FIELD_EXPERT)
@Roles(RoleEnum.FIELD_EXPERT, RoleEnum.FILE_MAKER, RoleEnum.FILE_REVIEWER)
export class ExpertInitiatedClaimMirrorController {
constructor(
private readonly claimRequestManagementService: ClaimRequestManagementService,
@@ -81,7 +82,8 @@ export class ExpertInitiatedClaimMirrorController {
@Post("create-from-blame/:blameRequestId")
@ApiParam({ name: "blameRequestId" })
@ApiOperation({
summary: "[Expert mirror] Create claim from expert-initiated IN_PERSON blame",
summary:
"[Expert mirror] Create claim from expert-initiated IN_PERSON blame",
description:
"Blame must be COMPLETED. Creates a ClaimCase owned by the damaged (non-guilty) party.",
})
@@ -173,8 +175,7 @@ export class ExpertInitiatedClaimMirrorController {
})
@ApiBody({
type: SelectOuterPartsV2Dto,
description:
"Selected vehicle type + selected outer part IDs from catalog",
description: "Selected vehicle type + selected outer part IDs from catalog",
examples: {
example1: {
summary: "Sedan - minor front damage",
@@ -211,7 +212,10 @@ export class ExpertInitiatedClaimMirrorController {
description: "Outer parts selected successfully",
type: SelectOuterPartsV2ResponseDto,
})
@ApiResponse({ status: 400, description: "Invalid workflow step or validation failed" })
@ApiResponse({
status: 400,
description: "Invalid workflow step or validation failed",
})
@ApiResponse({ status: 404, description: "Claim case not found" })
@ApiResponse({ status: 409, description: "Outer parts already selected" })
async selectOuterParts(
@@ -296,9 +300,15 @@ Optional: upload car green card file in the same step.
description: "Other parts and bank information saved successfully",
type: SelectOtherPartsV2ResponseDto,
})
@ApiResponse({ status: 400, description: "Invalid workflow step or validation failed" })
@ApiResponse({
status: 400,
description: "Invalid workflow step or validation failed",
})
@ApiResponse({ status: 404, description: "Claim case not found" })
@ApiResponse({ status: 409, description: "Bank information already submitted" })
@ApiResponse({
status: 409,
description: "Bank information already submitted",
})
async selectOtherParts(
@Param("claimRequestId") claimRequestId: string,
@Body() body: SelectOtherPartsV2Dto,
@@ -530,14 +540,22 @@ Returns status of each item (uploaded/captured or not).
type: "object",
required: ["sign", "agree", "branchId"],
properties: {
sign: { type: "string", format: "binary", description: "Signature image" },
agree: { type: "boolean", description: "true to accept, false to reject" },
sign: {
type: "string",
format: "binary",
description: "Signature image",
},
agree: {
type: "boolean",
description: "true to accept, false to reject",
},
branchId: { type: "string", description: "Insurer branch ID" },
},
},
})
@ApiOperation({
summary: "Owner signature on expert pricing (Flow 3 — expert acts on behalf of user)",
summary:
"Owner signature on expert pricing (Flow 3 — expert acts on behalf of user)",
description:
"Field expert submits the damaged party's signature during the final approval stage. " +
"Delegates to the same service method as the user sign endpoint; the expert's " +