Fixed flow steps of v5

This commit is contained in:
SepehrYahyaee
2026-07-29 10:51:42 +03:30
parent 637cbb20d5
commit b0c7a0890c
3 changed files with 54 additions and 23 deletions

View File

@@ -971,7 +971,7 @@ export class ExpertClaimService {
daghi: { daghi: {
option: part.daghi.option, option: part.daghi.option,
...(part.daghi.price && { price: part.daghi.price }), ...(part.daghi.price && { price: part.daghi.price }),
...(part.daghi.branchId && { ...(part.daghi.branchId && Types.ObjectId.isValid(part.daghi.branchId) && {
branchId: new Types.ObjectId(part.daghi.branchId), branchId: new Types.ObjectId(part.daghi.branchId),
}), }),
}, },
@@ -3382,6 +3382,12 @@ export class ExpertClaimService {
const nextCaseStatus = claimCaseStatusAfterExpertReplyV2(processedParts); const nextCaseStatus = claimCaseStatusAfterExpertReplyV2(processedParts);
let nextClaimStatus = ClaimStatus.APPROVED; let nextClaimStatus = ClaimStatus.APPROVED;
// V5 flow: FileMaker must approve before the owner is asked to sign.
// When no factor upload is needed, skip the owner-sign step entirely here
// and park the claim at WAITING_FOR_FILE_MAKER_APPROVAL. The owner SMS
// and INSURER_REVIEW status are emitted by fileMakerApproveV5 instead.
const isV5Claim = !!(claim as any).requiresFileMakerApproval;
if (needsFactorUpload) { if (needsFactorUpload) {
nextClaimStatus = ClaimStatus.NEEDS_REVISION; nextClaimStatus = ClaimStatus.NEEDS_REVISION;
if (mixedFactorAndPrice) { if (mixedFactorAndPrice) {
@@ -3391,6 +3397,10 @@ export class ExpertClaimService {
currentStep = ClaimWorkflowStep.OWNER_UPLOAD_FACTOR_DOCUMENTS; currentStep = ClaimWorkflowStep.OWNER_UPLOAD_FACTOR_DOCUMENTS;
nextWorkflowStep = ClaimWorkflowStep.EXPERT_COST_EVALUATION; nextWorkflowStep = ClaimWorkflowStep.EXPERT_COST_EVALUATION;
} }
} else if (isV5Claim) {
// V5, no factors: hold at WAITING_FOR_FILE_MAKER_APPROVAL
currentStep = ClaimWorkflowStep.INSURER_REVIEW;
nextWorkflowStep = ClaimWorkflowStep.CLAIM_COMPLETED;
} }
const mergedSelectedParts = [...ownerSelectedParts]; const mergedSelectedParts = [...ownerSelectedParts];
@@ -3408,7 +3418,9 @@ export class ExpertClaimService {
} }
const updatePayload: Record<string, unknown> = { const updatePayload: Record<string, unknown> = {
status: nextCaseStatus, status: isV5Claim && !needsFactorUpload
? ClaimCaseStatus.WAITING_FOR_FILE_MAKER_APPROVAL
: nextCaseStatus,
claimStatus: nextClaimStatus, claimStatus: nextClaimStatus,
...(expertAddedParts.length > 0 ...(expertAddedParts.length > 0
? { "damage.selectedParts": mergedSelectedParts } ? { "damage.selectedParts": mergedSelectedParts }
@@ -3464,8 +3476,9 @@ export class ExpertClaimService {
idempotencyKey: `claim:${claimRequestId}:handled:${actor.sub}`, idempotencyKey: `claim:${claimRequestId}:handled:${actor.sub}`,
}); });
// V5: owner SMS is sent by fileMakerApproveV5 after FileMaker approval.
const ownerPhoneNotify = await this.resolveClaimOwnerPhone(claim); const ownerPhoneNotify = await this.resolveClaimOwnerPhone(claim);
if (ownerPhoneNotify && !needsFactorUpload) { if (ownerPhoneNotify && !needsFactorUpload && !isV5Claim) {
const expertLastName = const expertLastName =
actor?.fullName?.trim()?.split(/\s+/).pop() || "کارشناس"; actor?.fullName?.trim()?.split(/\s+/).pop() || "کارشناس";
await this.smsOrchestrationService.sendSignatureReviewNotice({ await this.smsOrchestrationService.sendSignatureReviewNotice({
@@ -3473,7 +3486,7 @@ export class ExpertClaimService {
fileKind: "claim", fileKind: "claim",
publicId: claim.publicId, publicId: claim.publicId,
expertLastName, expertLastName,
link: this.smsOrchestrationService.buildClaimLink(String(claim._id), "v1"), link: this.smsOrchestrationService.buildClaimLink(String(claim._id), "v2"),
}); });
} }

View File

@@ -64,26 +64,19 @@ export class FileMakerClaimApprovalV5Controller {
@ApiOperation({ @ApiOperation({
summary: "Approve the completed claim (FileMaker V5)", summary: "Approve the completed claim (FileMaker V5)",
description: description:
"Approves a claim that is in WAITING_FOR_FILE_MAKER_APPROVAL status. " + "Approves a claim that is in `WAITING_FOR_FILE_MAKER_APPROVAL` status. " +
"Marks the claim COMPLETED and triggers fanavaran submission.", "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.",
}) })
async approve( async approve(
@Param("claimRequestId") claimRequestId: string, @Param("claimRequestId") claimRequestId: string,
@CurrentUser() fileMaker: any, @CurrentUser() fileMaker: any,
) { ) {
const result = await this.requestManagementService.fileMakerApproveV5( return this.requestManagementService.fileMakerApproveV5(
fileMaker, fileMaker,
claimRequestId, claimRequestId,
); );
// Trigger fanavaran auto-submit now that the claim is COMPLETED and the gate
// (requiresFileMakerApproval) has been cleared.
const fanavaran =
await this.claimRequestManagementService.autoSubmitToFanavaranV2OnClaimCompleted(
claimRequestId,
);
return { ...result, fanavaran };
} }
@Post("reject/:claimRequestId") @Post("reject/:claimRequestId")

View File

@@ -10389,28 +10389,53 @@ export class RequestManagementService {
metadata: {}, metadata: {},
}; };
// Move claim to COMPLETED and clear the approval gate // 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.
await this.claimCaseDbService.findByIdAndUpdate(claimRequestId, { await this.claimCaseDbService.findByIdAndUpdate(claimRequestId, {
$set: { $set: {
status: ClaimCaseStatus.COMPLETED, status: ClaimCaseStatus.INSURER_REVIEW_AWAITING_OWNER_SIGN,
claimStatus: ClaimStatus.APPROVED, claimStatus: ClaimStatus.APPROVED,
requiresFileMakerApproval: false, requiresFileMakerApproval: false,
"workflow.currentStep": ClaimWorkflowStep.CLAIM_COMPLETED, "workflow.currentStep": ClaimWorkflowStep.INSURER_REVIEW,
"workflow.nextStep": ClaimWorkflowStep.CLAIM_COMPLETED, "workflow.nextStep": ClaimWorkflowStep.CLAIM_COMPLETED,
}, },
$push: { $push: {
"workflow.completedSteps": ClaimWorkflowStep.INSURER_REVIEW,
history: historyEntry, history: historyEntry,
}, },
}); });
// 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 { return {
claimRequestId, claimRequestId,
publicId: claim.publicId, publicId: claim.publicId,
status: ClaimCaseStatus.COMPLETED, status: ClaimCaseStatus.INSURER_REVIEW_AWAITING_OWNER_SIGN,
message: message:
"Claim approved by FileMaker. Claim is now marked COMPLETED. " + "Claim approved by FileMaker. Owner has been notified to sign. " +
"Proceed with fanavaran submission via the claim service.", "Once the owner signs, the claim will be completed and submitted to Fanavaran.",
}; };
} }