forked from Yara724/api
update the fanavaran for both tejarat no and parsian clients , dont forget about the env files
This commit is contained in:
21
.env.example
21
.env.example
@@ -5,6 +5,10 @@ NODE_ENV =
|
|||||||
PORT =
|
PORT =
|
||||||
CLIENT_ID =
|
CLIENT_ID =
|
||||||
CLIENT_NAME =
|
CLIENT_NAME =
|
||||||
|
FANAVARAN_CLIENT=parsian
|
||||||
|
INSURANCE_CORP_ID='شرکت بيمه پارسيان(بيمه گر)'
|
||||||
|
CLAIM_V2_TOTAL_PAYMENT_CAP_ENABLED=false
|
||||||
|
CLAIM_V2_TOTAL_PAYMENT_CAP_TOMAN=53000000
|
||||||
# ---------------------------------------------
|
# ---------------------------------------------
|
||||||
# 🌐 Application URLs
|
# 🌐 Application URLs
|
||||||
# ---------------------------------------------
|
# ---------------------------------------------
|
||||||
@@ -63,6 +67,23 @@ AUTH_SMS_TEMPLATE =
|
|||||||
EXP_OTP_TIME =
|
EXP_OTP_TIME =
|
||||||
FAKE_OTP =
|
FAKE_OTP =
|
||||||
|
|
||||||
|
# ---------------------------------------------
|
||||||
|
# 🌐 Proxy Configuration (Local Development Only)
|
||||||
|
# ---------------------------------------------
|
||||||
|
# NOTE: These proxy settings are for local development only.
|
||||||
|
# When deploying to the server, comment out or remove these lines
|
||||||
|
# as the server IP is already whitelisted by Fanavaran.
|
||||||
|
# SOCKS_PROXY_HOST = localhost
|
||||||
|
# SOCKS_PROXY_PORT = 6565
|
||||||
|
|
||||||
|
# ---------------------------------------------
|
||||||
|
# 🏢 Fanavaran Insurance Corp
|
||||||
|
# ---------------------------------------------
|
||||||
|
# Caption from Fanavaran insurance-corp lookup used to resolve InsuranceCorpId
|
||||||
|
# for damage-case payloads. Must match a Caption in the insurance-corp code-list.
|
||||||
|
# Example: "شرکت بيمه تجارت نو"
|
||||||
|
INSURANCE_CORP_ID =
|
||||||
|
|
||||||
# ---------------------------------------------
|
# ---------------------------------------------
|
||||||
# ⚙️ Application Features / Flags
|
# ⚙️ Application Features / Flags
|
||||||
# ---------------------------------------------
|
# ---------------------------------------------
|
||||||
|
|||||||
@@ -45,6 +45,7 @@
|
|||||||
"pdfkit": "^0.19.1",
|
"pdfkit": "^0.19.1",
|
||||||
"reflect-metadata": "^0.2.2",
|
"reflect-metadata": "^0.2.2",
|
||||||
"rxjs": "^7.8.1",
|
"rxjs": "^7.8.1",
|
||||||
|
"socks-proxy-agent": "^8.0.4",
|
||||||
"svg-captcha": "^1.4.0"
|
"svg-captcha": "^1.4.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import { HttpModule } from "@nestjs/axios";
|
|
||||||
import { Module } from "@nestjs/common";
|
import { Module } from "@nestjs/common";
|
||||||
import { AiService } from "./ai.service";
|
import { AiService } from "./ai.service";
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [HttpModule],
|
imports: [],
|
||||||
providers: [AiService],
|
providers: [AiService],
|
||||||
exports: [AiService],
|
exports: [AiService],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import { APP_INTERCEPTOR, APP_PIPE } from "@nestjs/core";
|
import { APP_INTERCEPTOR, APP_PIPE } from "@nestjs/core";
|
||||||
import { Module } from "@nestjs/common";
|
import { Module } from "@nestjs/common";
|
||||||
import { ConfigService } from "@nestjs/config";
|
import { ConfigModule, ConfigService } from "@nestjs/config";
|
||||||
|
import { HttpModule } from "@nestjs/axios";
|
||||||
import { UnicodeDigitsNormalizeInterceptor } from "./common/interceptors/unicode-digits-normalize.interceptor";
|
import { UnicodeDigitsNormalizeInterceptor } from "./common/interceptors/unicode-digits-normalize.interceptor";
|
||||||
import { MongooseModule } from "@nestjs/mongoose";
|
import { MongooseModule } from "@nestjs/mongoose";
|
||||||
import { ServeStaticModule } from "@nestjs/serve-static";
|
import { ServeStaticModule } from "@nestjs/serve-static";
|
||||||
@@ -28,9 +29,15 @@ import { WorkflowStepManagementModule } from "./workflow-step-management/workflo
|
|||||||
import { DatabaseModule } from "./core/database/database.module";
|
import { DatabaseModule } from "./core/database/database.module";
|
||||||
import { AppConfigModule } from "./core/config/config.module";
|
import { AppConfigModule } from "./core/config/config.module";
|
||||||
import { SuperAdminModule } from "./super-admin/super-admin.module";
|
import { SuperAdminModule } from "./super-admin/super-admin.module";
|
||||||
|
import { createHttpModuleOptions } from "./core/config/http-proxy.factory";
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
|
HttpModule.registerAsync({
|
||||||
|
imports: [ConfigModule],
|
||||||
|
inject: [ConfigService],
|
||||||
|
useFactory: createHttpModuleOptions,
|
||||||
|
}),
|
||||||
AppConfigModule,
|
AppConfigModule,
|
||||||
DatabaseModule,
|
DatabaseModule,
|
||||||
CronModule,
|
CronModule,
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { Module } from "@nestjs/common";
|
import { Module } from "@nestjs/common";
|
||||||
import { MongooseModule } from "@nestjs/mongoose";
|
import { MongooseModule } from "@nestjs/mongoose";
|
||||||
|
import { HttpModule } from "@nestjs/axios";
|
||||||
|
import { ConfigModule, ConfigService } from "@nestjs/config";
|
||||||
|
import { createHttpModuleOptions } from "src/core/config/http-proxy.factory";
|
||||||
import { AiModule } from "src/ai/ai.module";
|
import { AiModule } from "src/ai/ai.module";
|
||||||
import { SandHubModule } from "src/sand-hub/sand-hub.module";
|
import { SandHubModule } from "src/sand-hub/sand-hub.module";
|
||||||
import { RequestManagementModule } from "src/request-management/request-management.module";
|
import { RequestManagementModule } from "src/request-management/request-management.module";
|
||||||
@@ -52,13 +55,16 @@ import { ClientModule } from "src/client/client.module";
|
|||||||
import { ClaimAccessGuard } from "src/auth/guards/claim-access.guard";
|
import { ClaimAccessGuard } from "src/auth/guards/claim-access.guard";
|
||||||
import { JwtModule } from "@nestjs/jwt";
|
import { JwtModule } from "@nestjs/jwt";
|
||||||
import { MediaPolicyModule } from "src/media-policy/media-policy.module";
|
import { MediaPolicyModule } from "src/media-policy/media-policy.module";
|
||||||
import { HttpModule } from "@nestjs/axios";
|
|
||||||
import { FanavaranAuditModule } from "src/fanavaran/fanavaran-audit.module";
|
import { FanavaranAuditModule } from "src/fanavaran/fanavaran-audit.module";
|
||||||
import { FanavaranLookupModule } from "src/fanavaran/fanavaran-lookup.module";
|
import { FanavaranLookupModule } from "src/fanavaran/fanavaran-lookup.module";
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
HttpModule,
|
HttpModule.registerAsync({
|
||||||
|
imports: [ConfigModule],
|
||||||
|
inject: [ConfigService],
|
||||||
|
useFactory: createHttpModuleOptions,
|
||||||
|
}),
|
||||||
FanavaranAuditModule,
|
FanavaranAuditModule,
|
||||||
FanavaranLookupModule,
|
FanavaranLookupModule,
|
||||||
PublicIdModule,
|
PublicIdModule,
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -86,6 +86,15 @@ export class FanavaranSyncStage {
|
|||||||
|
|
||||||
@Prop({ type: MongooseSchema.Types.Mixed })
|
@Prop({ type: MongooseSchema.Types.Mixed })
|
||||||
response?: unknown;
|
response?: unknown;
|
||||||
|
|
||||||
|
@Prop({ type: Number, default: 0 })
|
||||||
|
retryCount?: number;
|
||||||
|
|
||||||
|
@Prop({ type: Number, default: 2 })
|
||||||
|
maxRetries?: number;
|
||||||
|
|
||||||
|
@Prop({ type: Date })
|
||||||
|
nextRetryAt?: Date;
|
||||||
}
|
}
|
||||||
export const FanavaranSyncStageSchema =
|
export const FanavaranSyncStageSchema =
|
||||||
SchemaFactory.createForClass(FanavaranSyncStage);
|
SchemaFactory.createForClass(FanavaranSyncStage);
|
||||||
|
|||||||
@@ -20,7 +20,9 @@ export class AuthGuard implements CanActivate {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const payload: JwtPayload =
|
const payload: JwtPayload =
|
||||||
await this.jwtService.verifyAsync<JwtPayload>(token);
|
await this.jwtService.verifyAsync<JwtPayload>(token, {
|
||||||
|
secret: `${process.env.JWT_SECRET}`,
|
||||||
|
});
|
||||||
request["user"] = payload;
|
request["user"] = payload;
|
||||||
} catch {
|
} catch {
|
||||||
throw new UnauthorizedException("Invalid or expired token");
|
throw new UnauthorizedException("Invalid or expired token");
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ export interface FanavaranPayloadDefaults {
|
|||||||
AccidentReportTypeId: number;
|
AccidentReportTypeId: number;
|
||||||
AccidentVehicleUsedId: number;
|
AccidentVehicleUsedId: number;
|
||||||
ClaimExpertId: number;
|
ClaimExpertId: number;
|
||||||
|
ExpertiseClaimExpertId: number;
|
||||||
CompensationReferenceId: number;
|
CompensationReferenceId: number;
|
||||||
CulpritLicenceTypeId: number;
|
CulpritLicenceTypeId: number;
|
||||||
CulpritTypeId: number;
|
CulpritTypeId: number;
|
||||||
@@ -74,7 +75,8 @@ const FANAVARAN_CLIENT_PROFILES: Record<
|
|||||||
AccidentCityId: 701,
|
AccidentCityId: 701,
|
||||||
AccidentReportTypeId: 155,
|
AccidentReportTypeId: 155,
|
||||||
AccidentVehicleUsedId: 1,
|
AccidentVehicleUsedId: 1,
|
||||||
ClaimExpertId: 1589,
|
ClaimExpertId: 4543092,
|
||||||
|
ExpertiseClaimExpertId: 4543092,
|
||||||
CompensationReferenceId: 167,
|
CompensationReferenceId: 167,
|
||||||
CulpritLicenceTypeId: 2,
|
CulpritLicenceTypeId: 2,
|
||||||
CulpritTypeId: 337,
|
CulpritTypeId: 337,
|
||||||
@@ -84,7 +86,7 @@ const FANAVARAN_CLIENT_PROFILES: Record<
|
|||||||
PlaqueSampleId: 10,
|
PlaqueSampleId: 10,
|
||||||
DriverIsOwner: 0,
|
DriverIsOwner: 0,
|
||||||
FaultPercent: 100,
|
FaultPercent: 100,
|
||||||
ClaimFileTypeId: 70,
|
ClaimFileTypeId: 23,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
parsian: {
|
parsian: {
|
||||||
@@ -103,6 +105,7 @@ const FANAVARAN_CLIENT_PROFILES: Record<
|
|||||||
AccidentReportTypeId: 155,
|
AccidentReportTypeId: 155,
|
||||||
AccidentVehicleUsedId: 1,
|
AccidentVehicleUsedId: 1,
|
||||||
ClaimExpertId: 154,
|
ClaimExpertId: 154,
|
||||||
|
ExpertiseClaimExpertId: 29,
|
||||||
CompensationReferenceId: 167,
|
CompensationReferenceId: 167,
|
||||||
CulpritLicenceTypeId: 2,
|
CulpritLicenceTypeId: 2,
|
||||||
CulpritTypeId: 337,
|
CulpritTypeId: 337,
|
||||||
|
|||||||
26
src/core/config/http-proxy.factory.ts
Normal file
26
src/core/config/http-proxy.factory.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { ConfigService } from "@nestjs/config";
|
||||||
|
import { HttpModuleOptions } from "@nestjs/axios";
|
||||||
|
import { SocksProxyAgent } from "socks-proxy-agent";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shared HttpModule.registerAsync factory that applies the SOCKS proxy
|
||||||
|
* when SOCKS_PROXY_HOST + SOCKS_PROXY_PORT env vars are set.
|
||||||
|
* Used by every module that imports HttpModule.
|
||||||
|
*/
|
||||||
|
export function createHttpModuleOptions(
|
||||||
|
configService: ConfigService,
|
||||||
|
): HttpModuleOptions | Promise<HttpModuleOptions> {
|
||||||
|
const socksHost = configService.get<string>("SOCKS_PROXY_HOST");
|
||||||
|
const socksPort = configService.get<string>("SOCKS_PROXY_PORT");
|
||||||
|
|
||||||
|
if (socksHost && socksPort) {
|
||||||
|
const proxyUrl = `socks5://${socksHost}:${socksPort}`;
|
||||||
|
const agent = new SocksProxyAgent(proxyUrl);
|
||||||
|
return {
|
||||||
|
httpsAgent: agent,
|
||||||
|
httpAgent: agent,
|
||||||
|
proxy: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
import { HttpModule } from "@nestjs/axios";
|
import { HttpModule } from "@nestjs/axios";
|
||||||
import { Module } from "@nestjs/common";
|
import { Module } from "@nestjs/common";
|
||||||
|
import { ConfigModule, ConfigService } from "@nestjs/config";
|
||||||
|
import { createHttpModuleOptions } from "src/core/config/http-proxy.factory";
|
||||||
import { MongooseModule } from "@nestjs/mongoose";
|
import { MongooseModule } from "@nestjs/mongoose";
|
||||||
import { AiModule } from "src/ai/ai.module";
|
import { AiModule } from "src/ai/ai.module";
|
||||||
import { AuthModule } from "src/auth/auth.module";
|
import { AuthModule } from "src/auth/auth.module";
|
||||||
@@ -21,7 +23,11 @@ import { ExpertClaimService } from "./expert-claim.service";
|
|||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
HttpModule,
|
HttpModule.registerAsync({
|
||||||
|
imports: [ConfigModule],
|
||||||
|
inject: [ConfigService],
|
||||||
|
useFactory: createHttpModuleOptions,
|
||||||
|
}),
|
||||||
SandHubModule,
|
SandHubModule,
|
||||||
MongooseModule.forFeature([
|
MongooseModule.forFeature([
|
||||||
{ name: ClaimFactorsImage.name, schema: ClaimFactorsImageSchema },
|
{ name: ClaimFactorsImage.name, schema: ClaimFactorsImageSchema },
|
||||||
|
|||||||
@@ -3466,17 +3466,7 @@ export class ExpertClaimService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const fanavaranExpertise = !needsFactorUpload
|
// Fanavaran expertise is now triggered on owner final sign, not here.
|
||||||
? await this.claimRequestManagementService.autoSubmitFanavaranExpertiseOnExpertReply(
|
|
||||||
claimRequestId,
|
|
||||||
)
|
|
||||||
: {
|
|
||||||
attempted: false,
|
|
||||||
submitted: false,
|
|
||||||
skipped: true,
|
|
||||||
skipReason:
|
|
||||||
"Expertise submit waits until factor-needed repair lines are validated.",
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
claimRequestId,
|
claimRequestId,
|
||||||
@@ -3490,7 +3480,6 @@ export class ExpertClaimService {
|
|||||||
mixedPricingAndFactors: mixedFactorAndPrice,
|
mixedPricingAndFactors: mixedFactorAndPrice,
|
||||||
allPartsFactorNeeded: !!needsFactorUpload && !!allFactorLines,
|
allPartsFactorNeeded: !!needsFactorUpload && !!allFactorLines,
|
||||||
isFinalReplyAfterObjection,
|
isFinalReplyAfterObjection,
|
||||||
fanavaranExpertise,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -76,6 +76,16 @@ export const FANAVARAN_REMOTE_LOOKUPS: FanavaranRemoteLookupDefinition[] = [
|
|||||||
url: `${FANAVARAN_LOOKUP_BASE_URL}/common/code-list/person-role`,
|
url: `${FANAVARAN_LOOKUP_BASE_URL}/common/code-list/person-role`,
|
||||||
cacheFile: "person-role.json",
|
cacheFile: "person-role.json",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "insurance-corp",
|
||||||
|
url: `${FANAVARAN_LOOKUP_BASE_URL}/common/code-list/insurance-corp`,
|
||||||
|
cacheFile: "insurance-corp.json",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "file-types",
|
||||||
|
url: `${FANAVARAN_LOOKUP_BASE_URL}/common/base-info/file-types`,
|
||||||
|
cacheFile: "file-types.json",
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const TEJARAT_STATIC_ACCIDENT_FILES = {
|
export const TEJARAT_STATIC_ACCIDENT_FILES = {
|
||||||
|
|||||||
@@ -1,9 +1,17 @@
|
|||||||
import { Module } from "@nestjs/common";
|
import { Module } from "@nestjs/common";
|
||||||
import { HttpModule } from "@nestjs/axios";
|
import { HttpModule } from "@nestjs/axios";
|
||||||
|
import { ConfigModule, ConfigService } from "@nestjs/config";
|
||||||
|
import { createHttpModuleOptions } from "src/core/config/http-proxy.factory";
|
||||||
import { FanavaranLookupService } from "./fanavaran-lookup.service";
|
import { FanavaranLookupService } from "./fanavaran-lookup.service";
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [HttpModule],
|
imports: [
|
||||||
|
HttpModule.registerAsync({
|
||||||
|
imports: [ConfigModule],
|
||||||
|
inject: [ConfigService],
|
||||||
|
useFactory: createHttpModuleOptions,
|
||||||
|
}),
|
||||||
|
],
|
||||||
providers: [FanavaranLookupService],
|
providers: [FanavaranLookupService],
|
||||||
exports: [FanavaranLookupService],
|
exports: [FanavaranLookupService],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -183,24 +183,6 @@ export class FanavaranLookupService {
|
|||||||
this.logger.log(
|
this.logger.log(
|
||||||
`[${clientKey}] Fanavaran lookup response status=${response.status} dataCount=${dataCount}`,
|
`[${clientKey}] Fanavaran lookup response status=${response.status} dataCount=${dataCount}`,
|
||||||
);
|
);
|
||||||
this.logger.log(
|
|
||||||
`[${clientKey}] Fanavaran lookup raw response: ${JSON.stringify(
|
|
||||||
response.data,
|
|
||||||
null,
|
|
||||||
2,
|
|
||||||
)}`,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (Array.isArray(response.data) && response.data.length > 0) {
|
|
||||||
const firstItem = response.data[0];
|
|
||||||
if (firstItem && typeof firstItem === "object") {
|
|
||||||
this.logger.log(
|
|
||||||
`[${clientKey}] Fanavaran lookup first item keys: ${Object.keys(
|
|
||||||
firstItem,
|
|
||||||
).join(", ")}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.data;
|
return response.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -255,6 +237,34 @@ export class FanavaranLookupService {
|
|||||||
return this.fetchFromFanavaran(clientKey, url);
|
return this.fetchFromFanavaran(clientKey, url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async myPolicies(
|
||||||
|
clientKey: FanavaranClientKey,
|
||||||
|
nationalCode: string,
|
||||||
|
insuranceLineId: number = 5,
|
||||||
|
): Promise<unknown> {
|
||||||
|
const url =
|
||||||
|
`${FANAVARAN_LOOKUP_BASE_URL}/common/Policies/inquiry-my-policies` +
|
||||||
|
`?InsuranceLineId=${insuranceLineId}` +
|
||||||
|
`&NationalCode=${encodeURIComponent(nationalCode)}`;
|
||||||
|
return this.fetchFromFanavaran(clientKey, url);
|
||||||
|
}
|
||||||
|
|
||||||
|
async thirdPartyPolicyById(
|
||||||
|
clientKey: FanavaranClientKey,
|
||||||
|
policyId: number,
|
||||||
|
): Promise<unknown> {
|
||||||
|
const url = `${FANAVARAN_LOOKUP_BASE_URL}/car/third-party-car-policies/${policyId}`;
|
||||||
|
return this.fetchFromFanavaran(clientKey, url);
|
||||||
|
}
|
||||||
|
|
||||||
|
async bodyPolicyById(
|
||||||
|
clientKey: FanavaranClientKey,
|
||||||
|
policyId: number,
|
||||||
|
): Promise<unknown> {
|
||||||
|
const url = `${FANAVARAN_LOOKUP_BASE_URL}/car/vehicle-hull-policies/${policyId}`;
|
||||||
|
return this.fetchFromFanavaran(clientKey, url);
|
||||||
|
}
|
||||||
|
|
||||||
async inquiryByUniqueIdentifier(
|
async inquiryByUniqueIdentifier(
|
||||||
clientKey: FanavaranClientKey,
|
clientKey: FanavaranClientKey,
|
||||||
params: {
|
params: {
|
||||||
@@ -273,6 +283,91 @@ export class FanavaranLookupService {
|
|||||||
return this.fetchFromFanavaran(clientKey, url);
|
return this.fetchFromFanavaran(clientKey, url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async resolveInsuranceCorpId(
|
||||||
|
clientKey: FanavaranClientKey,
|
||||||
|
): Promise<number | null> {
|
||||||
|
const caption = process.env.INSURANCE_CORP_ID?.trim();
|
||||||
|
if (!caption) {
|
||||||
|
this.logger.warn("resolveInsuranceCorpId: INSURANCE_CORP_ID env not set");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check resolved cache first
|
||||||
|
const cachedId = await this.readCacheFile<number>(clientKey, "insurance-corp-id-resolved.json");
|
||||||
|
if (cachedId !== null) {
|
||||||
|
this.logger.log(`resolveInsuranceCorpId: using cached id=${cachedId} for "${caption}"`);
|
||||||
|
return cachedId;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try fetching the full list directly from Fanavaran
|
||||||
|
let companies: unknown;
|
||||||
|
const url = `${FANAVARAN_LOOKUP_BASE_URL}/common/code-list/insurance-corp`;
|
||||||
|
try {
|
||||||
|
companies = await this.fetchFromFanavaran(clientKey, url);
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.warn(
|
||||||
|
`resolveInsuranceCorpId: Fanavaran fetch failed, trying local lookup endpoint`,
|
||||||
|
);
|
||||||
|
// Fallback: call our own local lookup endpoint
|
||||||
|
try {
|
||||||
|
const localPort = process.env.PORT || 3000;
|
||||||
|
const response = await firstValueFrom(
|
||||||
|
this.httpService.get(`http://localhost:${localPort}/lookups/fanavaran/insurance-corp`, {
|
||||||
|
timeout: 10000,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
companies = response.data;
|
||||||
|
} catch (localError) {
|
||||||
|
this.logger.error(
|
||||||
|
`resolveInsuranceCorpId: both Fanavaran and local lookup failed`,
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Array.isArray(companies)) {
|
||||||
|
this.logger.warn(
|
||||||
|
`resolveInsuranceCorpId: insurance-corp response is not an array, type=${typeof companies}`,
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logger.log(
|
||||||
|
`resolveInsuranceCorpId: got ${companies.length} companies, searching for "${caption}"`,
|
||||||
|
);
|
||||||
|
|
||||||
|
const normalizedCaption = caption
|
||||||
|
.replace(/\s+/g, " ")
|
||||||
|
.toLowerCase()
|
||||||
|
.trim();
|
||||||
|
|
||||||
|
const match = companies.find((c: any) => {
|
||||||
|
if (c?.IsActive !== 1) return false;
|
||||||
|
if (typeof c?.Id !== "number") return false;
|
||||||
|
const cCaption = typeof c?.Caption === "string" ? c.Caption : "";
|
||||||
|
const normalized = cCaption.replace(/\s+/g, " ").toLowerCase().trim();
|
||||||
|
return normalized.includes(normalizedCaption) || normalizedCaption.includes(normalized);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!match) {
|
||||||
|
this.logger.warn(
|
||||||
|
`resolveInsuranceCorpId: no active company matching "${caption}". Available: ` +
|
||||||
|
companies
|
||||||
|
.filter((c: any) => c?.IsActive === 1)
|
||||||
|
.map((c: any) => `${c.Caption} (${c.Id})`)
|
||||||
|
.join(", "),
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const corpId = match.Id as number;
|
||||||
|
// Cache both the resolved id and the full list for future use
|
||||||
|
await this.writeCacheFile(clientKey, "insurance-corp-id-resolved.json", corpId);
|
||||||
|
await this.writeCacheFile(clientKey, "insurance-corp.json", companies);
|
||||||
|
this.logger.log(`resolveInsuranceCorpId: resolved id=${corpId} for "${caption}"`);
|
||||||
|
return corpId;
|
||||||
|
}
|
||||||
|
|
||||||
mapFanavaranAccidentCausesToReasonOptions(
|
mapFanavaranAccidentCausesToReasonOptions(
|
||||||
causes: unknown,
|
causes: unknown,
|
||||||
): { id: number; label: string; fanavaran: number }[] {
|
): { id: number; label: string; fanavaran: number }[] {
|
||||||
@@ -297,4 +392,5 @@ export class FanavaranLookupService {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,18 @@
|
|||||||
import { Controller, Get, Param, Query } from "@nestjs/common";
|
import { Controller, Get, Param, ParseIntPipe, Query, UseGuards } from "@nestjs/common";
|
||||||
import {
|
import {
|
||||||
|
ApiBearerAuth,
|
||||||
ApiOkResponse,
|
ApiOkResponse,
|
||||||
ApiOperation,
|
ApiOperation,
|
||||||
ApiParam,
|
ApiParam,
|
||||||
ApiQuery,
|
ApiQuery,
|
||||||
ApiTags,
|
ApiTags,
|
||||||
} from "@nestjs/swagger";
|
} from "@nestjs/swagger";
|
||||||
|
import { AuthGuard } from "src/common/auth/guards";
|
||||||
import { LookupsService } from "./lookups.service";
|
import { LookupsService } from "./lookups.service";
|
||||||
|
|
||||||
@ApiTags("lookups")
|
@ApiTags("lookups")
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
@Controller("lookups")
|
@Controller("lookups")
|
||||||
export class LookupsController {
|
export class LookupsController {
|
||||||
constructor(private readonly lookupsService: LookupsService) {}
|
constructor(private readonly lookupsService: LookupsService) {}
|
||||||
@@ -210,6 +214,30 @@ export class LookupsController {
|
|||||||
return await this.lookupsService.getPersonRole();
|
return await this.lookupsService.getPersonRole();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Get("file-types")
|
||||||
|
@ApiOperation({
|
||||||
|
summary: "Fanavaran file types lookup",
|
||||||
|
description:
|
||||||
|
"Returns file type codes from common/base-info/file-types. Use FileTypeId in attachment uploads.",
|
||||||
|
})
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: "Returns Fanavaran file types lookup data",
|
||||||
|
schema: {
|
||||||
|
type: "array",
|
||||||
|
items: {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
Caption: { type: "string" },
|
||||||
|
Id: { type: "number" },
|
||||||
|
IsActive: { type: "number" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
async getFileTypes() {
|
||||||
|
return await this.lookupsService.getFileTypes();
|
||||||
|
}
|
||||||
|
|
||||||
@Get("inquiry-by-vin")
|
@Get("inquiry-by-vin")
|
||||||
@ApiOperation({
|
@ApiOperation({
|
||||||
summary: "Fanavaran vehicle inquiry by VIN",
|
summary: "Fanavaran vehicle inquiry by VIN",
|
||||||
@@ -371,4 +399,75 @@ export class LookupsController {
|
|||||||
async getAccidentFields() {
|
async getAccidentFields() {
|
||||||
return await this.lookupsService.getAccidentFields();
|
return await this.lookupsService.getAccidentFields();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Get("my-policies")
|
||||||
|
@ApiOperation({
|
||||||
|
summary: "My policies inquiry",
|
||||||
|
description:
|
||||||
|
"Returns the list of policies for the given national code via common/Policies/inquiry-my-policies.",
|
||||||
|
})
|
||||||
|
@ApiQuery({
|
||||||
|
name: "nationalCode",
|
||||||
|
description: "National code of the insured person",
|
||||||
|
example: "0012345678",
|
||||||
|
})
|
||||||
|
@ApiQuery({
|
||||||
|
name: "insuranceLineId",
|
||||||
|
description: "Insurance line ID (default: 5 for car)",
|
||||||
|
required: false,
|
||||||
|
example: 5,
|
||||||
|
})
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: "Returns list of policies",
|
||||||
|
schema: { type: "array", items: { type: "object" } },
|
||||||
|
})
|
||||||
|
async myPolicies(
|
||||||
|
@Query("nationalCode") nationalCode: string,
|
||||||
|
@Query("insuranceLineId") insuranceLineId?: number,
|
||||||
|
) {
|
||||||
|
return await this.lookupsService.myPolicies(
|
||||||
|
nationalCode,
|
||||||
|
insuranceLineId ?? 5,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get("third-party-policy/:policyId")
|
||||||
|
@ApiOperation({
|
||||||
|
summary: "Third-party car policy inquiry by policy ID",
|
||||||
|
description:
|
||||||
|
"Returns third-party car insurance policy details for the given policy ID via car/third-party-car-policies/{policyId}.",
|
||||||
|
})
|
||||||
|
@ApiParam({
|
||||||
|
name: "policyId",
|
||||||
|
description: "Fanavaran policy ID",
|
||||||
|
example: 12345,
|
||||||
|
})
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: "Returns third-party policy details",
|
||||||
|
schema: { type: "object" },
|
||||||
|
})
|
||||||
|
async thirdPartyPolicyById(
|
||||||
|
@Param("policyId", ParseIntPipe) policyId: number,
|
||||||
|
) {
|
||||||
|
return await this.lookupsService.thirdPartyPolicyById(policyId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get("body-policy/:policyId")
|
||||||
|
@ApiOperation({
|
||||||
|
summary: "Vehicle hull (body) policy inquiry by policy ID",
|
||||||
|
description:
|
||||||
|
"Returns vehicle hull (body) insurance policy details for the given policy ID via car/vehicle-hull-policies/{policyId}.",
|
||||||
|
})
|
||||||
|
@ApiParam({
|
||||||
|
name: "policyId",
|
||||||
|
description: "Fanavaran policy ID",
|
||||||
|
example: 12345,
|
||||||
|
})
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: "Returns body policy details",
|
||||||
|
schema: { type: "object" },
|
||||||
|
})
|
||||||
|
async bodyPolicyById(@Param("policyId", ParseIntPipe) policyId: number) {
|
||||||
|
return await this.lookupsService.bodyPolicyById(policyId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -123,11 +123,37 @@ export class LookupsService {
|
|||||||
return await this.getClientRemoteLookup("person-role");
|
return await this.getClientRemoteLookup("person-role");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getFileTypes(): Promise<any> {
|
||||||
|
return await this.getClientRemoteLookup("file-types");
|
||||||
|
}
|
||||||
|
|
||||||
async inquiryByVin(vin: string): Promise<unknown> {
|
async inquiryByVin(vin: string): Promise<unknown> {
|
||||||
const clientKey = this.activeClientKey();
|
const clientKey = this.activeClientKey();
|
||||||
return this.fanavaranLookupService.inquiryByVin(clientKey, vin);
|
return this.fanavaranLookupService.inquiryByVin(clientKey, vin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async myPolicies(
|
||||||
|
nationalCode: string,
|
||||||
|
insuranceLineId: number = 5,
|
||||||
|
): Promise<unknown> {
|
||||||
|
const clientKey = this.activeClientKey();
|
||||||
|
return this.fanavaranLookupService.myPolicies(
|
||||||
|
clientKey,
|
||||||
|
nationalCode,
|
||||||
|
insuranceLineId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async thirdPartyPolicyById(policyId: number): Promise<unknown> {
|
||||||
|
const clientKey = this.activeClientKey();
|
||||||
|
return this.fanavaranLookupService.thirdPartyPolicyById(clientKey, policyId);
|
||||||
|
}
|
||||||
|
|
||||||
|
async bodyPolicyById(policyId: number): Promise<unknown> {
|
||||||
|
const clientKey = this.activeClientKey();
|
||||||
|
return this.fanavaranLookupService.bodyPolicyById(clientKey, policyId);
|
||||||
|
}
|
||||||
|
|
||||||
async getAccidentWay(): Promise<{ id: number; label: string }[]> {
|
async getAccidentWay(): Promise<{ id: number; label: string }[]> {
|
||||||
const clientKey = this.activeClientKey();
|
const clientKey = this.activeClientKey();
|
||||||
const fileName = TEJARAT_STATIC_ACCIDENT_FILES.accidentWay;
|
const fileName = TEJARAT_STATIC_ACCIDENT_FILES.accidentWay;
|
||||||
|
|||||||
@@ -41,6 +41,10 @@ export class Person {
|
|||||||
|
|
||||||
@Prop({ type: String })
|
@Prop({ type: String })
|
||||||
driverBirthday?: string | null;
|
driverBirthday?: string | null;
|
||||||
|
|
||||||
|
/** Cached Fanavaran party ID resolved from nationalCodeOfDriver + driverBirthday + driverIsInsurer */
|
||||||
|
@Prop({ type: Number })
|
||||||
|
fanavaranDriverId?: number;
|
||||||
}
|
}
|
||||||
export const PersonSchema = SchemaFactory.createForClass(Person);
|
export const PersonSchema = SchemaFactory.createForClass(Person);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { Module } from "@nestjs/common";
|
import { Module } from "@nestjs/common";
|
||||||
import { HttpModule } from "@nestjs/axios";
|
import { HttpModule } from "@nestjs/axios";
|
||||||
|
import { ConfigModule, ConfigService } from "@nestjs/config";
|
||||||
|
import { createHttpModuleOptions } from "src/core/config/http-proxy.factory";
|
||||||
import { MongooseModule } from "@nestjs/mongoose";
|
import { MongooseModule } from "@nestjs/mongoose";
|
||||||
import { ClientModule } from "src/client/client.module";
|
import { ClientModule } from "src/client/client.module";
|
||||||
import { SystemSettingsModule } from "src/system-settings/system-settings.module";
|
import { SystemSettingsModule } from "src/system-settings/system-settings.module";
|
||||||
@@ -9,7 +11,11 @@ import { SandHubService } from "./sand-hub.service";
|
|||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
HttpModule,
|
HttpModule.registerAsync({
|
||||||
|
imports: [ConfigModule],
|
||||||
|
inject: [ConfigService],
|
||||||
|
useFactory: createHttpModuleOptions,
|
||||||
|
}),
|
||||||
SystemSettingsModule,
|
SystemSettingsModule,
|
||||||
ClientModule,
|
ClientModule,
|
||||||
MongooseModule.forFeature([
|
MongooseModule.forFeature([
|
||||||
|
|||||||
Reference in New Issue
Block a user