From 8419ec06ae2953529d28b8d1b4ee63363ee9b383 Mon Sep 17 00:00:00 2001 From: "s.yahyaee" Date: Fri, 1 May 2026 11:14:05 +0330 Subject: [PATCH] YARA-867 --- .../claim-request-management.v2.controller.ts | 2 +- src/expert-claim/dto/claim-detail-v2.dto.ts | 10 +++ src/expert-claim/expert-claim.service.ts | 68 ++++++++++++++++--- 3 files changed, 70 insertions(+), 10 deletions(-) diff --git a/src/claim-request-management/claim-request-management.v2.controller.ts b/src/claim-request-management/claim-request-management.v2.controller.ts index 4a315b1..718ca76 100644 --- a/src/claim-request-management/claim-request-management.v2.controller.ts +++ b/src/claim-request-management/claim-request-management.v2.controller.ts @@ -85,7 +85,7 @@ export class ClaimRequestManagementV2Controller { @ApiOperation({ summary: "Get Claim Details (V2)", description: - "Get claim details for current actor. USER receives only minimal own-needed payload (no extra owner/security fields). FIELD_EXPERT keeps full view for expert workflows.", + "Get claim details for current actor. USER (claim owner) receives the same core claim payload including `owner` (userId, clientId, optional userClientKey). FIELD_EXPERT additionally receives unmasked money and userRating when present.", }) @ApiResponse({ status: 200, diff --git a/src/expert-claim/dto/claim-detail-v2.dto.ts b/src/expert-claim/dto/claim-detail-v2.dto.ts index 45f9caf..b02b672 100644 --- a/src/expert-claim/dto/claim-detail-v2.dto.ts +++ b/src/expert-claim/dto/claim-detail-v2.dto.ts @@ -99,6 +99,16 @@ export class ClaimDetailV2ResponseDto { damageExpertReplyFinal?: unknown; }; + @ApiPropertyOptional({ + description: + 'Owner objection to expert-priced parts (`evaluation.objection`): disputed lines + optional new parts + submission time.', + }) + objection?: { + objectionParts?: Array>; + newParts?: Array>; + submittedAt?: Date | string; + }; + @ApiPropertyOptional({ description: 'Car walk-around video from `claim-video-capture` (resolved from `media.videoCaptureId`).', diff --git a/src/expert-claim/expert-claim.service.ts b/src/expert-claim/expert-claim.service.ts index b65a951..d6d9c8f 100644 --- a/src/expert-claim/expert-claim.service.ts +++ b/src/expert-claim/expert-claim.service.ts @@ -2723,6 +2723,11 @@ export class ExpertClaimService { status: ClaimCaseStatus.WAITING_FOR_DAMAGE_EXPERT, 'workflow.locked': { $ne: true }, }, + // Expert reviewing but lock cleared (e.g. TTL expiry) — still list until status is reconciled + { + status: ClaimCaseStatus.EXPERT_REVIEWING, + 'workflow.locked': { $ne: true }, + }, // This expert's own locked/in-progress claims { 'workflow.locked': true, @@ -2919,6 +2924,34 @@ export class ExpertClaimService { return docRec; } + /** Strip bulky insurer-inquiry payloads (`raw` / `mapped`) from vehicle.inquiry. */ + private sanitizeVehicleInquiryForApi(vehicle: any) { + if (!vehicle || typeof vehicle !== "object") return vehicle; + const inquiry = vehicle.inquiry; + if (!inquiry || typeof inquiry !== "object") return vehicle; + const { raw, mapped, ...safeInquiry } = inquiry as Record; + return { + ...vehicle, + inquiry: safeInquiry, + }; + } + + /** Linked blame snapshot for damage-expert detail: keep API response lean. */ + private sanitizeBlameCaseForClaimDetailApi( + blame: Record, + ): Record { + const out = { ...blame }; + const parties = out.parties; + if (Array.isArray(parties)) { + out.parties = parties.map((p: any) => + p && typeof p === "object" + ? { ...p, vehicle: this.sanitizeVehicleInquiryForApi(p.vehicle) } + : p, + ); + } + return out; + } + /** JSON-safe `expert.decision` for API responses (ObjectIds → string). */ private serializeBlameExpertDecisionForClaimDetail( decision: unknown, @@ -3025,8 +3058,9 @@ export class ExpertClaimService { /** * Persist unlock when claim V2 workflow lock TTL has passed. - * If the expert never submitted a damage reply, restores queue fields from - * `workflow.preLockQueueSnapshot` (or defaults) so the case shows as WAITING_FOR_DAMAGE_EXPERT again. + * When the case was in {@link ClaimCaseStatus.EXPERT_REVIEWING}, restores queue fields from + * `workflow.preLockQueueSnapshot` (or defaults) and sets status back to WAITING_FOR_DAMAGE_EXPERT + * so the claim reappears in the open expert queue (same intent as blame list expiry + in-memory unlock). */ private async expireClaimWorkflowLockV2IfStale( claimRequestId: string, @@ -3047,7 +3081,6 @@ export class ExpertClaimService { ) { return false; } - if (!expiredAt && !lockedAt) return false; const lockedById = claim.workflow.lockedBy?.actorId; const filter: Record = { @@ -3059,12 +3092,10 @@ export class ExpertClaimService { } else if (lockedAt) { filter["workflow.lockedAt"] = lockedAt; } else if (lockedById) { - filter["workflow.lockedBy.actorId"] = lockedById; + filter["workflow.lockedBy.actorId"] = new Types.ObjectId(String(lockedById)); } - const needsQueueRestore = - claim.status === ClaimCaseStatus.EXPERT_REVIEWING && - !this.claimHasDamageExpertReply(claim); + const needsQueueRestore = claim.status === ClaimCaseStatus.EXPERT_REVIEWING; const snap = claim.workflow?.preLockQueueSnapshot as | { @@ -3136,6 +3167,7 @@ export class ExpertClaimService { actor: any, ): Promise { const actorId = actor.sub; + await this.reconcileStaleExpertReviewingClaimLocksForTenant(actor); await this.expireClaimWorkflowLockV2IfStale(claimRequestId); const claim = await this.claimCaseDbService.findById(claimRequestId); @@ -3319,6 +3351,21 @@ export class ExpertClaimService { ? ClaimCaseStatus.WAITING_FOR_DAMAGE_EXPERT : claim.status; + const objection = (claim as any).evaluation?.objection + ? { + objectionParts: + (claim as any).evaluation.objection.objectionParts ?? [], + newParts: (claim as any).evaluation.objection.newParts ?? [], + submittedAt: (claim as any).evaluation.objection.submittedAt, + } + : undefined; + + const blameCaseForApi = blameCase + ? this.sanitizeBlameCaseForClaimDetailApi( + blameCase as Record, + ) + : undefined; + return { claimRequestId: claim._id.toString(), publicId: claim.publicId, @@ -3341,7 +3388,9 @@ export class ExpertClaimService { fullName: claim.owner.fullName, } : undefined, - vehicle: vehiclePayload, + vehicle: vehiclePayload + ? this.sanitizeVehicleInquiryForApi(vehiclePayload) + : undefined, ...blameFileContext, blameRequestId: claim.blameRequestId?.toString(), blameRequestNo: claim.blameRequestNo, @@ -3361,8 +3410,9 @@ export class ExpertClaimService { damageExpertReplyFinal: (claim as any).evaluation?.damageExpertReplyFinal, } : undefined, + objection, videoCapture, - blameCase: blameCase ?? undefined, + blameCase: blameCaseForApi, blameExpertDecision, createdAt: (claim as any).createdAt, updatedAt: (claim as any).updatedAt,