From 8b65e43c60e8649f7b317254106641f80a4e6d03 Mon Sep 17 00:00:00 2001 From: SepehrYahyaee <7heycallmegray@gmail.com> Date: Mon, 21 Sep 2026 10:43:30 +0330 Subject: [PATCH] Fixed unstable parts fetching from fanavaran --- .../outer-damage-parts-resolve.spec.ts | 26 +++++++++++++++++++ src/helpers/outer-damage-parts.ts | 22 ++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/src/helpers/outer-damage-parts-resolve.spec.ts b/src/helpers/outer-damage-parts-resolve.spec.ts index 41c68dc..d81ef3b 100644 --- a/src/helpers/outer-damage-parts-resolve.spec.ts +++ b/src/helpers/outer-damage-parts-resolve.spec.ts @@ -2,6 +2,7 @@ import { ClaimVehicleTypeV2 } from "src/static/outer-car-parts-catalog"; import { buildCoefficientsFromPartSeverities } from "./claim-price-drop"; import { catalogPartIdFromSelectedPart, + normalizeDamageSelectedParts, resolveCatalogPartByPartId, resolvePartForExpertReply, resolveSelectedPartByPartId, @@ -86,6 +87,31 @@ describe("resolveSelectedPartByPartId", () => { expect(catalogPartIdFromSelectedPart(row)).toBeNull(); }); + it("preserves a live Fanavaran id that is newer than the static snapshot", () => { + const [row] = normalizeDamageSelectedParts( + [{ + id: 96, + name: "96", + side: "", + label_fa: "تیوتر", + catalogKey: "96", + }], + ClaimVehicleTypeV2.SEDAN, + ); + + expect(row).toEqual({ + id: 96, + name: "96", + side: "", + label_fa: "تیوتر", + catalogKey: "96", + }); + expect(catalogPartIdFromSelectedPart(row)).toBe(96); + expect(resolvePartForExpertReply(96, [row], ClaimVehicleTypeV2.SEDAN)).toBe( + row, + ); + }); + it("recovers outer part wrongly stored as internal with Persian label", () => { const fixed = sanitizeDamageSelectedPartV2( { diff --git a/src/helpers/outer-damage-parts.ts b/src/helpers/outer-damage-parts.ts index 38fc5bd..dcf7094 100644 --- a/src/helpers/outer-damage-parts.ts +++ b/src/helpers/outer-damage-parts.ts @@ -466,6 +466,28 @@ export function sanitizeDamageSelectedPartV2( } } + // Fanavaran serves this catalog live and can add ids that do not exist in + // our fallback snapshot yet. Live rows are persisted with their numeric id + // repeated as `name`/`catalogKey` and an empty side. Preserve that identity + // instead of downgrading the row to an internal free-text part. Otherwise a + // newly-added Fanavaran part (for example id 96) disappears from the expert + // reply payload and later fails with PART_NOT_ON_CLAIM. + const numericCatalogIdentity = + id != null && + Number.isFinite(id) && + Number(id) > 0 && + sideTrim === "" && + (nameTrim === String(id) || String(catalogKey ?? "").trim() === String(id)); + if (numericCatalogIdentity) { + return { + id: Number(id), + name: String(id), + side: "", + label_fa: labelTrim || nameTrim || String(id), + catalogKey: String(id), + }; + } + if (isInternalDamageSide(side)) { return { id: null,