Fixed bugs

This commit is contained in:
SepehrYahyaee
2026-05-12 11:07:58 +03:30
parent f75e6a4453
commit e26c533a52
2 changed files with 72 additions and 1 deletions

View File

@@ -82,6 +82,8 @@ import {
coerceDamagedPartsMediaToArray, coerceDamagedPartsMediaToArray,
normalizeCarPartDamageForExpertReply, normalizeCarPartDamageForExpertReply,
normalizeDamageSelectedParts, normalizeDamageSelectedParts,
partIdentityKey,
partIdentityKeyFromCarPartDamage,
} from "src/helpers/outer-damage-parts"; } from "src/helpers/outer-damage-parts";
import { ClaimVehicleTypeV2 } from "src/static/outer-car-parts-catalog"; import { ClaimVehicleTypeV2 } from "src/static/outer-car-parts-catalog";
import { snapshotFromDamageExpert } from "src/helpers/expert-profile-snapshot"; import { snapshotFromDamageExpert } from "src/helpers/expert-profile-snapshot";
@@ -2639,6 +2641,17 @@ export class ExpertClaimService {
reply.parts?.length > 0 reply.parts?.length > 0
? this.validateAndNormalizeDaghiForExpertReplyV2(reply.parts) ? this.validateAndNormalizeDaghiForExpertReplyV2(reply.parts)
: []; : [];
const ownerSelectedParts = normalizeDamageSelectedParts(
(claim as any)?.damage?.selectedParts,
carTypeSubmit,
(claim as any)?.damage?.selectedOuterParts,
);
const ownerSelectedIdentitySet = new Set(
ownerSelectedParts.map((p) => partIdentityKey(p)),
);
const seenIdentities = new Set<string>();
const processedParts = daghiNormalized.map((p) => { const processedParts = daghiNormalized.map((p) => {
let carPartDamage: Record<string, unknown>; let carPartDamage: Record<string, unknown>;
try { try {
@@ -2653,7 +2666,31 @@ export class ExpertClaimService {
}`, }`,
); );
} }
return { ...p, carPartDamage };
const identityKey = partIdentityKeyFromCarPartDamage(carPartDamage);
if (!identityKey) {
throw new BadRequestException(
`Invalid carPartDamage for part ${p.partId}: cannot derive a stable identity (need a catalog id or both name and side).`,
);
}
if (
ownerSelectedIdentitySet.size > 0 &&
!ownerSelectedIdentitySet.has(identityKey)
) {
throw new BadRequestException(
`Part "${identityKey}" is not in the owner's selected damaged parts; only parts the user picked (or added via objection) can be priced.`,
);
}
if (seenIdentities.has(identityKey)) {
throw new BadRequestException(
`Duplicate part submitted in expert reply: ${identityKey}.`,
);
}
seenIdentities.add(identityKey);
return { ...p, partId: identityKey, carPartDamage };
}); });
const { mixedFactorAndPrice, allFactorLines } = classifyV2ExpertPricingParts( const { mixedFactorAndPrice, allFactorLines } = classifyV2ExpertPricingParts(

View File

@@ -298,6 +298,40 @@ export function partIdentityKey(p: DamageSelectedPartV2): string {
return `${p.side}|${p.name}`; return `${p.side}|${p.name}`;
} }
/**
* Same canonical identity as `partIdentityKey`, but derived directly from a
* unified `carPartDamage` snapshot (the shape we persist on
* `evaluation.damageExpertReply*.parts[].carPartDamage`).
*
* Returns `null` when the input cannot produce an identity (no `id`, no
* `name`). Callers should treat that as an invalid expert reply line.
*/
export function partIdentityKeyFromCarPartDamage(
carPartDamage: unknown,
): string | null {
if (
!carPartDamage ||
typeof carPartDamage !== "object" ||
Array.isArray(carPartDamage)
) {
return null;
}
const o = carPartDamage as Record<string, unknown>;
const idRaw = o.id;
const id =
typeof idRaw === "number" && Number.isFinite(idRaw)
? idRaw
: typeof idRaw === "string" &&
idRaw.trim() &&
Number.isFinite(Number(idRaw))
? Number(idRaw)
: null;
const name = typeof o.name === "string" ? o.name.trim() : "";
const side = typeof o.side === "string" ? o.side.trim() : "";
if (id == null && !name) return null;
return partIdentityKey({ id, name, side, label_fa: "" });
}
function normPartSegment(s: string): string { function normPartSegment(s: string): string {
return String(s ?? "").replace(/[^a-z0-9]/gi, "").toLowerCase(); return String(s ?? "").replace(/[^a-z0-9]/gi, "").toLowerCase();
} }