1
0
forked from Yara724/api

Fixed claim/blame damagedParts

This commit is contained in:
2026-05-03 13:54:48 +03:30
parent c2d59112cf
commit c579f8fa1d
16 changed files with 1347 additions and 299 deletions

View File

@@ -71,8 +71,16 @@ import {
import {
getClaimCarAngleCaptureBlob,
getDamagedPartCaptureBlob,
hasDamagedPartCapture,
type ClaimCarAngleKey,
} from "src/helpers/claim-car-angle-media";
import {
catalogLikeKeyFromPart,
coerceDamagedPartsMediaToArray,
normalizeCarPartDamageForExpertReply,
normalizeDamageSelectedParts,
} from "src/helpers/outer-damage-parts";
import { ClaimVehicleTypeV2 } from "src/static/outer-car-parts-catalog";
import { snapshotFromDamageExpert } from "src/helpers/expert-profile-snapshot";
import { DamageExpertModel } from "src/users/entities/schema/damage-expert.schema";
import { SmsOrchestrationService } from "src/sms-orchestration/sms-orchestration.service";
@@ -2532,10 +2540,27 @@ export class ExpertClaimService {
});
}
const processedParts =
const carTypeSubmit = claim.vehicle?.carType as ClaimVehicleTypeV2 | undefined;
const daghiNormalized =
reply.parts?.length > 0
? this.validateAndNormalizeDaghiForExpertReplyV2(reply.parts)
: [];
const processedParts = daghiNormalized.map((p) => {
let carPartDamage: Record<string, unknown>;
try {
carPartDamage = normalizeCarPartDamageForExpertReply(
p.carPartDamage as unknown,
carTypeSubmit,
);
} catch (err) {
throw new BadRequestException(
`Invalid carPartDamage for part ${p.partId}: ${
err instanceof Error ? err.message : String(err)
}`,
);
}
return { ...p, carPartDamage };
});
const { mixedFactorAndPrice, allFactorLines } = classifyV2ExpertPricingParts(
processedParts,
@@ -3317,40 +3342,35 @@ export class ExpertClaimService {
};
}
// Build damaged parts map
const getPartLabelFa = (key: string): string => {
const labels: Record<string, string> = {
hood: "کاپوت",
trunk: "صندوق عقب",
roof: "سقف",
front_right_door: "درب جلو راست",
front_left_door: "درب جلو چپ",
rear_right_door: "درب عقب راست",
rear_left_door: "درب عقب چپ",
front_bumper: "سپر جلو",
rear_bumper: "سپر عقب",
front_right_fender: "گلگیر جلو راست",
front_left_fender: "گلگیر جلو چپ",
rear_right_fender: "گلگیر عقب راست",
rear_left_fender: "گلگیر عقب چپ",
};
return labels[key] || key;
};
const damagedPartsData = claim.media?.damagedParts as any;
const damagedParts: Record<
string,
{ label_fa: string; captured: boolean; url?: string }
> = {};
for (const p of claim.damage?.selectedParts || []) {
const cap = getDamagedPartCaptureBlob(damagedPartsData, p) as
| { url?: string; path?: string }
| undefined;
damagedParts[p] = {
label_fa: getPartLabelFa(p),
captured: !!cap,
// Build damaged parts list (aligned with normalized selected parts)
const carTypeExpert = claim.vehicle?.carType as ClaimVehicleTypeV2 | undefined;
const selectedNormExpert = normalizeDamageSelectedParts(
claim.damage?.selectedParts,
carTypeExpert,
(claim.damage as any)?.selectedOuterParts,
);
const damagedPartsDataExpert = claim.media?.damagedParts as any;
const damagedParts = selectedNormExpert.map((sp, index) => {
const ck = sp.catalogKey ?? catalogLikeKeyFromPart(sp);
const cap = getDamagedPartCaptureBlob(
damagedPartsDataExpert,
ck,
selectedNormExpert,
) as { url?: string; path?: string } | undefined;
return {
index,
id: sp.id,
name: sp.name,
side: sp.side,
label_fa: sp.label_fa,
captured: hasDamagedPartCapture(
damagedPartsDataExpert,
ck,
selectedNormExpert,
),
url: cap?.url || (cap?.path ? buildFileLink(cap.path) : undefined),
};
}
});
// --- Combine both branches of the conflict, preserving necessary logic from both ---
@@ -3480,7 +3500,7 @@ export class ExpertClaimService {
blameRequestId: claim.blameRequestId?.toString(),
blameRequestNo: claim.blameRequestNo,
money: moneyPayload,
selectedParts: claim.damage?.selectedParts,
selectedParts: selectedNormExpert,
otherParts: claim.damage?.otherParts,
requiredDocuments:
Object.keys(requiredDocumentsStatus).length > 0
@@ -3535,35 +3555,70 @@ export class ExpertClaimService {
const damagedPartsEditSnapshot = await this.snapshotDamageExpert(actor.sub);
const carType = claim.vehicle?.carType as ClaimVehicleTypeV2 | undefined;
const previousNorm = normalizeDamageSelectedParts(
(claim as any).damage?.selectedParts,
carType,
(claim as any).damage?.selectedOuterParts,
);
const prevMedia = coerceDamagedPartsMediaToArray(
(claim as any).media?.damagedParts,
previousNorm,
);
const nextNorm = body.selectedParts.map((p) => ({
id: p.id ?? null,
name: String(p.name || "").trim(),
side: String(p.side || ""),
label_fa: String(p.label_fa || "").trim() || String(p.name || "").trim(),
...(p.catalogKey?.trim() ? { catalogKey: p.catalogKey.trim() } : {}),
}));
const matchPreviousIndex = (
next: (typeof nextNorm)[0],
previous: typeof previousNorm,
): number => {
if (next.id != null) {
const byId = previous.findIndex((x) => x.id === next.id);
if (byId >= 0) return byId;
}
if (next.catalogKey) {
const byCk = previous.findIndex((x) => x.catalogKey === next.catalogKey);
if (byCk >= 0) return byCk;
}
return previous.findIndex(
(x) => x.name === next.name && x.side === next.side,
);
};
const nextMedia = nextNorm.map((sp) => {
const j = matchPreviousIndex(sp, previousNorm);
const row =
j >= 0 && prevMedia[j] && typeof prevMedia[j] === "object"
? (prevMedia[j] as Record<string, unknown>)
: {};
return {
...(sp.id != null ? { id: sp.id } : {}),
name: sp.name,
side: sp.side,
label_fa: sp.label_fa,
...(sp.catalogKey ? { catalogKey: sp.catalogKey } : {}),
...(row.path ? { path: row.path } : {}),
...(row.fileName ? { fileName: row.fileName } : {}),
...(row.url ? { url: row.url } : {}),
...(row.capturedAt ? { capturedAt: row.capturedAt } : {}),
};
});
const previous = Array.isArray((claim as any).damage?.selectedParts)
? [...(claim as any).damage.selectedParts]
: [];
const selectedParts = body.selectedParts as string[];
const $set: Record<string, unknown> = {
"damage.selectedParts": selectedParts,
"damage.selectedParts": nextNorm,
"media.damagedParts": nextMedia,
};
const damagedPartsMedia = (claim as any).media?.damagedParts;
if (damagedPartsMedia) {
const selectedSet = new Set(selectedParts);
if (damagedPartsMedia instanceof Map) {
const m = new Map(damagedPartsMedia as Map<string, unknown>);
for (const key of Array.from(m.keys())) {
if (!selectedSet.has(key)) m.delete(key);
}
$set["media.damagedParts"] = Object.fromEntries(m.entries());
} else {
const o = {
...(damagedPartsMedia as Record<string, unknown>),
};
for (const key of Object.keys(o)) {
if (!selectedSet.has(key)) delete o[key];
}
$set["media.damagedParts"] = o;
}
}
await this.claimCaseDbService.findByIdAndUpdate(claimRequestId, {
$set,
$push: {
@@ -3577,7 +3632,7 @@ export class ExpertClaimService {
timestamp: new Date(),
metadata: {
previousSelectedParts: previous,
selectedParts,
selectedParts: nextNorm,
...(damagedPartsEditSnapshot && {
expertProfileSnapshot: damagedPartsEditSnapshot,
}),
@@ -3588,7 +3643,7 @@ export class ExpertClaimService {
return {
claimRequestId: claim._id.toString(),
selectedParts,
selectedParts: nextNorm,
previousSelectedParts: previous,
message: "Damaged parts updated successfully.",
};