forked from Yara724/api
Added sign links and data to expert-claim API
This commit is contained in:
@@ -120,11 +120,32 @@ export class ClaimDetailV2ResponseDto {
|
|||||||
|
|
||||||
@ApiPropertyOptional({
|
@ApiPropertyOptional({
|
||||||
description:
|
description:
|
||||||
'Expert reply payloads (first and/or final) when awaiting factor validation.',
|
'Slice of `claim.evaluation` exposed to the damage expert. ' +
|
||||||
|
'`damageExpertReply` / `damageExpertReplyFinal` are returned only while ' +
|
||||||
|
'awaiting factor validation. `ownerInsurerApproval` and ' +
|
||||||
|
'`ownerPricedPartsApproval` are returned whenever they exist on the ' +
|
||||||
|
'claim — each carries `signLink` (resolved from `signDetailId`) so the ' +
|
||||||
|
'front-end can render the user signature directly. Likewise, ' +
|
||||||
|
'`damageExpertReply.userComment` / `damageExpertReplyFinal.userComment` ' +
|
||||||
|
'expose `signLink` when a user comment signature is present.',
|
||||||
})
|
})
|
||||||
evaluation?: {
|
evaluation?: {
|
||||||
damageExpertReply?: unknown;
|
damageExpertReply?: unknown;
|
||||||
damageExpertReplyFinal?: unknown;
|
damageExpertReplyFinal?: unknown;
|
||||||
|
ownerInsurerApproval?: {
|
||||||
|
agree: boolean;
|
||||||
|
branchId?: string;
|
||||||
|
signDetailId?: string;
|
||||||
|
signLink?: string;
|
||||||
|
signedAt?: Date | string;
|
||||||
|
};
|
||||||
|
ownerPricedPartsApproval?: {
|
||||||
|
agree: boolean;
|
||||||
|
branchId?: string;
|
||||||
|
signDetailId?: string;
|
||||||
|
signLink?: string;
|
||||||
|
signedAt?: Date | string;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
@ApiPropertyOptional({
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import { distance as stringDistance } from "fastest-levenshtein"; // حتما ن
|
|||||||
import { Types } from "mongoose";
|
import { Types } from "mongoose";
|
||||||
import { lastValueFrom } from "rxjs";
|
import { lastValueFrom } from "rxjs";
|
||||||
import { ClaimRequestManagementDbService } from "src/claim-request-management/entites/db-service/claim-request-management.db.service";
|
import { ClaimRequestManagementDbService } from "src/claim-request-management/entites/db-service/claim-request-management.db.service";
|
||||||
|
import { ClaimSignDbService } from "src/claim-request-management/entites/db-service/claim-sign.db.service";
|
||||||
import { DamageImageDbService } from "src/claim-request-management/entites/db-service/damage-image.db.service";
|
import { DamageImageDbService } from "src/claim-request-management/entites/db-service/damage-image.db.service";
|
||||||
import { VideoCaptureDbService } from "src/claim-request-management/entites/db-service/video-capture.db.service";
|
import { VideoCaptureDbService } from "src/claim-request-management/entites/db-service/video-capture.db.service";
|
||||||
import { ClientDbService } from "src/client/entities/db-service/client.db.service";
|
import { ClientDbService } from "src/client/entities/db-service/client.db.service";
|
||||||
@@ -216,8 +217,68 @@ export class ExpertClaimService {
|
|||||||
private readonly smsOrchestrationService: SmsOrchestrationService,
|
private readonly smsOrchestrationService: SmsOrchestrationService,
|
||||||
private readonly userDbService: UserDbService,
|
private readonly userDbService: UserDbService,
|
||||||
private readonly expertFileActivityDbService: ExpertFileActivityDbService,
|
private readonly expertFileActivityDbService: ExpertFileActivityDbService,
|
||||||
|
private readonly claimSignDbService: ClaimSignDbService,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve a `claim-sign` document id to a downloadable file URL.
|
||||||
|
* Mirrors the helper used in `ExpertInsurerService` so signature links are
|
||||||
|
* exposed identically across the two expert-facing detail endpoints.
|
||||||
|
*/
|
||||||
|
private async claimSignLinkFromId(
|
||||||
|
signDetailId: unknown,
|
||||||
|
): Promise<string | undefined> {
|
||||||
|
if (signDetailId == null || signDetailId === "") return undefined;
|
||||||
|
const id = String(signDetailId);
|
||||||
|
if (!Types.ObjectId.isValid(id)) return undefined;
|
||||||
|
const doc = await this.claimSignDbService.findOne({
|
||||||
|
_id: new Types.ObjectId(id),
|
||||||
|
});
|
||||||
|
const path = (doc as any)?.path;
|
||||||
|
return path ? buildFileLink(String(path)) : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a deep-cloned `evaluation` payload with resolved `signLink`s for the
|
||||||
|
* fields that carry a user signature:
|
||||||
|
*
|
||||||
|
* - `damageExpertReply.userComment.signLink`
|
||||||
|
* - `damageExpertReplyFinal.userComment.signLink`
|
||||||
|
* - `ownerInsurerApproval.signLink`
|
||||||
|
* - `ownerPricedPartsApproval.signLink`
|
||||||
|
*
|
||||||
|
* Returns `undefined` when there's no evaluation to enrich.
|
||||||
|
*/
|
||||||
|
private async enrichClaimEvaluationForExpert(
|
||||||
|
evaluation: Record<string, unknown> | undefined | null,
|
||||||
|
): Promise<Record<string, unknown> | undefined> {
|
||||||
|
if (!evaluation || typeof evaluation !== "object") return undefined;
|
||||||
|
const ev = JSON.parse(JSON.stringify(evaluation)) as Record<string, unknown>;
|
||||||
|
|
||||||
|
const enrichReplyUserComment = async (key: string) => {
|
||||||
|
const reply = ev[key] as Record<string, unknown> | undefined;
|
||||||
|
const uc = reply?.userComment as Record<string, unknown> | undefined;
|
||||||
|
if (uc?.signDetailId != null) {
|
||||||
|
const signLink = await this.claimSignLinkFromId(uc.signDetailId);
|
||||||
|
(reply as Record<string, unknown>).userComment = { ...uc, signLink };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
await enrichReplyUserComment("damageExpertReply");
|
||||||
|
await enrichReplyUserComment("damageExpertReplyFinal");
|
||||||
|
|
||||||
|
const enrichApproval = async (key: string) => {
|
||||||
|
const o = ev[key] as Record<string, unknown> | undefined;
|
||||||
|
if (o?.signDetailId != null) {
|
||||||
|
const signLink = await this.claimSignLinkFromId(o.signDetailId);
|
||||||
|
ev[key] = { ...o, signLink };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
await enrichApproval("ownerInsurerApproval");
|
||||||
|
await enrichApproval("ownerPricedPartsApproval");
|
||||||
|
|
||||||
|
return ev;
|
||||||
|
}
|
||||||
|
|
||||||
/** Load immutable profile fields from `damage-expert` for the acting expert. */
|
/** Load immutable profile fields from `damage-expert` for the acting expert. */
|
||||||
private async snapshotDamageExpert(actorId: string) {
|
private async snapshotDamageExpert(actorId: string) {
|
||||||
const doc = await this.damageExpertDbService.findById(actorId);
|
const doc = await this.damageExpertDbService.findById(actorId);
|
||||||
@@ -3473,6 +3534,46 @@ export class ExpertClaimService {
|
|||||||
)
|
)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
|
// Enrich the full evaluation block with `signLink`s for every place a user
|
||||||
|
// signature is referenced, then assemble the response slice.
|
||||||
|
//
|
||||||
|
// We always expose owner approval blocks (when present in the DB) because
|
||||||
|
// the signature URL is needed across all phases of the claim. The expert
|
||||||
|
// reply payloads are still gated on `isFactorValidationPending` to match
|
||||||
|
// the historical contract of this endpoint.
|
||||||
|
const claimEvaluationRaw = (claim as any).evaluation as
|
||||||
|
| Record<string, unknown>
|
||||||
|
| undefined;
|
||||||
|
const enrichedEvaluation = await this.enrichClaimEvaluationForExpert(
|
||||||
|
claimEvaluationRaw,
|
||||||
|
);
|
||||||
|
|
||||||
|
let evaluationForApi: Record<string, unknown> | undefined;
|
||||||
|
if (enrichedEvaluation) {
|
||||||
|
evaluationForApi = {};
|
||||||
|
if (isFactorValidationPending) {
|
||||||
|
if (enrichedEvaluation.damageExpertReply !== undefined) {
|
||||||
|
evaluationForApi.damageExpertReply =
|
||||||
|
enrichedEvaluation.damageExpertReply;
|
||||||
|
}
|
||||||
|
if (enrichedEvaluation.damageExpertReplyFinal !== undefined) {
|
||||||
|
evaluationForApi.damageExpertReplyFinal =
|
||||||
|
enrichedEvaluation.damageExpertReplyFinal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (enrichedEvaluation.ownerInsurerApproval !== undefined) {
|
||||||
|
evaluationForApi.ownerInsurerApproval =
|
||||||
|
enrichedEvaluation.ownerInsurerApproval;
|
||||||
|
}
|
||||||
|
if (enrichedEvaluation.ownerPricedPartsApproval !== undefined) {
|
||||||
|
evaluationForApi.ownerPricedPartsApproval =
|
||||||
|
enrichedEvaluation.ownerPricedPartsApproval;
|
||||||
|
}
|
||||||
|
if (Object.keys(evaluationForApi).length === 0) {
|
||||||
|
evaluationForApi = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
claimRequestId: claim._id.toString(),
|
claimRequestId: claim._id.toString(),
|
||||||
publicId: claim.publicId,
|
publicId: claim.publicId,
|
||||||
@@ -3511,12 +3612,9 @@ export class ExpertClaimService {
|
|||||||
carAngles,
|
carAngles,
|
||||||
damagedParts,
|
damagedParts,
|
||||||
awaitingFactorValidation: isFactorValidationPending,
|
awaitingFactorValidation: isFactorValidationPending,
|
||||||
evaluation: isFactorValidationPending
|
evaluation: evaluationForApi as
|
||||||
? {
|
| ClaimDetailV2ResponseDto["evaluation"]
|
||||||
damageExpertReply: (claim as any).evaluation?.damageExpertReply,
|
| undefined,
|
||||||
damageExpertReplyFinal: (claim as any).evaluation?.damageExpertReplyFinal,
|
|
||||||
}
|
|
||||||
: undefined,
|
|
||||||
objection,
|
objection,
|
||||||
videoCapture,
|
videoCapture,
|
||||||
blameCase: blameCaseForApi,
|
blameCase: blameCaseForApi,
|
||||||
|
|||||||
Reference in New Issue
Block a user