forked from Yara724/api
feat: complete in-person claims without final sign
This commit is contained in:
@@ -37,12 +37,13 @@ class FileMakerRejectDto {
|
||||
* V5 FileMaker approval panel.
|
||||
*
|
||||
* After the full claim flow completes (FileReviewer does damage assessment via
|
||||
* expert-claim APIs, user signs), the claim lands in WAITING_FOR_FILE_MAKER_APPROVAL.
|
||||
* expert-claim APIs and any required factors are validated), the claim lands in
|
||||
* WAITING_FOR_FILE_MAKER_APPROVAL.
|
||||
* The FileMaker who created the blame file can then:
|
||||
*
|
||||
* approve → triggers fanavaran submission (claim → COMPLETED)
|
||||
* approve → claim → COMPLETED; an expert submits to Fanavaran manually
|
||||
* reject → sends claim back to WAITING_FOR_DAMAGE_EXPERT so the FileReviewer
|
||||
* can re-lock, adjust pricing, and redo the user interaction
|
||||
* can re-lock, adjust pricing, and redo the claim work
|
||||
*
|
||||
* All endpoints operate on `claimRequestId` (the claim case ID, not the blame ID).
|
||||
* Use `GET v5/file-maker/blame-request-management/claim-id/:requestId` to obtain
|
||||
@@ -65,9 +66,8 @@ export class FileMakerClaimApprovalV5Controller {
|
||||
summary: "Approve the completed claim (FileMaker V5)",
|
||||
description:
|
||||
"Approves a claim that is in `WAITING_FOR_FILE_MAKER_APPROVAL` status. " +
|
||||
"Moves the claim to `INSURER_REVIEW_AWAITING_OWNER_SIGN` and sends the owner an SMS " +
|
||||
"with a signature link. Fanavaran submission is triggered automatically after the " +
|
||||
"owner signs.",
|
||||
"Moves the claim to `COMPLETED`. No final owner signature is requested and " +
|
||||
"Fanavaran submission remains a manual expert action.",
|
||||
})
|
||||
async approve(
|
||||
@Param("claimRequestId") claimRequestId: string,
|
||||
|
||||
56
src/request-management/file-maker-claim-approval-v5.spec.ts
Normal file
56
src/request-management/file-maker-claim-approval-v5.spec.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import { Types } from "mongoose";
|
||||
import { ClaimCaseStatus } from "src/Types&Enums/claim-request-management/claim-case-status.enum";
|
||||
import { RoleEnum } from "src/Types&Enums/role.enum";
|
||||
import { RequestManagementService } from "./request-management.service";
|
||||
|
||||
describe("RequestManagementService V5 FileMaker approval", () => {
|
||||
it("completes the claim without notifying the owner for a final signature", async () => {
|
||||
const fileMakerId = new Types.ObjectId();
|
||||
const claimId = new Types.ObjectId();
|
||||
const claimCaseDbService = {
|
||||
findById: jest.fn().mockResolvedValue({
|
||||
_id: claimId,
|
||||
publicId: "CLM-V5",
|
||||
status: ClaimCaseStatus.WAITING_FOR_FILE_MAKER_APPROVAL,
|
||||
requiresFileMakerApproval: true,
|
||||
fileMakerApprovalActorId: fileMakerId,
|
||||
}),
|
||||
findByIdAndUpdate: jest.fn().mockResolvedValue({}),
|
||||
};
|
||||
const service = new (RequestManagementService as any)(
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
claimCaseDbService,
|
||||
) as RequestManagementService;
|
||||
|
||||
const result = await service.fileMakerApproveV5(
|
||||
{ sub: String(fileMakerId), role: RoleEnum.FILE_MAKER },
|
||||
String(claimId),
|
||||
);
|
||||
|
||||
expect(result.status).toBe(ClaimCaseStatus.COMPLETED);
|
||||
expect(result.message).toContain("Submit it to Fanavaran manually");
|
||||
expect(claimCaseDbService.findByIdAndUpdate).toHaveBeenCalledWith(
|
||||
String(claimId),
|
||||
expect.objectContaining({
|
||||
$set: expect.objectContaining({
|
||||
status: ClaimCaseStatus.COMPLETED,
|
||||
requiresFileMakerApproval: false,
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -344,7 +344,7 @@ export class FileReviewerBlameV4Controller {
|
||||
);
|
||||
}
|
||||
|
||||
// ─── Owner signature on expert pricing ───────────────────────────────────────
|
||||
// ─── Mixed-factor priced-line signature ──────────────────────────────────────
|
||||
|
||||
@Put("claim-sign/:claimRequestId")
|
||||
@ApiParam({ name: "claimRequestId" })
|
||||
@@ -362,11 +362,12 @@ export class FileReviewerBlameV4Controller {
|
||||
},
|
||||
})
|
||||
@ApiOperation({
|
||||
summary: "Owner signature on expert pricing (V4 — FileReviewer acts on behalf of user)",
|
||||
summary: "Priced-line acceptance before factor uploads (V4 — FileReviewer acts for user)",
|
||||
description:
|
||||
"FileReviewer submits the damaged party's signature during the final approval stage. " +
|
||||
"Delegates to the same service method as the user sign endpoint; the FileReviewer's " +
|
||||
"identity is resolved to the claim owner via `resolveClaimEffectiveUserId`.",
|
||||
"For mixed priced/factor claims only, FileReviewer records the damaged party's " +
|
||||
"acceptance of priced lines before factor uploads. V2–V5 no longer require a final " +
|
||||
"owner signature: they complete after expert work. The FileReviewer's identity is " +
|
||||
"resolved to the claim owner via `resolveClaimEffectiveUserId`.",
|
||||
})
|
||||
@UseInterceptors(
|
||||
FileInterceptor("sign", {
|
||||
|
||||
@@ -342,7 +342,7 @@ export class FileReviewerBlameV5Controller {
|
||||
);
|
||||
}
|
||||
|
||||
// ─── Owner signature on expert pricing ───────────────────────────────────────
|
||||
// ─── Mixed-factor priced-line signature ──────────────────────────────────────
|
||||
|
||||
@Put("claim-sign/:claimRequestId")
|
||||
@ApiParam({ name: "claimRequestId" })
|
||||
@@ -360,11 +360,12 @@ export class FileReviewerBlameV5Controller {
|
||||
},
|
||||
})
|
||||
@ApiOperation({
|
||||
summary: "Owner signature on expert pricing (V5 — FileReviewer acts on behalf of user)",
|
||||
summary: "Priced-line acceptance before factor uploads (V5 — FileReviewer acts for user)",
|
||||
description:
|
||||
"FileReviewer submits the damaged party's signature during the final approval stage. " +
|
||||
"Delegates to the same service method as the user sign endpoint; the FileReviewer's " +
|
||||
"identity is resolved to the claim owner via `resolveClaimEffectiveUserId`.",
|
||||
"For mixed priced/factor claims only, FileReviewer records the damaged party's " +
|
||||
"acceptance of priced lines before factor uploads. V2–V5 no longer require a final " +
|
||||
"owner signature: they complete after expert work. The FileReviewer's identity is " +
|
||||
"resolved to the claim owner via `resolveClaimEffectiveUserId`.",
|
||||
})
|
||||
@UseInterceptors(
|
||||
FileInterceptor("sign", {
|
||||
|
||||
@@ -10462,9 +10462,9 @@ export class RequestManagementService {
|
||||
/**
|
||||
* V5 variant of expertUploadBlameVideoV3.
|
||||
* Same flow as V3/V4 but additionally marks the linked claim with
|
||||
* `requiresFileMakerApproval: true` so that after the owner signs,
|
||||
* the claim is held at WAITING_FOR_FILE_MAKER_APPROVAL rather than
|
||||
* being auto-submitted to fanavaran.
|
||||
* `requiresFileMakerApproval: true` so that after expert work completes,
|
||||
* the claim is held at WAITING_FOR_FILE_MAKER_APPROVAL. FileMaker approval
|
||||
* completes the claim; Fanavaran submission is manual.
|
||||
*/
|
||||
async expertUploadBlameVideoV5(
|
||||
expert: any,
|
||||
@@ -10612,7 +10612,7 @@ export class RequestManagementService {
|
||||
videoId,
|
||||
status: req.status,
|
||||
message: file
|
||||
? "Blame accident video uploaded. File is now in expert review queue. After the full claim flow completes and the owner signs, the FileMaker must approve before fanavaran submission."
|
||||
? "Blame accident video uploaded. File is now in expert review queue. After the full claim flow completes, the FileMaker must approve before manual Fanavaran submission."
|
||||
: "File completed. Claim is now ready for damage expert review.",
|
||||
};
|
||||
}
|
||||
@@ -10625,7 +10625,7 @@ export class RequestManagementService {
|
||||
* - claim.status === WAITING_FOR_FILE_MAKER_APPROVAL
|
||||
* - actor is FILE_MAKER and is the original creator of the linked blame file
|
||||
*
|
||||
* On approval: triggers fanavaran submission and moves claim to COMPLETED.
|
||||
* On approval: completes the claim. Fanavaran submission is manual.
|
||||
*/
|
||||
async fileMakerApproveV5(
|
||||
fileMaker: any,
|
||||
@@ -10669,16 +10669,14 @@ export class RequestManagementService {
|
||||
metadata: {},
|
||||
};
|
||||
|
||||
// V5 approval: move claim to INSURER_REVIEW_AWAITING_OWNER_SIGN so the
|
||||
// owner can now sign. Clear requiresFileMakerApproval so that when the
|
||||
// owner later signs and autoSubmitToFanavaranV2OnClaimCompleted runs, it
|
||||
// does not re-intercept the claim as a pending V5 gate.
|
||||
// V5 approval is the final V5 gate. The damaged-party final signature is
|
||||
// no longer required, and Fanavaran submission is intentionally manual.
|
||||
await this.claimCaseDbService.findByIdAndUpdate(claimRequestId, {
|
||||
$set: {
|
||||
status: ClaimCaseStatus.INSURER_REVIEW_AWAITING_OWNER_SIGN,
|
||||
status: ClaimCaseStatus.COMPLETED,
|
||||
claimStatus: ClaimStatus.APPROVED,
|
||||
requiresFileMakerApproval: false,
|
||||
"workflow.currentStep": ClaimWorkflowStep.INSURER_REVIEW,
|
||||
"workflow.currentStep": ClaimWorkflowStep.CLAIM_COMPLETED,
|
||||
"workflow.nextStep": ClaimWorkflowStep.CLAIM_COMPLETED,
|
||||
},
|
||||
$push: {
|
||||
@@ -10686,36 +10684,12 @@ export class RequestManagementService {
|
||||
},
|
||||
});
|
||||
|
||||
// Notify the owner that the claim is ready for their signature.
|
||||
const notifyUserId = (claim as any).damagedPartyUserId ?? (claim as any).owner?.userId;
|
||||
if (notifyUserId && claim.blameRequestId) {
|
||||
const blame = await this.blameRequestDbService.findById(
|
||||
String(claim.blameRequestId),
|
||||
);
|
||||
const ownerParty = (blame?.parties || []).find(
|
||||
(p: any) =>
|
||||
p?.person?.userId && String(p.person.userId) === String(notifyUserId),
|
||||
);
|
||||
const ownerPhone = ownerParty?.person?.phoneNumber
|
||||
?? (await this.userDbService.findOne({ _id: new Types.ObjectId(String(notifyUserId)) }))?.mobile;
|
||||
if (ownerPhone && typeof ownerPhone === "string") {
|
||||
await this.smsOrchestrationService.sendSignatureReviewNotice({
|
||||
receptor: ownerPhone,
|
||||
fileKind: "claim",
|
||||
publicId: claim.publicId,
|
||||
expertLastName: actorName.split(/\s+/).pop() || "کارشناس",
|
||||
link: this.smsOrchestrationService.buildClaimLink(claimRequestId, "v2"),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
claimRequestId,
|
||||
publicId: claim.publicId,
|
||||
status: ClaimCaseStatus.INSURER_REVIEW_AWAITING_OWNER_SIGN,
|
||||
status: ClaimCaseStatus.COMPLETED,
|
||||
message:
|
||||
"Claim approved by FileMaker. Owner has been notified to sign. " +
|
||||
"Once the owner signs, the claim will be completed and submitted to Fanavaran.",
|
||||
"Claim approved by FileMaker and completed. Submit it to Fanavaran manually when ready.",
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user