diff --git a/src/expert-claim/expert-claim.service.ts b/src/expert-claim/expert-claim.service.ts index b142f01..2b1fb7c 100644 --- a/src/expert-claim/expert-claim.service.ts +++ b/src/expert-claim/expert-claim.service.ts @@ -378,7 +378,10 @@ export class ExpertClaimService { return this.blameRequestDbService.findById(id); } - private async assertExpertActorOnClaim(claim: any, actor: any): Promise { + private async assertExpertActorOnClaim( + claim: any, + actor: any, + ): Promise { const blame = await this.loadBlameForClaim(claim); assertClaimCaseForExpertActor(claim, actor, blame); } @@ -2462,9 +2465,15 @@ export class ExpertClaimService { */ async assignClaimForReviewV2( claimRequestId: string, - actor: { sub: string; fullName?: string; clientKey?: string; role?: string }, + actor: { + sub: string; + fullName?: string; + clientKey?: string; + role?: string; + }, ): Promise { - if ((actor as any).role !== RoleEnum.FIELD_EXPERT) requireActorClientKey(actor); + if ((actor as any).role !== RoleEnum.FIELD_EXPERT) + requireActorClientKey(actor); await this.expireClaimWorkflowLockV2IfStale(claimRequestId); const claim = await this.claimCaseDbService.findById(claimRequestId); @@ -3003,7 +3012,9 @@ export class ExpertClaimService { if (priceCap !== null) { let totalPrice = 0; for (const part of reply.parts || []) { - const parsed = this.parsePersianNumber(String(part.totalPayment ?? "0")); + const parsed = this.parsePersianNumber( + String(part.totalPayment ?? "0"), + ); if (!isNaN(parsed)) totalPrice += parsed; } if (totalPrice > priceCap) { @@ -3492,10 +3503,11 @@ export class ExpertClaimService { const clientKey = requireActorClientKey(actor); const expertId = String(actor.sub); const expertOid = new Types.ObjectId(expertId); - const activityEvents = await this.expertFileActivityDbService.findByExpert( - expertId, - ExpertFileKind.CLAIM, - ); + const activityEvents = + await this.expertFileActivityDbService.findByExpert( + expertId, + ExpertFileKind.CLAIM, + ); const activityFileIds = expertPortfolioFileIdsFromActivityEvents(activityEvents); const orClauses: Record[] = [ @@ -3511,7 +3523,10 @@ export class ExpertClaimService { const rows = orClauses.length === 0 ? [] - : await this.claimCaseDbService.find({ $or: orClauses }, { lean: true }); + : await this.claimCaseDbService.find( + { $or: orClauses }, + { lean: true }, + ); const seen = new Set(); for (const doc of rows as Record[]) { const id = String(doc._id ?? ""); @@ -4234,54 +4249,54 @@ export class ExpertClaimService { } // Fall through to the detail build below (skip the damage-expert gate) } else { - const isResendPending = - claim.status === ClaimCaseStatus.WAITING_FOR_USER_RESEND; + const isResendPending = + claim.status === ClaimCaseStatus.WAITING_FOR_USER_RESEND; - const assignedForReviewById = String( - (claim.workflow as any)?.assignedForReviewBy?.actorId ?? "", - ); - const lockEnforced = this.isClaimV2WorkflowLockCurrentlyEnforced(claim); - const lockedById = String(claim.workflow?.lockedBy?.actorId ?? ""); - const isAssignedToMe = - !!assignedForReviewById && assignedForReviewById === actorId; - - // Gate: must satisfy at least one bucket - if ( - !isDamageExpertPhase && - !isFactorValidationPending && - !isResendPending && - !isAssignedToMe - ) { - throw new ForbiddenException( - `This claim is not available for expert review. Current status: ${claim.status}`, + const assignedForReviewById = String( + (claim.workflow as any)?.assignedForReviewBy?.actorId ?? "", ); - } + const lockEnforced = this.isClaimV2WorkflowLockCurrentlyEnforced(claim); + const lockedById = String(claim.workflow?.lockedBy?.actorId ?? ""); + const isAssignedToMe = + !!assignedForReviewById && assignedForReviewById === actorId; - const isAvailable = - isDamageExpertPhase && !assignedForReviewById && !lockEnforced; - const isLockedByMe = lockEnforced && lockedById === actorId; - const isFactorValidationOpen = isFactorValidationPending; - - if ( - !isAvailable && - !isAssignedToMe && - !isLockedByMe && - !isFactorValidationOpen - ) { - if (lockEnforced && lockedById && lockedById !== actorId) { + // Gate: must satisfy at least one bucket + if ( + !isDamageExpertPhase && + !isFactorValidationPending && + !isResendPending && + !isAssignedToMe + ) { throw new ForbiddenException( - `This claim is locked by another expert: ${claim.workflow?.lockedBy?.actorName}`, + `This claim is not available for expert review. Current status: ${claim.status}`, ); } - if (assignedForReviewById && assignedForReviewById !== actorId) { + + const isAvailable = + isDamageExpertPhase && !assignedForReviewById && !lockEnforced; + const isLockedByMe = lockEnforced && lockedById === actorId; + const isFactorValidationOpen = isFactorValidationPending; + + if ( + !isAvailable && + !isAssignedToMe && + !isLockedByMe && + !isFactorValidationOpen + ) { + if (lockEnforced && lockedById && lockedById !== actorId) { + throw new ForbiddenException( + `This claim is locked by another expert: ${claim.workflow?.lockedBy?.actorName}`, + ); + } + if (assignedForReviewById && assignedForReviewById !== actorId) { + throw new ForbiddenException( + "This claim has been assigned to another expert.", + ); + } throw new ForbiddenException( - "This claim has been assigned to another expert.", + "You do not have permission to view this claim.", ); } - throw new ForbiddenException( - "You do not have permission to view this claim.", - ); - } } // end damage-expert gate (else block) // Build requiredDocuments map @@ -4513,9 +4528,10 @@ export class ExpertClaimService { carAngles, damagedParts, awaitingFactorValidation: isFactorValidationPending, - evaluation: evaluationForApi as - | ClaimDetailV2ResponseDto["evaluation"] - | undefined, + evaluation: + (evaluationForApi as + | ClaimDetailV2ResponseDto["evaluation"] + | undefined) || (enrichedEvaluation as any), videoCapture, blameCase: blameCaseForApi, blameExpertDecision,