From d525b8dd0d28cf5d46913c9dfc67f5cc1675c9cf Mon Sep 17 00:00:00 2001 From: "s.hajizadeh" Date: Wed, 1 Jul 2026 18:05:09 +0330 Subject: [PATCH] fix it --- .../claim-request-management.service.ts | 95 ++++++++++++++++--- src/fanavaran/fanavaran-lookup.config.ts | 5 + src/fanavaran/fanavaran-lookup.service.ts | 9 ++ src/fanavaran/fanavaran.controller.ts | 7 ++ src/lookups/lookups.controller.ts | 36 ++++++- src/lookups/lookups.service.ts | 9 ++ 6 files changed, 148 insertions(+), 13 deletions(-) diff --git a/src/claim-request-management/claim-request-management.service.ts b/src/claim-request-management/claim-request-management.service.ts index d2db482..7ba3711 100644 --- a/src/claim-request-management/claim-request-management.service.ts +++ b/src/claim-request-management/claim-request-management.service.ts @@ -241,6 +241,15 @@ const FANAVARAN_ACCIDENT_LOCATION_ADDRESS = "استان تهران شهر تهر const FANAVARAN_DEFAULT_ACCIDENT_CAUSE_ID = 6; const FANAVARAN_PROVISIONAL_ESTIMATE_AMOUNT = 1000; +/** Local carType → Fanavaran vehicle-kind Caption keywords for matching. */ +const VEHICLE_KIND_KEYWORDS: Record = { + [ClaimVehicleTypeV2.SEDAN]: ["سواری", "سواري", "sedan"], + [ClaimVehicleTypeV2.SUV]: ["شاسی", "شاسي", "suv", "بلند"], + [ClaimVehicleTypeV2.HATCHBACK]: ["هاچبک", "هاچ بک", "hatchback"], + [ClaimVehicleTypeV2.PICKUP]: ["وانت", "پیکاپ", "pick", "pickup"], + [ClaimVehicleTypeV2.VAN]: ["ون", "van"], +}; + const FANAVARAN_PLATE_LETTER_CODE: Record = { الف: 1, ب: 2, @@ -3593,10 +3602,52 @@ export class ClaimRequestManagementService { return `${plate.centerDigits}${plate.centerAlphabet}${plate.leftDigits}`; } - private buildFanavaranDamageCasePayload(input: { + private async resolveVehicleKindId( + clientKey: FanavaranClientKey, + carType?: string, + ): Promise { + if (!carType) return null; + + const vehicleType = carType as ClaimVehicleTypeV2; + const keywords = VEHICLE_KIND_KEYWORDS[vehicleType]; + if (!keywords) return null; + + try { + const lookup = this.fanavaranLookupService; + const definition = FANAVARAN_REMOTE_LOOKUPS.find( + (l) => l.name === "vehicle-kinds", + ); + if (!definition) return null; + + const data = (await lookup.getRemoteLookup( + clientKey, + definition.url, + definition.cacheFile, + )) as Array<{ Id?: number; Caption?: string; IsActive?: number }>; + + if (!Array.isArray(data)) return null; + + const normalizedKeywords = keywords.map((k) => k.toLowerCase()); + const match = data.find((item) => { + if (!item.Caption || item.IsActive === 0) return false; + const caption = item.Caption.toLowerCase(); + return normalizedKeywords.some((kw) => caption.includes(kw)); + }); + + return match?.Id ?? null; + } catch { + this.logger.warn( + `Failed to resolve VehicleKindId for carType=${carType}`, + ); + return null; + } + } + + private async buildFanavaranDamageCasePayload(input: { claimCase: any; blameCase?: any; selectedParts: unknown[]; + clientKey: FanavaranClientKey; defaults: { AccidentVehicleUsedId: number; CulpritLicenceTypeId: number; @@ -3607,7 +3658,7 @@ export class ClaimRequestManagementService { DriverIsOwner: number; FaultPercent: number; }; - }): Record { + }): Promise> { const plate = this.resolveOwnershipPlateForClaim( input.claimCase, input.blameCase, @@ -3625,6 +3676,12 @@ export class ClaimRequestManagementService { const inquiryMapped = vehicle?.inquiry?.mapped ?? {}; const inquiryRaw = vehicle?.inquiry?.raw ?? {}; + const carType = input.claimCase?.vehicle?.carType as string | undefined; + const vehicleKindId = await this.resolveVehicleKindId( + input.clientKey, + carType, + ); + return { BeginDate: insurance.startDate ?? null, BuiltYear: @@ -3659,7 +3716,7 @@ export class ClaimRequestManagementService { PlaqueSerial: plate ? String(plate.ir) : null, PolicyNo: insurance.policyNumber ?? null, PreviousPolicyEndDate: insurance.endDate ?? "", - VehicleKindId: null, + VehicleKindId: vehicleKindId, VIN: inquiryMapped.VIN ?? inquiryRaw.VIN ?? null, AccidentVehicleUsedId: input.defaults.AccidentVehicleUsedId, PolicyCINumber: null, @@ -4454,9 +4511,10 @@ export class ClaimRequestManagementService { async submitFanavaranV2( claimCaseId: string, clientKey: FanavaranClientKey, + bodyOverride?: Record, ): Promise { try { - return await this.executeFanavaranV2Submit(claimCaseId, clientKey); + return await this.executeFanavaranV2Submit(claimCaseId, clientKey, bodyOverride); } catch (error) { this.logger.error( `[Fanavaran ${clientKey} V2] Error submitting to Fanavaran`, @@ -4495,10 +4553,11 @@ export class ClaimRequestManagementService { ); } - const payload = this.buildFanavaranDamageCasePayload({ + const payload = await this.buildFanavaranDamageCasePayload({ claimCase, blameCase, selectedParts: claimCase.damage?.selectedParts ?? [], + clientKey, defaults: profile.defaults, }); @@ -4520,6 +4579,7 @@ export class ClaimRequestManagementService { async submitFanavaranDamageCaseV2( claimCaseId: string, clientKey: FanavaranClientKey, + bodyOverride?: Record, ): Promise { try { return await this.executeFanavaranDamageCaseSubmit( @@ -4527,6 +4587,7 @@ export class ClaimRequestManagementService { clientKey, undefined, FanavaranAuditSource.SUBMIT, + bodyOverride, ); } catch (error) { this.logger.error( @@ -5112,15 +5173,22 @@ export class ClaimRequestManagementService { async submitFanavaranExpertiseV2( claimCaseId: string, clientKey: FanavaranClientKey, + bodyOverride?: Record, ): Promise { try { const claimCase = await this.claimCaseDbService.findById(claimCaseId); if (!claimCase) throw new NotFoundException("Claim case not found"); - const { payload, warnings } = await this.buildFanavaranExpertisePayload({ - claimCase, - clientKey, - }); - this.assertFanavaranExpertisePayloadReady(payload, warnings); + let payload: Record; + if (bodyOverride) { + payload = bodyOverride; + } else { + const built = await this.buildFanavaranExpertisePayload({ + claimCase, + clientKey, + }); + payload = built.payload; + this.assertFanavaranExpertisePayloadReady(payload, built.warnings); + } return await this.executeFanavaranExpertiseSubmit( claimCaseId, clientKey, @@ -5625,6 +5693,7 @@ export class ClaimRequestManagementService { clientKey: FanavaranClientKey, selectedParts?: unknown[], auditSource: FanavaranAuditSource = FanavaranAuditSource.AUTO_SUBMIT, + bodyOverride?: Record, ): Promise { const profile = getFanavaranClientProfile(clientKey); const claimCase = await this.claimCaseDbService.findById(claimCaseId); @@ -5652,10 +5721,11 @@ export class ClaimRequestManagementService { ); } - const payload = this.buildFanavaranDamageCasePayload({ + const payload = bodyOverride ?? await this.buildFanavaranDamageCasePayload({ claimCase, blameCase, selectedParts: selectedParts ?? claimCase.damage?.selectedParts ?? [], + clientKey, defaults: profile.defaults, }); const url = `${this.FANAVARAN_SUBMIT_URL}/${claimCase.claimId}/dmg-cases`; @@ -6059,6 +6129,7 @@ export class ClaimRequestManagementService { private async executeFanavaranV2Submit( claimCaseId: string, clientKey: FanavaranClientKey, + bodyOverride?: Record, ): Promise { const profile = getFanavaranClientProfile(clientKey); const logPrefix = `[Fanavaran ${clientKey} V2]`; @@ -6066,7 +6137,7 @@ export class ClaimRequestManagementService { this.logger.log( `${logPrefix} Starting submission for claimCaseId: ${claimCaseId}`, ); - const fanavaranData = await this.previewFanavaranSubmitV2( + const fanavaranData = bodyOverride ?? await this.previewFanavaranSubmitV2( claimCaseId, clientKey, ); diff --git a/src/fanavaran/fanavaran-lookup.config.ts b/src/fanavaran/fanavaran-lookup.config.ts index e7aff0c..921b00f 100644 --- a/src/fanavaran/fanavaran-lookup.config.ts +++ b/src/fanavaran/fanavaran-lookup.config.ts @@ -66,6 +66,11 @@ export const FANAVARAN_REMOTE_LOOKUPS: FanavaranRemoteLookupDefinition[] = [ url: `${FANAVARAN_LOOKUP_BASE_URL}/car/code-list/expert-status`, cacheFile: "expert-status.json", }, + { + name: "vehicle-kinds", + url: `${FANAVARAN_LOOKUP_BASE_URL}/car/base-info/vehicle-kinds`, + cacheFile: "vehicle-kinds.json", + }, ]; export const TEJARAT_STATIC_ACCIDENT_FILES = { diff --git a/src/fanavaran/fanavaran-lookup.service.ts b/src/fanavaran/fanavaran-lookup.service.ts index b4eb609..b7e4ecd 100644 --- a/src/fanavaran/fanavaran-lookup.service.ts +++ b/src/fanavaran/fanavaran-lookup.service.ts @@ -14,6 +14,7 @@ import { type FanavaranClientKey, } from "src/core/config/fanavaran-client.config"; import { + FANAVARAN_LOOKUP_BASE_URL, fanavaranLookupCacheDir, tejaratStaticAccidentFilePath, } from "./fanavaran-lookup.config"; @@ -246,6 +247,14 @@ export class FanavaranLookupService { } } + async inquiryByVin( + clientKey: FanavaranClientKey, + vin: string, + ): Promise { + const url = `${FANAVARAN_LOOKUP_BASE_URL}/car/vehicles/inquiry-by-vin?vin=${encodeURIComponent(vin)}`; + return this.fetchFromFanavaran(clientKey, url); + } + mapFanavaranAccidentCausesToReasonOptions( causes: unknown, ): { id: number; label: string; fanavaran: number }[] { diff --git a/src/fanavaran/fanavaran.controller.ts b/src/fanavaran/fanavaran.controller.ts index 2de4f65..704de81 100644 --- a/src/fanavaran/fanavaran.controller.ts +++ b/src/fanavaran/fanavaran.controller.ts @@ -1,5 +1,6 @@ import { BadRequestException, + Body, Controller, Get, Param, @@ -105,11 +106,13 @@ export class FanavaranController { async submit( @Param("client") client: string, @Param("claimCaseId") claimCaseId: string, + @Body() body?: Record, ) { const clientKey = this.parseClientParam(client); return await this.claimRequestManagementService.submitFanavaranV2( claimCaseId, clientKey, + body && Object.keys(body).length > 0 ? body : undefined, ); } @@ -157,11 +160,13 @@ export class FanavaranController { async submitDamageCase( @Param("client") client: string, @Param("claimCaseId") claimCaseId: string, + @Body() body?: Record, ) { const clientKey = this.parseClientParam(client); return await this.claimRequestManagementService.submitFanavaranDamageCaseV2( claimCaseId, clientKey, + body && Object.keys(body).length > 0 ? body : undefined, ); } @@ -261,11 +266,13 @@ export class FanavaranController { async submitExpertise( @Param("client") client: string, @Param("claimCaseId") claimCaseId: string, + @Body() body?: Record, ) { const clientKey = this.parseClientParam(client); return await this.claimRequestManagementService.submitFanavaranExpertiseV2( claimCaseId, clientKey, + body && Object.keys(body).length > 0 ? body : undefined, ); } diff --git a/src/lookups/lookups.controller.ts b/src/lookups/lookups.controller.ts index bdb6e97..ea77575 100644 --- a/src/lookups/lookups.controller.ts +++ b/src/lookups/lookups.controller.ts @@ -1,8 +1,9 @@ -import { Controller, Get, Param } from "@nestjs/common"; +import { Controller, Get, Param, Query } from "@nestjs/common"; import { ApiOkResponse, ApiOperation, ApiParam, + ApiQuery, ApiTags, } from "@nestjs/swagger"; import { LookupsService } from "./lookups.service"; @@ -171,6 +172,39 @@ export class LookupsController { return await this.lookupsService.getExpertStatus(); } + @Get("vehicle-kinds") + @ApiOperation({ + summary: "Fanavaran GEN.12 vehicle kind lookup", + description: + "Returns values for damage case payload field VehicleKindId from car/base-info/vehicle-kinds.", + }) + @ApiOkResponse({ + description: "Returns Fanavaran vehicle kinds lookup data", + schema: { type: "array", items: { type: "object" } }, + }) + async getVehicleKinds() { + return await this.lookupsService.getVehicleKinds(); + } + + @Get("inquiry-by-vin") + @ApiOperation({ + summary: "Fanavaran vehicle inquiry by VIN", + description: + "Returns vehicle details from Fanavaran for the given VIN number via car/vehicles/inquiry-by-vin.", + }) + @ApiQuery({ + name: "vin", + description: "Vehicle Identification Number (VIN)", + example: "IRNKAEK4150012345", + }) + @ApiOkResponse({ + description: "Returns Fanavaran vehicle inquiry data for the VIN", + schema: { type: "object" }, + }) + async inquiryByVin(@Query("vin") vin: string) { + return await this.lookupsService.inquiryByVin(vin); + } + @Get("fanavaran") @ApiOperation({ summary: "List configured Fanavaran remote lookups", diff --git a/src/lookups/lookups.service.ts b/src/lookups/lookups.service.ts index 9b46426..e8a69e5 100644 --- a/src/lookups/lookups.service.ts +++ b/src/lookups/lookups.service.ts @@ -115,6 +115,15 @@ export class LookupsService { return await this.getClientRemoteLookup("expert-status"); } + async getVehicleKinds(): Promise { + return await this.getClientRemoteLookup("vehicle-kinds"); + } + + async inquiryByVin(vin: string): Promise { + const clientKey = this.activeClientKey(); + return this.fanavaranLookupService.inquiryByVin(clientKey, vin); + } + async getAccidentWay(): Promise<{ id: number; label: string }[]> { const clientKey = this.activeClientKey(); const fileName = TEJARAT_STATIC_ACCIDENT_FILES.accidentWay;