forked from Yara724/api
feat: complete in-person claims without final sign
This commit is contained in:
@@ -2223,10 +2223,16 @@ export class ExpertClaimService {
|
||||
actorType: string;
|
||||
},
|
||||
metadata: Record<string, unknown>,
|
||||
): Promise<void> {
|
||||
): Promise<ClaimCaseStatus> {
|
||||
// V5 retains the FileMaker approval gate, but no longer waits for a
|
||||
// damaged-party final signature after that approval.
|
||||
const completionStatus = (claimForTenant as any).requiresFileMakerApproval
|
||||
? ClaimCaseStatus.WAITING_FOR_FILE_MAKER_APPROVAL
|
||||
: ClaimCaseStatus.COMPLETED;
|
||||
|
||||
await this.claimCaseDbService.findByIdAndUpdate(claimRequestId, {
|
||||
$set: {
|
||||
status: ClaimCaseStatus.COMPLETED,
|
||||
status: completionStatus,
|
||||
claimStatus: ClaimStatus.APPROVED,
|
||||
"workflow.currentStep": ClaimWorkflowStep.CLAIM_COMPLETED,
|
||||
"workflow.nextStep": ClaimWorkflowStep.CLAIM_COMPLETED,
|
||||
@@ -2248,6 +2254,8 @@ export class ExpertClaimService {
|
||||
eventType: ExpertFileActivityType.HANDLED,
|
||||
idempotencyKey: `claim:${claimRequestId}:factor_validation:${historyType}:${actor.sub}`,
|
||||
});
|
||||
|
||||
return completionStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2420,7 +2428,7 @@ export class ExpertClaimService {
|
||||
};
|
||||
|
||||
if (anyRejected) {
|
||||
await this.completeClaimCaseAfterFactorValidationV2(
|
||||
const completionStatus = await this.completeClaimCaseAfterFactorValidationV2(
|
||||
claimRequestId,
|
||||
claim,
|
||||
actor,
|
||||
@@ -2428,28 +2436,19 @@ export class ExpertClaimService {
|
||||
historyActor,
|
||||
{ replyField },
|
||||
);
|
||||
const fanavaran =
|
||||
await this.claimRequestManagementService.autoSubmitToFanavaranV2OnClaimCompleted(
|
||||
claimRequestId,
|
||||
);
|
||||
// Expertise (stage 4) is intentionally NOT submitted here.
|
||||
// It is only triggered after the owner's final approval signature
|
||||
// (submitOwnerInsurerApprovalSignV2), because until that point the
|
||||
// expert review is not considered final — the user can still object.
|
||||
return {
|
||||
message: 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).",
|
||||
fanavaran,
|
||||
),
|
||||
message:
|
||||
completionStatus === ClaimCaseStatus.WAITING_FOR_FILE_MAKER_APPROVAL
|
||||
? "Factors were reviewed with expert repricing. The claim is awaiting FileMaker approval."
|
||||
: "Factors were reviewed with expert repricing. The claim is completed without a final owner signature.",
|
||||
claimRequestId,
|
||||
claimStatus: ClaimStatus.APPROVED,
|
||||
caseStatus: ClaimCaseStatus.COMPLETED,
|
||||
caseStatus: completionStatus,
|
||||
outcome: "REJECTED_REPRICED_AUTO_COMPLETED",
|
||||
fanavaran,
|
||||
};
|
||||
}
|
||||
|
||||
await this.completeClaimCaseAfterFactorValidationV2(
|
||||
const completionStatus = await this.completeClaimCaseAfterFactorValidationV2(
|
||||
claimRequestId,
|
||||
claim,
|
||||
actor,
|
||||
@@ -2458,25 +2457,15 @@ export class ExpertClaimService {
|
||||
{ replyField },
|
||||
);
|
||||
|
||||
const fanavaran =
|
||||
await this.claimRequestManagementService.autoSubmitToFanavaranV2OnClaimCompleted(
|
||||
claimRequestId,
|
||||
);
|
||||
// Expertise (stage 4) is intentionally NOT submitted here.
|
||||
// It is only triggered after the owner's final approval signature
|
||||
// (submitOwnerInsurerApprovalSignV2), because until that point the
|
||||
// expert review is not considered final — the user can still object.
|
||||
|
||||
return {
|
||||
message: this.appendFanavaranAutoSubmitToMessage(
|
||||
"All factors were approved by the expert. The claim is completed without an additional owner signature.",
|
||||
fanavaran,
|
||||
),
|
||||
message:
|
||||
completionStatus === ClaimCaseStatus.WAITING_FOR_FILE_MAKER_APPROVAL
|
||||
? "All factors were approved. The claim is awaiting FileMaker approval."
|
||||
: "All factors were approved by the expert. The claim is completed without a final owner signature.",
|
||||
claimRequestId,
|
||||
claimStatus: ClaimStatus.APPROVED,
|
||||
caseStatus: ClaimCaseStatus.COMPLETED,
|
||||
caseStatus: completionStatus,
|
||||
outcome: "ALL_APPROVED_AUTO_COMPLETED",
|
||||
fanavaran,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3230,9 +3219,9 @@ export class ExpertClaimService {
|
||||
* On success:
|
||||
* - Stores reply; clears owner signature fields (`ownerInsurerApproval`, `ownerPricedPartsApproval`)
|
||||
* - Unlocks the workflow
|
||||
* - Pricing-only (`factorNeeded=false` everywhere): INSURER_REVIEW_AWAITING_OWNER_SIGN, APPROVED, INSURER_REVIEW → owner final sign/reject.
|
||||
* - Pricing-only (`factorNeeded=false` everywhere): COMPLETED, APPROVED, CLAIM_COMPLETED — no final owner signature.
|
||||
* - All lines `factorNeeded`: NEEDS_REVISION, OWNER_UPLOAD_FACTOR_DOCUMENTS → uploads → UNDER_REVIEW, EXPERT_COST_EVALUATION (validate factors).
|
||||
* - Mixed priced + factor: NEEDS_REVISION, INSURER_REVIEW with next OWNER_UPLOAD_FACTOR_DOCUMENTS → owner signs priced lines first → upload factors → expert validates → final sign.
|
||||
* - Mixed priced + factor: NEEDS_REVISION, INSURER_REVIEW with next OWNER_UPLOAD_FACTOR_DOCUMENTS → owner signs priced lines first → upload factors → expert validates → complete.
|
||||
*/
|
||||
async submitExpertReplyV2(
|
||||
claimRequestId: string,
|
||||
@@ -3398,14 +3387,23 @@ export class ExpertClaimService {
|
||||
let currentStep = ClaimWorkflowStep.INSURER_REVIEW;
|
||||
let nextWorkflowStep = ClaimWorkflowStep.CLAIM_COMPLETED;
|
||||
|
||||
const nextCaseStatus = claimCaseStatusAfterExpertReplyV2(processedParts);
|
||||
const blameForFlow = claim.blameRequestId
|
||||
? await this.blameRequestDbService.findById(String(claim.blameRequestId))
|
||||
: undefined;
|
||||
const skipsFinalOwnerSignature =
|
||||
!!(blameForFlow as any)?.expertInitiated &&
|
||||
(blameForFlow as any)?.creationMethod === "IN_PERSON";
|
||||
const nextCaseStatus = claimCaseStatusAfterExpertReplyV2(
|
||||
processedParts,
|
||||
skipsFinalOwnerSignature,
|
||||
);
|
||||
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.
|
||||
// V5 retains a FileMaker approval gate after expert work. Expert-initiated
|
||||
// in-person V2–V5 flows do not request a final damaged-party signature.
|
||||
const isV5Claim = !!(claim as any).requiresFileMakerApproval;
|
||||
const completesWithoutFactors =
|
||||
!needsFactorUpload && !isV5Claim && skipsFinalOwnerSignature;
|
||||
|
||||
if (needsFactorUpload) {
|
||||
nextClaimStatus = ClaimStatus.NEEDS_REVISION;
|
||||
@@ -3418,7 +3416,10 @@ export class ExpertClaimService {
|
||||
}
|
||||
} else if (isV5Claim) {
|
||||
// V5, no factors: hold at WAITING_FOR_FILE_MAKER_APPROVAL
|
||||
currentStep = ClaimWorkflowStep.INSURER_REVIEW;
|
||||
currentStep = ClaimWorkflowStep.CLAIM_COMPLETED;
|
||||
nextWorkflowStep = ClaimWorkflowStep.CLAIM_COMPLETED;
|
||||
} else if (completesWithoutFactors) {
|
||||
currentStep = ClaimWorkflowStep.CLAIM_COMPLETED;
|
||||
nextWorkflowStep = ClaimWorkflowStep.CLAIM_COMPLETED;
|
||||
}
|
||||
|
||||
@@ -3436,10 +3437,11 @@ export class ExpertClaimService {
|
||||
}
|
||||
}
|
||||
|
||||
const updatePayload: Record<string, unknown> = {
|
||||
status: isV5Claim && !needsFactorUpload
|
||||
const persistedCaseStatus = isV5Claim && !needsFactorUpload
|
||||
? ClaimCaseStatus.WAITING_FOR_FILE_MAKER_APPROVAL
|
||||
: nextCaseStatus,
|
||||
: nextCaseStatus;
|
||||
const updatePayload: Record<string, unknown> = {
|
||||
status: persistedCaseStatus,
|
||||
claimStatus: nextClaimStatus,
|
||||
...(expertAddedParts.length > 0
|
||||
? { "damage.selectedParts": mergedSelectedParts }
|
||||
@@ -3454,9 +3456,7 @@ export class ExpertClaimService {
|
||||
"evaluation.ownerPricedPartsApproval": "",
|
||||
},
|
||||
"workflow.currentStep": currentStep,
|
||||
"workflow.nextStep": needsFactorUpload
|
||||
? nextWorkflowStep
|
||||
: ClaimWorkflowStep.CLAIM_COMPLETED,
|
||||
"workflow.nextStep": nextWorkflowStep,
|
||||
[`evaluation.${replyField}`]: replyPayload,
|
||||
$push: {
|
||||
"workflow.completedSteps": completedStep,
|
||||
@@ -3495,30 +3495,36 @@ export class ExpertClaimService {
|
||||
idempotencyKey: `claim:${claimRequestId}:handled:${actor.sub}`,
|
||||
});
|
||||
|
||||
// V5: owner SMS is sent by fileMakerApproveV5 after FileMaker approval.
|
||||
const ownerPhoneNotify = await this.resolveClaimOwnerPhone(claim);
|
||||
if (ownerPhoneNotify && !needsFactorUpload && !isV5Claim) {
|
||||
const expertLastName =
|
||||
actor?.fullName?.trim()?.split(/\s+/).pop() || "کارشناس";
|
||||
await this.smsOrchestrationService.sendSignatureReviewNotice({
|
||||
receptor: ownerPhoneNotify,
|
||||
fileKind: "claim",
|
||||
publicId: claim.publicId,
|
||||
expertLastName,
|
||||
link: this.smsOrchestrationService.buildClaimLink(String(claim._id), "v2"),
|
||||
});
|
||||
// V1 retains its existing final owner-signature notification. V2–V5 do not
|
||||
// send this notification because their completion policy is manual Fanavaran
|
||||
// submission after expert work (and FileMaker approval for V5).
|
||||
if (!needsFactorUpload && !isV5Claim && !skipsFinalOwnerSignature) {
|
||||
const ownerPhoneNotify = await this.resolveClaimOwnerPhone(claim);
|
||||
if (ownerPhoneNotify) {
|
||||
const expertLastName =
|
||||
actor?.fullName?.trim()?.split(/\s+/).pop() || "کارشناس";
|
||||
await this.smsOrchestrationService.sendSignatureReviewNotice({
|
||||
receptor: ownerPhoneNotify,
|
||||
fileKind: "claim",
|
||||
publicId: claim.publicId,
|
||||
expertLastName,
|
||||
link: this.smsOrchestrationService.buildClaimLink(
|
||||
String(claim._id),
|
||||
"v2",
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Fanavaran expertise is now triggered on owner final sign, not here.
|
||||
// V2–V5 completion and FileMaker approval do not trigger a Fanavaran
|
||||
// submission; the expert submits manually when ready.
|
||||
|
||||
return {
|
||||
claimRequestId,
|
||||
status: nextCaseStatus,
|
||||
status: persistedCaseStatus,
|
||||
claimStatus: nextClaimStatus,
|
||||
currentStep,
|
||||
workflowNextStep: needsFactorUpload
|
||||
? nextWorkflowStep
|
||||
: ClaimWorkflowStep.CLAIM_COMPLETED,
|
||||
workflowNextStep: nextWorkflowStep,
|
||||
factorNeeded: needsFactorUpload,
|
||||
mixedPricingAndFactors: mixedFactorAndPrice,
|
||||
allPartsFactorNeeded: !!needsFactorUpload && !!allFactorLines,
|
||||
|
||||
Reference in New Issue
Block a user