Fixed Lock for Field expert + user view of files

This commit is contained in:
2026-06-18 13:31:56 +03:30
parent 084d0e1360
commit fa188862e5
8 changed files with 328 additions and 234 deletions

View File

@@ -2437,12 +2437,35 @@ export class ExpertClaimService {
assertClaimCaseForExpertActor(claim, actor);
const isFieldExpertOwner =
(actor as any).role === RoleEnum.FIELD_EXPERT &&
claimCaseInitiatedByFieldExpert(claim, actor);
const isFactorValidationLock =
claimIsAwaitingExpertFactorValidationV2(claim);
const isDamageReviewLock =
claim.status === ClaimCaseStatus.WAITING_FOR_DAMAGE_EXPERT;
const isExpertReviewingReentry =
claim.status === ClaimCaseStatus.EXPERT_REVIEWING;
const isResendPending =
claim.status === ClaimCaseStatus.WAITING_FOR_USER_RESEND;
const isTerminalClaim =
claim.status === ClaimCaseStatus.COMPLETED ||
claim.status === ClaimCaseStatus.CANCELLED;
const isFieldExpertPortfolioLock =
isFieldExpertOwner &&
!isTerminalClaim &&
!isDamageReviewLock &&
!isFactorValidationLock &&
!isExpertReviewingReentry &&
!isResendPending;
if (!isDamageReviewLock && !isFactorValidationLock) {
if (
!isDamageReviewLock &&
!isFactorValidationLock &&
!isExpertReviewingReentry &&
!isResendPending &&
!isFieldExpertPortfolioLock
) {
throw new BadRequestException({
success: false,
status: "unavailable" satisfies ExpertFileAssignStatus,
@@ -2490,11 +2513,15 @@ export class ExpertClaimService {
const now = new Date();
const lockSnapshot = await this.snapshotDamageExpert(actor.sub);
const expiredAt = new Date(now.getTime() + this.claimV2WorkflowLockTtlMs);
const actorType = isFieldExpertOwner ? "field_expert" : "damage_expert";
const actorRoleLabel = isFieldExpertOwner
? ("field_expert" as const)
: ("damage_expert" as const);
const lockedByPayload = {
actorId: expertOid,
actorName: actor.fullName,
actorRole: "damage_expert" as const,
actorRole: actorRoleLabel,
...(lockSnapshot && { expertProfileSnapshot: lockSnapshot }),
};
@@ -2503,34 +2530,40 @@ export class ExpertClaimService {
"workflow.lockedAt": now,
"workflow.expiredAt": expiredAt,
"workflow.lockedBy": lockedByPayload,
"workflow.preLockQueueSnapshot": {
claimStatus: claim.claimStatus,
currentStep: this.normalizeClaimWorkflowStepForSnapshot(
claim.workflow?.currentStep,
ClaimWorkflowStep.USER_SUBMISSION_COMPLETE,
),
...(claim.workflow?.nextStep != null
? {
nextStep: this.normalizeClaimWorkflowStepForSnapshot(
claim.workflow.nextStep,
ClaimWorkflowStep.EXPERT_DAMAGE_ASSESSMENT,
...(isDamageReviewLock || isExpertReviewingReentry || isResendPending
? {
"workflow.preLockQueueSnapshot": {
claimStatus: claim.claimStatus,
currentStep: this.normalizeClaimWorkflowStepForSnapshot(
claim.workflow?.currentStep,
ClaimWorkflowStep.USER_SUBMISSION_COMPLETE,
),
}
: {}),
},
...(claim.workflow?.nextStep != null
? {
nextStep: this.normalizeClaimWorkflowStepForSnapshot(
claim.workflow.nextStep,
ClaimWorkflowStep.EXPERT_DAMAGE_ASSESSMENT,
),
}
: {}),
},
}
: {}),
$push: {
history: {
type: "CLAIM_ASSIGNED",
actor: {
actorId: expertOid,
actorName: actor.fullName,
actorType: "damage_expert",
actorType,
},
timestamp: now,
metadata: {
note: isFactorValidationLock
? "Expert assigned for factor validation review"
: "Expert assigned via review assign endpoint",
: isFieldExpertPortfolioLock
? "Field expert portfolio lock (no status change)"
: "Expert assigned via review assign endpoint",
...(isFactorValidationLock ? { phase: "factorValidation" } : {}),
},
},
@@ -2541,14 +2574,18 @@ export class ExpertClaimService {
baseLockUpdate["workflow.assignedForReviewBy"] = lockedByPayload;
}
const lockUpdate: Record<string, unknown> = isFactorValidationLock
? baseLockUpdate
: {
...baseLockUpdate,
status: ClaimCaseStatus.EXPERT_REVIEWING,
claimStatus: ClaimStatus.UNDER_REVIEW,
"workflow.currentStep": ClaimWorkflowStep.EXPERT_DAMAGE_ASSESSMENT,
};
const lockUpdate: Record<string, unknown> =
isFactorValidationLock ||
isExpertReviewingReentry ||
isResendPending ||
isFieldExpertPortfolioLock
? baseLockUpdate
: {
...baseLockUpdate,
status: ClaimCaseStatus.EXPERT_REVIEWING,
claimStatus: ClaimStatus.UNDER_REVIEW,
"workflow.currentStep": ClaimWorkflowStep.EXPERT_DAMAGE_ASSESSMENT,
};
const assignFilter: Record<string, unknown> = {
_id: new Types.ObjectId(claimRequestId),
@@ -2581,6 +2618,13 @@ export class ExpertClaimService {
ClaimCaseStatus.WAITING_FOR_INSURER_APPROVAL,
],
};
} else if (isExpertReviewingReentry) {
assignFilter.status = ClaimCaseStatus.EXPERT_REVIEWING;
} else if (isResendPending) {
assignFilter.status = ClaimCaseStatus.WAITING_FOR_USER_RESEND;
} else if (isFieldExpertPortfolioLock) {
assignFilter.initiatedByFieldExpertId = expertOid;
assignFilter.status = claim.status;
} else {
assignFilter.status = ClaimCaseStatus.WAITING_FOR_DAMAGE_EXPERT;
}
@@ -2634,7 +2678,13 @@ export class ExpertClaimService {
});
const ownerPhone = await this.resolveClaimOwnerPhone(updated);
if (ownerPhone) {
if (
ownerPhone &&
isDamageReviewLock &&
!isFieldExpertPortfolioLock &&
!isExpertReviewingReentry &&
!isResendPending
) {
const expertLastName =
actor?.fullName?.trim()?.split(/\s+/).pop() || "کارشناس";
await this.smsOrchestrationService.sendThirdPartyExpertStartedReviewNotice(