diff --git a/src/claim-request-management/claim-request-management.service.ts b/src/claim-request-management/claim-request-management.service.ts index 4667138..2661385 100644 --- a/src/claim-request-management/claim-request-management.service.ts +++ b/src/claim-request-management/claim-request-management.service.ts @@ -38,6 +38,7 @@ import { ClaimCaseDbService } from "./entites/db-service/claim-case.db.service"; import { BlameRequestDbService } from "src/request-management/entities/db-service/blame-request.db.service"; import { CreateClaimFromBlameResponseDto } from "./dto/create-claim-v2.dto"; import { + OuterPartCatalogItemDto, SelectOuterPartsV2Dto, SelectOuterPartsV2ResponseDto, } from "./dto/select-outer-parts-v2.dto"; @@ -140,7 +141,7 @@ import { } from "src/helpers/claim-car-angle-media"; import { ClaimVehicleTypeV2, - OUTER_PARTS_BY_CAR_TYPE, + FANAVARAN_CAR_PARTS_CATALOG, OuterPartCatalogItem, } from "src/static/outer-car-parts-catalog"; import { @@ -563,67 +564,53 @@ export class ClaimRequestManagementService { /** * Outer-parts catalog returned to clients (user app + expert panel). * - * The shape intentionally mirrors how items are persisted under - * `damage.selectedParts` so the front-end can match catalog rows to the - * stored selection by `id` / `catalogKey` without renaming fields: - * - * { id, name, side, label_fa, catalogKey, carType } - * - * - `name` is side-agnostic (`backWheel`, not `left_backWheel`). - * - `label_fa` is disambiguated with the side in parentheses when multiple - * catalog rows share the same Farsi title (e.g. `چرخ عقب (چپ)`). - * - `catalogKey` keeps the original full key (`left_backWheel`) for clients - * that already address parts by it. + * Fetches the live `car/base-info/car-components` lookup from Fanavaran so + * the list always matches what Fanavaran accepts in DmgSections[].DmgSectionId. + * The lookup service handles caching to disk; `carType` is ignored because + * Fanavaran uses a single flat catalog across all vehicle types. */ - getOuterPartsCatalogV2( - carType?: ClaimVehicleTypeV2, - ): (DamageSelectedPartV2 & { carType: ClaimVehicleTypeV2 })[] { - const buildForType = ( - type: ClaimVehicleTypeV2, - items: OuterPartCatalogItem[], - ): (DamageSelectedPartV2 & { carType: ClaimVehicleTypeV2 })[] => - items.map((p) => ({ - ...catalogItemToSelectedPart(p, items), - carType: type, - })); - - if (carType) { - const items = OUTER_PARTS_BY_CAR_TYPE[carType] || []; - return buildForType(carType, items); - } - - const out: (DamageSelectedPartV2 & { carType: ClaimVehicleTypeV2 })[] = []; - for (const [type, items] of Object.entries(OUTER_PARTS_BY_CAR_TYPE)) { - out.push(...buildForType(type as ClaimVehicleTypeV2, items)); - } - return out.sort((a, b) => { - if (a.carType === b.carType) return (a.id ?? 0) - (b.id ?? 0); - return String(a.carType).localeCompare(String(b.carType)); - }); + async getOuterPartsCatalogV2(): Promise { + const clientKey = resolveFanavaranClientKey(); + const rows = await this.getFanavaranLookupRows(clientKey, "car-components"); + return rows + .filter( + (r): r is { Id: number; Caption: string } => + !!r && + typeof r === "object" && + typeof (r as any).Id === "number" && + typeof (r as any).Caption === "string", + ) + .map((r) => ({ id: r.Id, label_fa: r.Caption })); } /** - * Internal raw catalog (kept in the original `OuterPartCatalogItem` shape so - * existing internal code that indexes by `key`/`titleFa` keeps working). - * Public API consumers should use `getOuterPartsCatalogV2` instead. + * Resolve the live Fanavaran car-components catalog as `OuterPartCatalogItem` + * rows for internal use (e.g. validating selectedPartIds). + * Falls back to the static snapshot when the remote lookup is unavailable. */ - private getOuterPartsRawCatalog( - carType?: ClaimVehicleTypeV2, - ): OuterPartCatalogItem[] { - if (carType) { - return (OUTER_PARTS_BY_CAR_TYPE[carType] || []).map((p) => ({ - ...p, - carType, + private async getLiveFanavaranCatalogItems(): Promise { + const clientKey = resolveFanavaranClientKey(); + const rows = await this.getFanavaranLookupRows(clientKey, "car-components"); + if (!rows.length) { + this.logger.warn( + "getLiveFanavaranCatalogItems: remote lookup returned empty, falling back to snapshot", + ); + return FANAVARAN_CAR_PARTS_CATALOG; + } + return rows + .filter( + (r): r is { Id: number; Caption: string } => + !!r && + typeof r === "object" && + typeof (r as any).Id === "number" && + typeof (r as any).Caption === "string", + ) + .map((r): OuterPartCatalogItem => ({ + id: r.Id, + key: String(r.Id), + titleFa: r.Caption, + side: "", })); - } - const out: OuterPartCatalogItem[] = []; - for (const [type, items] of Object.entries(OUTER_PARTS_BY_CAR_TYPE)) { - const t = type as ClaimVehicleTypeV2; - for (const p of items) { - out.push({ ...p, carType: t }); - } - } - return out; } private userDamageDetail(blRequest: RequestManagementModel) { @@ -7628,22 +7615,14 @@ export class ClaimRequestManagementService { ); } - // 5. Validate by selected car type and resolve selected parts by ids/keys + // 5. Fetch the live Fanavaran catalog and resolve selected parts by ids const selectedCarType = (body as any)?.carType as | ClaimVehicleTypeV2 | undefined; - if (!selectedCarType || !OUTER_PARTS_BY_CAR_TYPE[selectedCarType]) { - throw new BadRequestException( - "Vehicle type is required before selecting outer parts.", - ); - } - const catalog = OUTER_PARTS_BY_CAR_TYPE[selectedCarType]; + const liveCatalog = await this.getLiveFanavaranCatalogItems(); const byId = new Map( - catalog.map((p) => [p.id, p]), - ); - const byKey = new Map( - catalog.map((p) => [p.key, p]), + liveCatalog.map((p) => [p.id, p]), ); const selectedFromIds: OuterPartCatalogItem[] = []; @@ -7655,43 +7634,20 @@ export class ClaimRequestManagementService { const item = byId.get(id); if (!item) { throw new BadRequestException( - `Invalid outer part id for ${selectedCarType}: ${id}`, + `Invalid outer part id: ${id}. Use GET outer-parts-catalog to see valid IDs.`, ); } selectedFromIds.push(item); } } - // Backward compatibility with selectedParts + legacy carPartDamage - let selectedParts = Array.isArray((body as any)?.selectedParts) - ? ((body as any).selectedParts as string[]) - : []; - if (selectedParts.length === 0 && (body as any)?.carPartDamage) { - this.assertCarPartDamageAtMostTwoOfFourSides( - (body as any).carPartDamage as CarDamagePartDto, + if (selectedFromIds.length === 0) { + throw new BadRequestException( + "selectedPartIds is required and must contain at least one valid Fanavaran part ID", ); - selectedParts = this.carDamagePartDtoToOuterPartSlugs( - (body as any).carPartDamage as CarDamagePartDto, - ); - } - const selectedFromKeys: OuterPartCatalogItem[] = []; - for (const key of selectedParts) { - const item = byKey.get(key); - if (!item) { - throw new BadRequestException( - `Invalid outer part key for ${selectedCarType}: ${key}`, - ); - } - selectedFromKeys.push(item); } - const selectedItems = - selectedFromIds.length > 0 ? selectedFromIds : selectedFromKeys; - if (selectedItems.length === 0) { - throw new BadRequestException( - "selectedPartIds or selectedParts is required and must contain at least one item", - ); - } + const selectedItems = selectedFromIds; // DISABLED: At most two non-top sides allowed // const sideSet = new Set( @@ -7706,7 +7662,7 @@ export class ClaimRequestManagementService { // } const selectedPartDocs = selectedItems.map((p) => - catalogItemToSelectedPart(p, catalog), + catalogItemToSelectedPart(p, liveCatalog), ); const damagedPartsInitial = selectedPartDocs.map((p) => ({ id: p.id ?? undefined, @@ -8061,18 +8017,12 @@ export class ClaimRequestManagementService { "Only the claim owner can view capture requirements", ); - // Build car-type aware outer-parts lookup (complete source: static catalog). - // Uses the raw catalog shape (`{id, key, titleFa, side, ...}`) because the - // map below is keyed on the full `key` (e.g. `left_backfender`). - const selectedCarType = claimCase.vehicle?.carType as - | ClaimVehicleTypeV2 - | undefined; - const catalogForType = - selectedCarType && OUTER_PARTS_BY_CAR_TYPE[selectedCarType] - ? OUTER_PARTS_BY_CAR_TYPE[selectedCarType] - : this.getOuterPartsRawCatalog(); + // Build outer-parts lookup for resolving stored DB parts. + // Uses the static snapshot (FANAVARAN_CAR_PARTS_CATALOG) which is keyed + // by numeric id (key === String(id)). This is only needed for legacy + // migration of parts stored before the Fanavaran catalog change. const catalogByKey = new Map(); - for (const item of catalogForType) { + for (const item of FANAVARAN_CAR_PARTS_CATALOG) { if (!catalogByKey.has(item.key)) catalogByKey.set(item.key, item); } diff --git a/src/claim-request-management/claim-request-management.v2.controller.ts b/src/claim-request-management/claim-request-management.v2.controller.ts index d6d050a..b3628bb 100644 --- a/src/claim-request-management/claim-request-management.v2.controller.ts +++ b/src/claim-request-management/claim-request-management.v2.controller.ts @@ -61,7 +61,6 @@ import { ListQueryV2Dto } from "src/common/dto/list-query-v2.dto"; import { ClaimDetailsV2ResponseDto } from "./dto/claim-details-v2.dto"; import { UserObjectionV2Dto } from "./dto/user-objection-v2.dto"; import { UserRatingDto } from "./dto/user-rating.dto"; -import { ClaimVehicleTypeV2 } from "src/static/outer-car-parts-catalog"; @ApiTags("claim-request-management (v2)") @Controller("v2/claim-request-management") @@ -452,17 +451,15 @@ export class ClaimRequestManagementV2Controller { @ApiOperation({ summary: "Get outer parts catalog (V2)", description: - "Returns outer-damage parts with id/key/side. Optional `carType` filter returns only that type catalog.", + "Returns the Fanavaran car-components list. All vehicle types share the same catalog.", }) @ApiResponse({ status: 200, description: "Outer parts catalog", type: [OuterPartCatalogItemDto], }) - async getOuterPartsCatalog( - @Query("carType") carType?: ClaimVehicleTypeV2, - ): Promise { - return this.claimRequestManagementService.getOuterPartsCatalogV2(carType); + async getOuterPartsCatalog(): Promise { + return await this.claimRequestManagementService.getOuterPartsCatalogV2(); } @Get("branches/:insuranceId") diff --git a/src/claim-request-management/dto/select-outer-parts-v2.dto.ts b/src/claim-request-management/dto/select-outer-parts-v2.dto.ts index abeda1a..912b19c 100644 --- a/src/claim-request-management/dto/select-outer-parts-v2.dto.ts +++ b/src/claim-request-management/dto/select-outer-parts-v2.dto.ts @@ -8,69 +8,20 @@ import { IsOptional, IsInt, } from "class-validator"; -import { - ClaimVehicleTypeV2, - OuterPartSideV2, -} from "src/static/outer-car-parts-catalog"; +import { ClaimVehicleTypeV2 } from "src/static/outer-car-parts-catalog"; import { DamageSelectedPartV2BodyDto } from "./damage-selected-part-v2.dto"; /** - * Enum for valid outer car parts that can be damaged - * Follows the naming convention from workflow step definition - */ -export enum OuterCarPart { - // Hood - HOOD = "hood", - - // Doors - FRONT_RIGHT_DOOR = "front_right_door", - FRONT_LEFT_DOOR = "front_left_door", - REAR_RIGHT_DOOR = "rear_right_door", - REAR_LEFT_DOOR = "rear_left_door", - - // Bumpers - FRONT_BUMPER = "front_bumper", - REAR_BUMPER = "rear_bumper", - - // Fenders - FRONT_RIGHT_FENDER = "front_right_fender", - FRONT_LEFT_FENDER = "front_left_fender", - REAR_RIGHT_FENDER = "rear_right_fender", - REAR_LEFT_FENDER = "rear_left_fender", - - // Trunk & Roof - TRUNK = "trunk", - ROOF = "roof", -} - -/** - * V2 DTO for selecting damaged outer car parts - * Much cleaner than the nested boolean structure + * V2 DTO for selecting damaged outer car parts. + * Submit `selectedPartIds` with IDs from the `GET outer-parts-catalog` response. */ export class SelectOuterPartsV2Dto { - @ApiProperty({ - description: "Array of selected damaged outer car parts", - example: ["hood", "front_right_door", "rear_bumper", "roof"], - enum: OuterCarPart, - isArray: true, - minItems: 1, - maxItems: 13, - }) - @IsOptional() - @IsArray({ message: "selectedParts must be an array" }) - @ArrayMinSize(1, { message: "At least one damaged part must be selected" }) - @ArrayUnique({ message: "Duplicate parts are not allowed" }) - @IsEnum(OuterCarPart, { - each: true, - message: "Invalid part name. Must be one of the valid outer car parts", - }) - selectedParts?: OuterCarPart[]; - - @ApiProperty({ - description: "Selected outer part IDs from catalog", - example: [9, 10, 4], + @ApiPropertyOptional({ + description: + "Selected outer part IDs from the Fanavaran car-components catalog. " + + "IDs must match values returned by GET outer-parts-catalog.", + example: [9, 10, 30], type: [Number], - required: false, }) @IsOptional() @IsArray({ message: "selectedPartIds must be an array" }) @@ -79,12 +30,13 @@ export class SelectOuterPartsV2Dto { @IsInt({ each: true, message: "Each selected part ID must be an integer" }) selectedPartIds?: number[]; - @ApiProperty({ - description: "Vehicle type for validating available outer parts", + @ApiPropertyOptional({ + description: + "Vehicle type (sedan, suv, hatchback, pickup, van). Optional — stored for context only; " + + "all vehicle types share the same Fanavaran parts catalog.", enum: ClaimVehicleTypeV2, - required: true, }) - @IsNotEmpty() + @IsOptional() @IsEnum(ClaimVehicleTypeV2) carType?: ClaimVehicleTypeV2; } @@ -156,53 +108,21 @@ export class SetClaimVehicleTypeV2Dto { } /** - * Shape returned by `GET .../outer-parts-catalog` (both user and expert - * controllers). It mirrors how items are persisted under - * `damage.selectedParts` (see `DamageSelectedPartV2BodyDto`) so the front-end - * can match catalog rows to stored selections without any field renaming: - * `name` is side-agnostic, `label_fa` is disambiguated with the side in - * parentheses, and the original full catalog key (`left_backfender`) is - * exposed as `catalogKey`. + * Shape returned by `GET .../outer-parts-catalog`. + * Sourced live from the Fanavaran `car/base-info/car-components` lookup so that + * the list always matches what Fanavaran accepts in `DmgSections[].DmgSectionId`. */ export class OuterPartCatalogItemDto { @ApiProperty({ - description: "Static catalog id (unique across all car types)", - example: 102, + description: + "Fanavaran DmgSectionId — use this value when submitting selectedPartIds", + example: 30, }) id: number; @ApiProperty({ - description: "Side-agnostic part name (matches stored part `name`)", - example: "backWheel", - }) - name: string; - - @ApiProperty({ - description: "Vehicle side / region", - enum: OuterPartSideV2, - example: "left", - }) - side: string; - - @ApiProperty({ - description: - "Display label in Farsi, with side disambiguator in parentheses when needed", - example: "چرخ عقب (چپ)", + description: "Display label in Farsi (Caption from Fanavaran)", + example: "درب جلو سمت راننده", }) label_fa: string; - - @ApiProperty({ - description: "Original full catalog key (matches stored `catalogKey`)", - example: "left_backWheel", - required: false, - }) - catalogKey?: string; - - @ApiProperty({ - description: "Vehicle type this catalog row belongs to", - enum: ClaimVehicleTypeV2, - required: false, - example: "suv", - }) - carType?: ClaimVehicleTypeV2; } diff --git a/src/claim-request-management/expert-initiated-claim.mirror.controller.ts b/src/claim-request-management/expert-initiated-claim.mirror.controller.ts index 501ca09..6670bbc 100644 --- a/src/claim-request-management/expert-initiated-claim.mirror.controller.ts +++ b/src/claim-request-management/expert-initiated-claim.mirror.controller.ts @@ -10,7 +10,6 @@ import { Patch, Post, Put, - Query, UploadedFile, UseGuards, UseInterceptors, @@ -33,7 +32,6 @@ import { CurrentUser } from "src/decorators/user.decorator"; import { MediaPolicyService } from "src/media-policy/media-policy.service"; import { DEFAULT_MEDIA_MAX_BYTES } from "src/client/client.service"; import { RoleEnum } from "src/Types&Enums/role.enum"; -import { ClaimVehicleTypeV2 } from "src/static/outer-car-parts-catalog"; import { ClaimRequestManagementService } from "./claim-request-management.service"; import { OuterPartCatalogItemDto, @@ -101,15 +99,15 @@ export class ExpertInitiatedClaimMirrorController { @ApiOperation({ summary: "Get outer parts catalog (V2)", description: - "Returns outer-damage parts with id/key/side. Optional `carType` filter returns only that type catalog.", + "Returns the Fanavaran car-components list. All vehicle types share the same catalog.", }) @ApiResponse({ status: 200, description: "Outer parts catalog", type: [OuterPartCatalogItemDto], }) - async getOuterPartsCatalog(@Query("carType") carType?: ClaimVehicleTypeV2) { - return this.claimRequestManagementService.getOuterPartsCatalogV2(carType); + async getOuterPartsCatalog() { + return await this.claimRequestManagementService.getOuterPartsCatalogV2(); } @Get("car-other-part") diff --git a/src/claim-request-management/registrar-claim.mirror.controller.ts b/src/claim-request-management/registrar-claim.mirror.controller.ts index 2d554d0..845dde9 100644 --- a/src/claim-request-management/registrar-claim.mirror.controller.ts +++ b/src/claim-request-management/registrar-claim.mirror.controller.ts @@ -7,7 +7,6 @@ import { Param, Patch, Post, - Query, UploadedFile, UseGuards, UseInterceptors, @@ -30,7 +29,6 @@ import { CurrentUser } from "src/decorators/user.decorator"; import { MediaPolicyService } from "src/media-policy/media-policy.service"; import { DEFAULT_MEDIA_MAX_BYTES } from "src/client/client.service"; import { RoleEnum } from "src/Types&Enums/role.enum"; -import { ClaimVehicleTypeV2 } from "src/static/outer-car-parts-catalog"; import { ClaimRequestManagementService } from "./claim-request-management.service"; import { OuterPartCatalogItemDto, @@ -96,15 +94,15 @@ export class RegistrarClaimMirrorController { @ApiOperation({ summary: "Get outer parts catalog (V2)", description: - "Returns outer-damage parts with id/key/side. Optional `carType` filter returns only that type catalog.", + "Returns the Fanavaran car-components list. All vehicle types share the same catalog.", }) @ApiResponse({ status: 200, description: "Outer parts catalog", type: [OuterPartCatalogItemDto], }) - async getOuterPartsCatalog(@Query("carType") carType?: ClaimVehicleTypeV2) { - return this.claimRequestManagementService.getOuterPartsCatalogV2(carType); + async getOuterPartsCatalog() { + return await this.claimRequestManagementService.getOuterPartsCatalogV2(); } @Get("car-other-part") diff --git a/src/expert-claim/expert-claim.v2.controller.ts b/src/expert-claim/expert-claim.v2.controller.ts index b1e8735..c506e11 100644 --- a/src/expert-claim/expert-claim.v2.controller.ts +++ b/src/expert-claim/expert-claim.v2.controller.ts @@ -50,7 +50,6 @@ import { import { FactorValidationV2Dto } from "./dto/factor-validation.dto"; import { ClaimRequestManagementService } from "src/claim-request-management/claim-request-management.service"; import { OuterPartCatalogItemDto } from "src/claim-request-management/dto/select-outer-parts-v2.dto"; -import { ClaimVehicleTypeV2 } from "src/static/outer-car-parts-catalog"; class InPersonVisitV2Dto { @ApiPropertyOptional({ example: "Paint damage requires physical inspection" }) @@ -105,17 +104,15 @@ export class ExpertClaimV2Controller { @ApiOperation({ summary: "Get outer parts catalog (V2)", description: - "Returns outer-damage parts with id/key/side. Optional `carType` filter returns only that type catalog.", + "Returns the Fanavaran car-components list. All vehicle types share the same catalog.", }) @ApiResponse({ status: 200, description: "Outer parts catalog", type: [OuterPartCatalogItemDto], }) - async getOuterPartsCatalog( - @Query("carType") carType?: ClaimVehicleTypeV2, - ): Promise { - return this.claimRequestManagementService.getOuterPartsCatalogV2(carType); + async getOuterPartsCatalog(): Promise { + return await this.claimRequestManagementService.getOuterPartsCatalogV2(); } @Get("inner-parts-catalog") diff --git a/src/helpers/outer-damage-parts.ts b/src/helpers/outer-damage-parts.ts index 1f97cfb..38fc5bd 100644 --- a/src/helpers/outer-damage-parts.ts +++ b/src/helpers/outer-damage-parts.ts @@ -1,5 +1,6 @@ import { ClaimVehicleTypeV2, + FANAVARAN_CAR_PARTS_CATALOG, OUTER_PARTS_BY_CAR_TYPE, OuterPartCatalogItem, OuterPartSideV2, @@ -93,12 +94,14 @@ export function sameCatalogPartId( return na != null && nb != null && na === nb; } -/** API part id: numeric catalog id only (`null` for internal / non-catalog lines). */ +/** API part id: numeric catalog id only (`null` for free-text / non-catalog lines). */ export function catalogPartIdFromSelectedPart( sp: DamageSelectedPartV2, ): number | null { - if (isInternalDamageSide(sp.side)) return null; + // Parts from the Fanavaran catalog always carry a numeric id regardless of side. if (sp.id != null && Number.isFinite(sp.id)) return sp.id; + // Legacy parts without an id and with an internal/unknown side are free-text only. + if (isInternalDamageSide(sp.side)) return null; return null; } @@ -153,12 +156,13 @@ export function disambiguateOuterPartLabelFa( return `${t} (${sideFa})`; } -const CATALOG_ITEM_BY_ID = new Map(); -for (const list of Object.values(OUTER_PARTS_BY_CAR_TYPE)) { - for (const it of list) { - CATALOG_ITEM_BY_ID.set(it.id, it); - } -} +/** + * Static id → item map for legacy DB part resolution. + * Built from the Fanavaran snapshot; unique because all car types share the same list. + */ +const CATALOG_ITEM_BY_ID = new Map( + FANAVARAN_CAR_PARTS_CATALOG.map((it) => [it.id, it]), +); function findCatalogItemByKey(key: string): OuterPartCatalogItem | undefined { for (const list of Object.values(OUTER_PARTS_BY_CAR_TYPE)) { @@ -681,7 +685,7 @@ export function normalizeCarPartDamageForExpertReply( ? o.side.trim().toLowerCase() : ""; - if (name && sideIn) { + if (name) { const id = toNum(o.id); const ck = typeof o.catalogKey === "string" && o.catalogKey.trim() @@ -689,36 +693,28 @@ export function normalizeCarPartDamageForExpertReply( : undefined; const catalog = carType ? OUTER_PARTS_BY_CAR_TYPE[carType] : undefined; let catItem: OuterPartCatalogItem | undefined; - if (catalog) { + // Try id-based lookup first (works for Fanavaran parts which have no side) + if (id != null) { + catItem = catalog?.find((c) => c.id === id) ?? CATALOG_ITEM_BY_ID.get(id) ?? undefined; + } + if (!catItem && catalog) { const byKey = new Map(catalog.map((c) => [c.key, c])); if (ck) catItem = byKey.get(ck); - if (!catItem && id != null) catItem = catalog.find((c) => c.id === id); - if (!catItem) { + if (!catItem && sideIn) { catItem = byKey.get(catalogLikeKeyFromPart({ side: sideIn, name })); } } - if (!catItem && id != null) { - catItem = CATALOG_ITEM_BY_ID.get(id!) ?? undefined; - } - const list = catalog?.length - ? catalog - : catItem - ? outerCatalogListForItem(catItem) - : []; - if (catItem && list.length) { + if (!catItem && ck) catItem = findCatalogItemByKey(ck); + if (catItem) { return selectedPartToStoredRecord( - catalogItemToSelectedPart(catItem, list), + catalogItemToSelectedPart(catItem, outerCatalogListForItem(catItem)), ); } const label_fa = typeof o.label_fa === "string" && o.label_fa.trim() ? o.label_fa.trim() : name; - const out: Record = { - name, - side: sideIn, - label_fa, - }; + const out: Record = { name, side: sideIn, label_fa }; if (id != null) out.id = id; if (ck) out.catalogKey = ck; return out; @@ -731,9 +727,8 @@ export function normalizeCarPartDamageForExpertReply( carType, ); if (catItem) { - const list = outerCatalogListForItem(catItem); return selectedPartToStoredRecord( - catalogItemToSelectedPart(catItem, list), + catalogItemToSelectedPart(catItem, outerCatalogListForItem(catItem)), ); } const sideFa = SIDE_LABEL_FA[legacySide] || legacySide; @@ -744,7 +739,7 @@ export function normalizeCarPartDamageForExpertReply( }; } - throw new Error("carPartDamage must include side and (name or part)"); + throw new Error("carPartDamage must include a name or part field"); } /** Best-effort migration for API output; never throws. */ diff --git a/src/static/fanavaran-car-components.snapshot.json b/src/static/fanavaran-car-components.snapshot.json new file mode 100644 index 0000000..f68898a --- /dev/null +++ b/src/static/fanavaran-car-components.snapshot.json @@ -0,0 +1,857 @@ +[ + { + "Caption": "جلو کامل", + "DmgBusinessLineId": 5454, + "FromDate": "1300/01/01", + "GroupId": null, + "Id": 1, + "ToDate": "1379/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "جلو چپ", + "DmgBusinessLineId": 5454, + "FromDate": "1300/01/01", + "GroupId": null, + "Id": 2, + "ToDate": "1379/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "جلو راست", + "DmgBusinessLineId": 5454, + "FromDate": "1300/01/01", + "GroupId": null, + "Id": 3, + "ToDate": "1379/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "عقب کامل", + "DmgBusinessLineId": 5454, + "FromDate": "1300/01/01", + "GroupId": null, + "Id": 4, + "ToDate": "1379/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "عقب چپ", + "DmgBusinessLineId": 5454, + "FromDate": "1300/01/01", + "GroupId": null, + "Id": 5, + "ToDate": "1379/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "عقب راست", + "DmgBusinessLineId": 5454, + "FromDate": "1300/01/01", + "GroupId": null, + "Id": 6, + "ToDate": "1379/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "بدنه راست", + "DmgBusinessLineId": 5454, + "FromDate": "1300/01/01", + "GroupId": null, + "Id": 7, + "ToDate": "1379/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "بدنه چپ", + "DmgBusinessLineId": 5454, + "FromDate": "1300/01/01", + "GroupId": null, + "Id": 8, + "ToDate": "1379/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "سپر جلو", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 9, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "سپر عقب", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 10, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "آينه سمت راننده", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": 93, + "Id": 11, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "آينه سمت سرنشين", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": 93, + "Id": 12, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "چراغ جلو چپ", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 13, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "چراغ جلو راست", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 14, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "چراغ عقب چپ", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 15, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "چراغ عقب راست", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 16, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "لاستيک جلو چپ", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": 90, + "Id": 17, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "لاستيک جلو راست", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": 90, + "Id": 18, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "لاستيک عقب چپ", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": 90, + "Id": 19, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "لاستيک عقب راست", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": 90, + "Id": 20, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "لاستيک زاپاس", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": 90, + "Id": 21, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "رينگ جلو چپ", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": 90, + "Id": 22, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "رينگ جلو راست", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": 90, + "Id": 23, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "رينگ عقب چپ", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": 90, + "Id": 24, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "رينگ عقب راست", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": 90, + "Id": 25, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "راهنماي جلو چپ", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 26, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "راهنماي جلو راست", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 27, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "راهنماي عقب چپ", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 28, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "راهنماي عقب راست", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 29, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "درب جلو سمت راننده", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 30, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "درب جلو سمت سرنشين", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 31, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "درب عقب سمت راننده", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 32, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "درب عقب سمت سرنشين", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 33, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "گلگير جلو سمت راننده", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 34, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "گلگير جلو سمت سرنشين", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 35, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "گلگير عقب سمت راننده", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 36, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "گلگير عقب سمت سرنشين", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 37, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "ستون جلو چپ", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 38, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "ستون جلو راست", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 39, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "ستون وسط چپ", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 40, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "ستون وسط راست", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 41, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "ستون عقب چپ", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 42, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "ستون عقب راست", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 43, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "شيشه جلو", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": 95, + "Id": 44, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "شيشه عقب", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": 95, + "Id": 45, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "سيني جلو (زير موتور)", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 46, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "درب موتور جلو", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 47, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "درب صندوق عقب", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 48, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "جلو پنجره", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 49, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "سقف خودرو", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 50, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "سانروف", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 51, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "سيني کف صندوق", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 52, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "متعلقات جلوبندي", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 53, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "متعلقات داخل خودرو", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 54, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "اگزوز و متعلقات", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 55, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "بلوکه سيلندر", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 56, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "موتور و متعلقات ( به غير از سيلندر)", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 57, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "شاسي جلو", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 58, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "شاسي عقب", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 59, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "ايربگ", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 60, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "شيشه پنجره درب جلو سمت راننده", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": 95, + "Id": 61, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "شيشه پنجره درب جلو سمت سرنشين", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": 95, + "Id": 62, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "شيشه پنجره درب عقب سمت راننده", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": 95, + "Id": 63, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "شيشه پنجره درب عقب سمت سرنشين", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": 95, + "Id": 64, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "بادگير شيشه ها", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 65, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "برف پاک کن", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 66, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "رکاب ها", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 67, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "درپوش باک بنزين", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 68, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "سامانه برق رساني (به جزء باتري)", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 69, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "باتري", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 70, + "ToDate": "1999/12/29", + "UsedPlaceId": 5450 + }, + { + "Caption": "سيستم تهويه مطبوع (به جزء کولر)", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 71, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "کولر", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 72, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "خسارتهاي پنهان", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 73, + "ToDate": "1405/03/03", + "UsedPlaceId": 5449 + }, + { + "Caption": "کلاف", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 74, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "کف اتاق", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 75, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "سيني عقب", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 76, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "حسگرها", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 77, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "قفل در", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 78, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "لوازم تزيني", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 79, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "دستگيره ها", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 80, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "زه ها", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 81, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "ديفيوژر", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 82, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "رادياتور", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 83, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "فلاپ ها", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 84, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "دوربين", + "DmgBusinessLineId": 5454, + "FromDate": "1380/01/01", + "GroupId": null, + "Id": 85, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "اتاق", + "DmgBusinessLineId": 5454, + "FromDate": "1404/03/04", + "GroupId": null, + "Id": 86, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "موتور", + "DmgBusinessLineId": 5454, + "FromDate": "1404/03/04", + "GroupId": null, + "Id": 87, + "ToDate": "1999/12/29", + "UsedPlaceId": 5449 + }, + { + "Caption": "سيستم صوتي و تصويري", + "DmgBusinessLineId": 5453, + "FromDate": "1300/01/01", + "GroupId": null, + "Id": 88, + "ToDate": "1999/12/29", + "UsedPlaceId": 5451 + }, + { + "Caption": "ساير وسايل صوتي", + "DmgBusinessLineId": 5453, + "FromDate": "1300/01/01", + "GroupId": 88, + "Id": 89, + "ToDate": "1999/12/29", + "UsedPlaceId": 5450 + }, + { + "Caption": "رينگ و لاستيك", + "DmgBusinessLineId": 5453, + "FromDate": "1300/01/01", + "GroupId": null, + "Id": 90, + "ToDate": "1999/12/29", + "UsedPlaceId": 5448 + }, + { + "Caption": "ساير (بجز لوازم صوتي و تصويري )", + "DmgBusinessLineId": 5453, + "FromDate": "1300/01/01", + "GroupId": null, + "Id": 91, + "ToDate": "1999/12/29", + "UsedPlaceId": 5450 + }, + { + "Caption": "كليه قطعات فابريك", + "DmgBusinessLineId": 5453, + "FromDate": "1300/01/01", + "GroupId": null, + "Id": 92, + "ToDate": "1999/12/29", + "UsedPlaceId": 5448 + }, + { + "Caption": "آينه بغل", + "DmgBusinessLineId": 5453, + "FromDate": "1300/01/01", + "GroupId": null, + "Id": 93, + "ToDate": "1999/12/29", + "UsedPlaceId": 5448 + }, + { + "Caption": "راديو پخش", + "DmgBusinessLineId": 5453, + "FromDate": "1300/01/01", + "GroupId": 88, + "Id": 94, + "ToDate": "1999/12/29", + "UsedPlaceId": 5450 + }, + { + "Caption": "شيشه", + "DmgBusinessLineId": 5453, + "FromDate": "1300/01/01", + "GroupId": null, + "Id": 95, + "ToDate": "1999/12/29", + "UsedPlaceId": 5448 + } +] diff --git a/src/static/outer-car-parts-catalog.ts b/src/static/outer-car-parts-catalog.ts index 3ad8a59..b87169c 100644 --- a/src/static/outer-car-parts-catalog.ts +++ b/src/static/outer-car-parts-catalog.ts @@ -1,3 +1,14 @@ +/** + * Outer car parts catalog — shaped around the Fanavaran `car/base-info/car-components` + * API response. + * + * The *live* catalog is fetched at runtime through `FanavaranLookupService` so + * any changes Fanavaran makes to their part list are picked up automatically. + * `FANAVARAN_CAR_PARTS_CATALOG` below is a **static fallback snapshot** used + * only for legacy in-DB part resolution (i.e. parts that were stored before + * this change). It must not be used as the source for API responses. + */ + export enum ClaimVehicleTypeV2 { SEDAN = "sedan", SUV = "suv", @@ -16,58 +27,132 @@ export enum OuterPartSideV2 { export type OuterPartCatalogItem = { id: number; + /** String key — equals `String(id)` for Fanavaran-sourced items. */ key: string; titleFa: string; - side: OuterPartSideV2; + /** Side is encoded in the Farsi caption for Fanavaran items; empty string for those. */ + side: OuterPartSideV2 | ""; carType?: ClaimVehicleTypeV2; }; -const baseFor = (offset: number): OuterPartCatalogItem[] => [ - // left - { id: offset + 1, key: "left_backfender", titleFa: "گلگیر عقب", side: OuterPartSideV2.LEFT }, - { id: offset + 2, key: "left_backWheel", titleFa: "چرخ عقب", side: OuterPartSideV2.LEFT }, - { id: offset + 3, key: "left_backDoor", titleFa: "در عقب", side: OuterPartSideV2.LEFT }, - { id: offset + 4, key: "left_frontDoor", titleFa: "در جلو", side: OuterPartSideV2.LEFT }, - { id: offset + 5, key: "left_mirror", titleFa: "آیینه", side: OuterPartSideV2.LEFT }, - { id: offset + 6, key: "left_frontWheel", titleFa: "چرخ جلو", side: OuterPartSideV2.LEFT }, - { id: offset + 7, key: "left_frontFender", titleFa: "گلگیر جلو", side: OuterPartSideV2.LEFT }, - { id: offset + 8, key: "left_backWindow", titleFa: "شیشه عقب", side: OuterPartSideV2.LEFT }, - { id: offset + 9, key: "left_frontWindow", titleFa: "شیشه جلو", side: OuterPartSideV2.LEFT }, - // right - { id: offset + 10, key: "right_backfender", titleFa: "گلگیر عقب", side: OuterPartSideV2.RIGHT }, - { id: offset + 11, key: "right_backWheel", titleFa: "چرخ عقب", side: OuterPartSideV2.RIGHT }, - { id: offset + 12, key: "right_backDoor", titleFa: "در عقب", side: OuterPartSideV2.RIGHT }, - { id: offset + 13, key: "right_frontDoor", titleFa: "در جلو", side: OuterPartSideV2.RIGHT }, - { id: offset + 14, key: "right_mirror", titleFa: "آیینه", side: OuterPartSideV2.RIGHT }, - { id: offset + 15, key: "right_frontWheel", titleFa: "چرخ جلو", side: OuterPartSideV2.RIGHT }, - { id: offset + 16, key: "right_frontFender", titleFa: "گلگیر جلو", side: OuterPartSideV2.RIGHT }, - { id: offset + 17, key: "right_backWindow", titleFa: "شیشه عقب", side: OuterPartSideV2.RIGHT }, - { id: offset + 18, key: "right_frontWindow", titleFa: "شیشه جلو", side: OuterPartSideV2.RIGHT }, - // front - { id: offset + 19, key: "front_frontBumper", titleFa: "سپر جلو", side: OuterPartSideV2.FRONT }, - { id: offset + 20, key: "front_frontCarWindshield", titleFa: "شیشه جلو", side: OuterPartSideV2.FRONT }, - { id: offset + 21, key: "front_carHood", titleFa: "کاپوت", side: OuterPartSideV2.FRONT }, - { id: offset + 22, key: "front_leftLight", titleFa: "چراغ چپ", side: OuterPartSideV2.FRONT }, - { id: offset + 23, key: "front_rightLight", titleFa: "چراغ راست", side: OuterPartSideV2.FRONT }, - { id: offset + 24, key: "front_frontGrille", titleFa: "جلو پنجره", side: OuterPartSideV2.FRONT }, - // back - { id: offset + 25, key: "back_backBumper", titleFa: "سپر عقب", side: OuterPartSideV2.BACK }, - { id: offset + 26, key: "back_carTrunk", titleFa: "صندوق عقب", side: OuterPartSideV2.BACK }, - { id: offset + 27, key: "back_backCarWindshield", titleFa: "شیشه عقب", side: OuterPartSideV2.BACK }, - { id: offset + 28, key: "back_leftLight", titleFa: "چراغ چپ", side: OuterPartSideV2.BACK }, - { id: offset + 29, key: "back_rightLight", titleFa: "چراغ راست", side: OuterPartSideV2.BACK }, - // top - { id: offset + 30, key: "top_roof", titleFa: "سقف", side: OuterPartSideV2.TOP }, +/** + * Snapshot of the Fanavaran `car/base-info/car-components` lookup. + * Used ONLY as a static fallback when resolving legacy part IDs already stored + * in the database. All live API endpoints must call `FanavaranLookupService` + * instead. + */ +export const FANAVARAN_CAR_PARTS_CATALOG: OuterPartCatalogItem[] = [ + { id: 1, key: "1", titleFa: "جلو کامل", side: "" }, + { id: 2, key: "2", titleFa: "جلو چپ", side: "" }, + { id: 3, key: "3", titleFa: "جلو راست", side: "" }, + { id: 4, key: "4", titleFa: "عقب کامل", side: "" }, + { id: 5, key: "5", titleFa: "عقب چپ", side: "" }, + { id: 6, key: "6", titleFa: "عقب راست", side: "" }, + { id: 7, key: "7", titleFa: "بدنه راست", side: "" }, + { id: 8, key: "8", titleFa: "بدنه چپ", side: "" }, + { id: 9, key: "9", titleFa: "سپر جلو", side: "" }, + { id: 10, key: "10", titleFa: "سپر عقب", side: "" }, + { id: 11, key: "11", titleFa: "آينه سمت راننده", side: "" }, + { id: 12, key: "12", titleFa: "آينه سمت سرنشين", side: "" }, + { id: 13, key: "13", titleFa: "چراغ جلو چپ", side: "" }, + { id: 14, key: "14", titleFa: "چراغ جلو راست", side: "" }, + { id: 15, key: "15", titleFa: "چراغ عقب چپ", side: "" }, + { id: 16, key: "16", titleFa: "چراغ عقب راست", side: "" }, + { id: 17, key: "17", titleFa: "لاستيک جلو چپ", side: "" }, + { id: 18, key: "18", titleFa: "لاستيک جلو راست", side: "" }, + { id: 19, key: "19", titleFa: "لاستيک عقب چپ", side: "" }, + { id: 20, key: "20", titleFa: "لاستيک عقب راست", side: "" }, + { id: 21, key: "21", titleFa: "لاستيک زاپاس", side: "" }, + { id: 22, key: "22", titleFa: "رينگ جلو چپ", side: "" }, + { id: 23, key: "23", titleFa: "رينگ جلو راست", side: "" }, + { id: 24, key: "24", titleFa: "رينگ عقب چپ", side: "" }, + { id: 25, key: "25", titleFa: "رينگ عقب راست", side: "" }, + { id: 26, key: "26", titleFa: "راهنماي جلو چپ", side: "" }, + { id: 27, key: "27", titleFa: "راهنماي جلو راست", side: "" }, + { id: 28, key: "28", titleFa: "راهنماي عقب چپ", side: "" }, + { id: 29, key: "29", titleFa: "راهنماي عقب راست", side: "" }, + { id: 30, key: "30", titleFa: "درب جلو سمت راننده", side: "" }, + { id: 31, key: "31", titleFa: "درب جلو سمت سرنشين", side: "" }, + { id: 32, key: "32", titleFa: "درب عقب سمت راننده", side: "" }, + { id: 33, key: "33", titleFa: "درب عقب سمت سرنشين", side: "" }, + { id: 34, key: "34", titleFa: "گلگير جلو سمت راننده", side: "" }, + { id: 35, key: "35", titleFa: "گلگير جلو سمت سرنشين", side: "" }, + { id: 36, key: "36", titleFa: "گلگير عقب سمت راننده", side: "" }, + { id: 37, key: "37", titleFa: "گلگير عقب سمت سرنشين", side: "" }, + { id: 38, key: "38", titleFa: "ستون جلو چپ", side: "" }, + { id: 39, key: "39", titleFa: "ستون جلو راست", side: "" }, + { id: 40, key: "40", titleFa: "ستون وسط چپ", side: "" }, + { id: 41, key: "41", titleFa: "ستون وسط راست", side: "" }, + { id: 42, key: "42", titleFa: "ستون عقب چپ", side: "" }, + { id: 43, key: "43", titleFa: "ستون عقب راست", side: "" }, + { id: 44, key: "44", titleFa: "شيشه جلو", side: "" }, + { id: 45, key: "45", titleFa: "شيشه عقب", side: "" }, + { id: 46, key: "46", titleFa: "سيني جلو (زير موتور)", side: "" }, + { id: 47, key: "47", titleFa: "درب موتور جلو", side: "" }, + { id: 48, key: "48", titleFa: "درب صندوق عقب", side: "" }, + { id: 49, key: "49", titleFa: "جلو پنجره", side: "" }, + { id: 50, key: "50", titleFa: "سقف خودرو", side: "" }, + { id: 51, key: "51", titleFa: "سانروف", side: "" }, + { id: 52, key: "52", titleFa: "سيني کف صندوق", side: "" }, + { id: 53, key: "53", titleFa: "متعلقات جلوبندي", side: "" }, + { id: 54, key: "54", titleFa: "متعلقات داخل خودرو", side: "" }, + { id: 55, key: "55", titleFa: "اگزوز و متعلقات", side: "" }, + { id: 56, key: "56", titleFa: "بلوکه سيلندر", side: "" }, + { id: 57, key: "57", titleFa: "موتور و متعلقات ( به غير از سيلندر)", side: "" }, + { id: 58, key: "58", titleFa: "شاسي جلو", side: "" }, + { id: 59, key: "59", titleFa: "شاسي عقب", side: "" }, + { id: 60, key: "60", titleFa: "ايربگ", side: "" }, + { id: 61, key: "61", titleFa: "شيشه پنجره درب جلو سمت راننده", side: "" }, + { id: 62, key: "62", titleFa: "شيشه پنجره درب جلو سمت سرنشين", side: "" }, + { id: 63, key: "63", titleFa: "شيشه پنجره درب عقب سمت راننده", side: "" }, + { id: 64, key: "64", titleFa: "شيشه پنجره درب عقب سمت سرنشين", side: "" }, + { id: 65, key: "65", titleFa: "بادگير شيشه ها", side: "" }, + { id: 66, key: "66", titleFa: "برف پاک کن", side: "" }, + { id: 67, key: "67", titleFa: "رکاب ها", side: "" }, + { id: 68, key: "68", titleFa: "درپوش باک بنزين", side: "" }, + { id: 69, key: "69", titleFa: "سامانه برق رساني (به جزء باتري)", side: "" }, + { id: 70, key: "70", titleFa: "باتري", side: "" }, + { id: 71, key: "71", titleFa: "سيستم تهويه مطبوع (به جزء کولر)", side: "" }, + { id: 72, key: "72", titleFa: "کولر", side: "" }, + { id: 73, key: "73", titleFa: "خسارتهاي پنهان", side: "" }, + { id: 74, key: "74", titleFa: "کلاف", side: "" }, + { id: 75, key: "75", titleFa: "کف اتاق", side: "" }, + { id: 76, key: "76", titleFa: "سيني عقب", side: "" }, + { id: 77, key: "77", titleFa: "حسگرها", side: "" }, + { id: 78, key: "78", titleFa: "قفل در", side: "" }, + { id: 79, key: "79", titleFa: "لوازم تزيني", side: "" }, + { id: 80, key: "80", titleFa: "دستگيره ها", side: "" }, + { id: 81, key: "81", titleFa: "زه ها", side: "" }, + { id: 82, key: "82", titleFa: "ديفيوژر", side: "" }, + { id: 83, key: "83", titleFa: "رادياتور", side: "" }, + { id: 84, key: "84", titleFa: "فلاپ ها", side: "" }, + { id: 85, key: "85", titleFa: "دوربين", side: "" }, + { id: 86, key: "86", titleFa: "اتاق", side: "" }, + { id: 87, key: "87", titleFa: "موتور", side: "" }, + { id: 88, key: "88", titleFa: "سيستم صوتي و تصويري", side: "" }, + { id: 89, key: "89", titleFa: "ساير وسايل صوتي", side: "" }, + { id: 90, key: "90", titleFa: "رينگ و لاستيك", side: "" }, + { id: 91, key: "91", titleFa: "ساير (بجز لوازم صوتي و تصويري )", side: "" }, + { id: 92, key: "92", titleFa: "كليه قطعات فابريك", side: "" }, + { id: 93, key: "93", titleFa: "آينه بغل", side: "" }, + { id: 94, key: "94", titleFa: "راديو پخش", side: "" }, + { id: 95, key: "95", titleFa: "شيشه", side: "" }, ]; +/** + * Backward-compatible alias used by `outer-damage-parts.ts` for legacy DB part + * resolution. All vehicle types share the same flat Fanavaran catalog because + * Fanavaran does not differentiate parts by car type. + * + * Do NOT use this for serving API responses — use `FanavaranLookupService` instead. + */ export const OUTER_PARTS_BY_CAR_TYPE: Record< ClaimVehicleTypeV2, OuterPartCatalogItem[] > = { - [ClaimVehicleTypeV2.SEDAN]: baseFor(0), - [ClaimVehicleTypeV2.SUV]: baseFor(100), - [ClaimVehicleTypeV2.HATCHBACK]: baseFor(200), - [ClaimVehicleTypeV2.PICKUP]: baseFor(300), - [ClaimVehicleTypeV2.VAN]: baseFor(400), + [ClaimVehicleTypeV2.SEDAN]: FANAVARAN_CAR_PARTS_CATALOG, + [ClaimVehicleTypeV2.SUV]: FANAVARAN_CAR_PARTS_CATALOG, + [ClaimVehicleTypeV2.HATCHBACK]: FANAVARAN_CAR_PARTS_CATALOG, + [ClaimVehicleTypeV2.PICKUP]: FANAVARAN_CAR_PARTS_CATALOG, + [ClaimVehicleTypeV2.VAN]: FANAVARAN_CAR_PARTS_CATALOG, }; -