forked from Yara724/api
Merge pull request 'fix it' (#170) from s.hajizadeh/yara724api:main into main
Reviewed-on: Yara724/api#170
This commit is contained in:
@@ -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, string[]> = {
|
||||
[ClaimVehicleTypeV2.SEDAN]: ["سواری", "سواري", "sedan"],
|
||||
[ClaimVehicleTypeV2.SUV]: ["شاسی", "شاسي", "suv", "بلند"],
|
||||
[ClaimVehicleTypeV2.HATCHBACK]: ["هاچبک", "هاچ بک", "hatchback"],
|
||||
[ClaimVehicleTypeV2.PICKUP]: ["وانت", "پیکاپ", "pick", "pickup"],
|
||||
[ClaimVehicleTypeV2.VAN]: ["ون", "van"],
|
||||
};
|
||||
|
||||
const FANAVARAN_PLATE_LETTER_CODE: Record<string, number> = {
|
||||
الف: 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<number | null> {
|
||||
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<string, unknown> {
|
||||
}): Promise<Record<string, unknown>> {
|
||||
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<string, unknown>,
|
||||
): Promise<any> {
|
||||
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<string, unknown>,
|
||||
): Promise<any> {
|
||||
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<string, unknown>,
|
||||
): Promise<any> {
|
||||
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<string, unknown>;
|
||||
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<string, unknown>,
|
||||
): Promise<any> {
|
||||
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<string, unknown>,
|
||||
): Promise<any> {
|
||||
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,
|
||||
);
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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<unknown> {
|
||||
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 }[] {
|
||||
|
||||
@@ -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<string, unknown>,
|
||||
) {
|
||||
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<string, unknown>,
|
||||
) {
|
||||
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<string, unknown>,
|
||||
) {
|
||||
const clientKey = this.parseClientParam(client);
|
||||
return await this.claimRequestManagementService.submitFanavaranExpertiseV2(
|
||||
claimCaseId,
|
||||
clientKey,
|
||||
body && Object.keys(body).length > 0 ? body : undefined,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -115,6 +115,15 @@ export class LookupsService {
|
||||
return await this.getClientRemoteLookup("expert-status");
|
||||
}
|
||||
|
||||
async getVehicleKinds(): Promise<any> {
|
||||
return await this.getClientRemoteLookup("vehicle-kinds");
|
||||
}
|
||||
|
||||
async inquiryByVin(vin: string): Promise<unknown> {
|
||||
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;
|
||||
|
||||
Reference in New Issue
Block a user