forked from Yara724/api
181 lines
5.6 KiB
TypeScript
181 lines
5.6 KiB
TypeScript
// claim-damaged-part.enricher.ts
|
|
|
|
import { resolveStoredFileUrl } from "src/helpers/urlCreator";
|
|
import {
|
|
DamagedPartCaptureStatus,
|
|
EnrichedDamagedPart,
|
|
} from "./claim-damaged-part.types";
|
|
|
|
export function buildEnrichedDamagedParts(params: {
|
|
selectedParts: any[];
|
|
damagedPartsData: any;
|
|
evaluationBlock: any;
|
|
expertAddedParts?: any[];
|
|
getDamagedPartCaptureBlob: (data: any, key: string, parts: any[]) => any;
|
|
hasDamagedPartCapture: (data: any, key: string, parts: any[]) => boolean;
|
|
catalogLikeKeyFromPart: (part: any) => string;
|
|
buildFileLink: (path: string) => string;
|
|
resolveStoredFileUrl?: (
|
|
stored?: { url?: string; path?: string } | null,
|
|
) => string | undefined;
|
|
}): EnrichedDamagedPart[] {
|
|
const {
|
|
selectedParts,
|
|
damagedPartsData,
|
|
evaluationBlock,
|
|
expertAddedParts = [],
|
|
getDamagedPartCaptureBlob,
|
|
hasDamagedPartCapture,
|
|
catalogLikeKeyFromPart,
|
|
buildFileLink,
|
|
resolveStoredFileUrl: resolveUrl = resolveStoredFileUrl,
|
|
} = params;
|
|
|
|
const priceDropLines: any[] = evaluationBlock?.priceDrop?.partLines ?? [];
|
|
|
|
// objectionParts stores { partId, carPartDamage, side } — not { id, name }
|
|
const objectionParts: any[] =
|
|
evaluationBlock?.objection?.objectionParts ?? [];
|
|
|
|
// newParts stores { partId, partName, side } — not { id, name }
|
|
const newObjectionParts: any[] = evaluationBlock?.objection?.newParts ?? [];
|
|
|
|
const damageExpertResend = evaluationBlock?.damageExpertResend;
|
|
const resendCarParts: any[] = damageExpertResend?.resendCarParts ?? [];
|
|
const resendFulfilledAt: Date | undefined = damageExpertResend?.fulfilledAt;
|
|
|
|
// Helpers that match against the ACTUAL stored field names
|
|
function partMatchesObjection(sp: any, objPart: any): boolean {
|
|
if (sp.id != null && objPart.partId != null && sp.id === objPart.partId)
|
|
return true;
|
|
const objName = objPart.carPartDamage ?? objPart.name ?? objPart.partName;
|
|
if (objName && (sp.name === objName || sp.label_fa === objName))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
function partMatchesNewObjection(sp: any, newPart: any): boolean {
|
|
if (sp.id != null && newPart.partId != null && sp.id === newPart.partId)
|
|
return true;
|
|
const newName = newPart.partName ?? newPart.name;
|
|
if (newName && (sp.name === newName || sp.label_fa === newName))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
function isPartInResend(sp: any): boolean {
|
|
return resendCarParts.some(
|
|
(r: any) =>
|
|
(sp.id != null && r.id === sp.id) ||
|
|
r.catalogKey === sp.catalogKey ||
|
|
r.name === sp.name,
|
|
);
|
|
}
|
|
|
|
function resolveOrigin(sp: any): EnrichedDamagedPart["origin"] {
|
|
if (newObjectionParts.some((p) => partMatchesNewObjection(sp, p))) {
|
|
return "added_by_objection";
|
|
}
|
|
if (
|
|
expertAddedParts.some((p: any) => p.id === sp.id || p.name === sp.name)
|
|
) {
|
|
return "added_by_expert";
|
|
}
|
|
return "user_selected";
|
|
}
|
|
|
|
function resolveCaptureStatus(
|
|
sp: any,
|
|
captured: boolean,
|
|
): DamagedPartCaptureStatus {
|
|
const inResend = isPartInResend(sp);
|
|
if (!captured) {
|
|
return inResend ? "resend_requested" : "pending";
|
|
}
|
|
return inResend && resendFulfilledAt ? "resent" : "uploaded";
|
|
}
|
|
|
|
return selectedParts.map((sp, index) => {
|
|
const ck = sp.catalogKey ?? catalogLikeKeyFromPart(sp);
|
|
|
|
const cap = getDamagedPartCaptureBlob(
|
|
damagedPartsData,
|
|
ck,
|
|
selectedParts,
|
|
) as { url?: string; path?: string } | undefined;
|
|
|
|
// Fallback: if id is null but media.damagedParts has a match by name
|
|
const captured =
|
|
hasDamagedPartCapture(damagedPartsData, ck, selectedParts) ||
|
|
(sp.id == null && Array.isArray(damagedPartsData)
|
|
? (damagedPartsData as any[]).some(
|
|
(d) => d.name === sp.name && d.side === sp.side && d.path,
|
|
)
|
|
: false);
|
|
|
|
const capUrl =
|
|
resolveUrl(cap) ||
|
|
(sp.id == null && Array.isArray(damagedPartsData)
|
|
? (() => {
|
|
const match = (damagedPartsData as any[]).find(
|
|
(d) => d.name === sp.name && d.side === sp.side && d.path,
|
|
);
|
|
return resolveUrl(match);
|
|
})()
|
|
: undefined);
|
|
|
|
const matchingPriceDrop = priceDropLines.find(
|
|
(line: any) =>
|
|
line.partId === sp.id ||
|
|
line.priceDropPartKey?.toLowerCase() === sp.name?.toLowerCase(),
|
|
);
|
|
|
|
const isObjected = objectionParts.some((p) => partMatchesObjection(sp, p));
|
|
const isNewlyAdded = newObjectionParts.some((p) =>
|
|
partMatchesNewObjection(sp, p),
|
|
);
|
|
const isNewByExpert = expertAddedParts.some(
|
|
(p: any) => p.id === sp.id || p.name === sp.name,
|
|
);
|
|
const inResend = isPartInResend(sp);
|
|
|
|
return {
|
|
index,
|
|
partId: sp.id ?? null,
|
|
id: sp.id ?? null,
|
|
name: sp.name,
|
|
side: sp.side,
|
|
label_fa: sp.label_fa,
|
|
...(sp.catalogKey ? { catalogKey: sp.catalogKey } : {}),
|
|
|
|
origin: resolveOrigin(sp),
|
|
captureStatus: resolveCaptureStatus(sp, captured),
|
|
captured,
|
|
url: capUrl,
|
|
|
|
objection: {
|
|
isObjected,
|
|
isNewlyAdded,
|
|
...(isNewlyAdded ? { addedBy: isNewByExpert ? "expert" : "user" } : {}),
|
|
},
|
|
|
|
resend: {
|
|
wasRequested: inResend,
|
|
isFulfilled: inResend && !!resendFulfilledAt,
|
|
...(inResend && damageExpertResend?.requestedAt
|
|
? { requestedAt: damageExpertResend.requestedAt }
|
|
: {}),
|
|
...(inResend && resendFulfilledAt
|
|
? { fulfilledAt: resendFulfilledAt }
|
|
: {}),
|
|
},
|
|
|
|
pricing: {
|
|
hasPriceDrop: !!matchingPriceDrop,
|
|
severity: matchingPriceDrop?.severity ?? null,
|
|
coefficient: matchingPriceDrop?.coefficient ?? null,
|
|
},
|
|
};
|
|
});
|
|
}
|