1
0
forked from Yara724/api
This commit is contained in:
2026-05-01 11:14:05 +03:30
parent def4023185
commit 8419ec06ae
3 changed files with 70 additions and 10 deletions

View File

@@ -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<Record<string, unknown>>;
newParts?: Array<Record<string, unknown>>;
submittedAt?: Date | string;
};
@ApiPropertyOptional({
description:
'Car walk-around video from `claim-video-capture` (resolved from `media.videoCaptureId`).',

View File

@@ -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<string, unknown>;
return {
...vehicle,
inquiry: safeInquiry,
};
}
/** Linked blame snapshot for damage-expert detail: keep API response lean. */
private sanitizeBlameCaseForClaimDetailApi(
blame: Record<string, unknown>,
): Record<string, unknown> {
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<string, unknown> = {
@@ -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<ClaimDetailV2ResponseDto> {
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<string, unknown>,
)
: 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,