forked from Yara724/api
Merge pull request 'Fix addClient bug' (#109) from s.yahyaee/yara724-api:main into main
Reviewed-on: Yara724/api#109
This commit is contained in:
@@ -99,19 +99,34 @@ export class ClientService {
|
|||||||
try {
|
try {
|
||||||
const newClient = await this.clientDbService.create({
|
const newClient = await this.clientDbService.create({
|
||||||
clientCode: client.clientCode,
|
clientCode: client.clientCode,
|
||||||
|
|
||||||
clientName: {
|
clientName: {
|
||||||
persian: client.clientName.persian,
|
persian:
|
||||||
english: client.clientName.english || null,
|
typeof client.clientName === "string"
|
||||||
|
? client.clientName
|
||||||
|
: client.clientName?.persian,
|
||||||
|
|
||||||
|
english:
|
||||||
|
typeof client.clientName === "string"
|
||||||
|
? null
|
||||||
|
: (client.clientName?.english ?? null),
|
||||||
},
|
},
|
||||||
|
|
||||||
property: {
|
property: {
|
||||||
smsApiKey: client.property.smsApiKey || null,
|
smsApiKey: client.property?.smsApiKey ?? null,
|
||||||
},
|
},
|
||||||
useExpertMode: client.useExpertMode || null,
|
|
||||||
|
useExpertMode: client.useExpertMode ?? null,
|
||||||
});
|
});
|
||||||
if (newClient) return new ClientDtoRs(newClient);
|
|
||||||
else throw new GoneException("database not connected");
|
if (!newClient) {
|
||||||
|
throw new GoneException("database not connected");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ClientDtoRs(newClient);
|
||||||
} catch (er) {
|
} catch (er) {
|
||||||
throw new BadGatewayException(er.errors);
|
console.error("ADD CLIENT ERROR:", er);
|
||||||
|
throw er;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
import { ApiProperty } from "@nestjs/swagger";
|
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
|
||||||
import { IsIn, IsNotEmpty, IsNumber, IsOptional, IsString, ValidateNested } from "class-validator";
|
import {
|
||||||
|
IsIn,
|
||||||
|
IsNotEmpty,
|
||||||
|
IsNumber,
|
||||||
|
IsOptional,
|
||||||
|
IsString,
|
||||||
|
ValidateNested,
|
||||||
|
} from "class-validator";
|
||||||
import { Type } from "class-transformer";
|
import { Type } from "class-transformer";
|
||||||
import { Types } from "mongoose";
|
import { Types } from "mongoose";
|
||||||
|
|
||||||
@@ -15,9 +22,8 @@ class ClientName {
|
|||||||
english: string;
|
english: string;
|
||||||
}
|
}
|
||||||
class Property {
|
class Property {
|
||||||
@ApiProperty({})
|
@ApiPropertyOptional({})
|
||||||
@IsString()
|
@IsString()
|
||||||
@IsNotEmpty()
|
|
||||||
smsApiKey: string;
|
smsApiKey: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -945,354 +945,375 @@ export class RequestManagementService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async initialFormV2(requestId: string, body: AddPlateDto, user: any) {
|
async initialFormV2(requestId: string, body: AddPlateDto, user: any) {
|
||||||
const req = await this.blameRequestDbService.findById(requestId);
|
try {
|
||||||
if (!req) {
|
const req = await this.blameRequestDbService.findById(requestId);
|
||||||
throw new NotFoundException("Request not found");
|
if (!req) {
|
||||||
}
|
throw new NotFoundException("Request not found");
|
||||||
|
}
|
||||||
|
|
||||||
if (!req.workflow?.currentStep) {
|
if (!req.workflow?.currentStep) {
|
||||||
throw new BadRequestException("Request workflow is not initialized");
|
throw new BadRequestException("Request workflow is not initialized");
|
||||||
}
|
}
|
||||||
|
|
||||||
const stepKey = req.workflow.currentStep as WorkflowStep;
|
const stepKey = req.workflow.currentStep as WorkflowStep;
|
||||||
if (
|
|
||||||
stepKey !== WorkflowStep.FIRST_INITIAL_FORM &&
|
|
||||||
stepKey !== WorkflowStep.SECOND_INITIAL_FORM
|
|
||||||
) {
|
|
||||||
throw new BadRequestException(
|
|
||||||
`Invalid step. Expected FIRST_INITIAL_FORM or SECOND_INITIAL_FORM but got ${stepKey}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Detect which party from step
|
|
||||||
const role = this.stepKeyToPartyRole(stepKey);
|
|
||||||
const idx = this.getPartyIndex(req, role);
|
|
||||||
if (idx === -1) {
|
|
||||||
throw new BadRequestException(`${role} party not found on request`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const party = req.parties[idx];
|
|
||||||
this.assertPartyOwner(
|
|
||||||
party,
|
|
||||||
user,
|
|
||||||
`Only ${role} party can submit this step`,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Set userId for party if not already set (important for second party)
|
|
||||||
if (!party.person) party.person = {} as any;
|
|
||||||
if (!party.person.userId && user?.sub) {
|
|
||||||
party.person.userId = Types.ObjectId.isValid(user.sub)
|
|
||||||
? new Types.ObjectId(user.sub)
|
|
||||||
: undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validation: driver/insurer sameness rules
|
|
||||||
if (body.driverIsInsurer === false) {
|
|
||||||
if (
|
if (
|
||||||
body.nationalCodeOfInsurer === body.nationalCodeOfDriver ||
|
stepKey !== WorkflowStep.FIRST_INITIAL_FORM &&
|
||||||
body.insurerLicense === body.driverLicense
|
stepKey !== WorkflowStep.SECOND_INITIAL_FORM
|
||||||
) {
|
) {
|
||||||
throw new BadRequestException(
|
throw new BadRequestException(
|
||||||
"Insurer and Driver should be two different persons in this mode.",
|
`Invalid step. Expected FIRST_INITIAL_FORM or SECOND_INITIAL_FORM but got ${stepKey}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if (body.driverIsInsurer === true) {
|
|
||||||
const sameNat = body.nationalCodeOfInsurer === body.nationalCodeOfDriver;
|
// Detect which party from step
|
||||||
const sameLic = body.insurerLicense === body.driverLicense;
|
const role = this.stepKeyToPartyRole(stepKey);
|
||||||
const sameBirthday =
|
const idx = this.getPartyIndex(req, role);
|
||||||
body.driverBirthday == null ||
|
if (idx === -1) {
|
||||||
String(body.driverBirthday) === String(body.insurerBirthday);
|
throw new BadRequestException(`${role} party not found on request`);
|
||||||
if (!sameNat || !sameLic || !sameBirthday) {
|
|
||||||
throw new BadRequestException(
|
|
||||||
"When driverIsInsurer is true, insurer and driver data must be the same.",
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// ---- External inquiry 1: Tejarat block inquiry ----
|
const party = req.parties[idx];
|
||||||
let inquiryRaw: any;
|
this.assertPartyOwner(
|
||||||
let inquiryMapped: any;
|
party,
|
||||||
try {
|
user,
|
||||||
const inquiry = await this.sandHubService.getTejaratBlockInquiry({
|
`Only ${role} party can submit this step`,
|
||||||
plate: body.plate,
|
|
||||||
nationalCodeOfInsurer: body.nationalCodeOfInsurer,
|
|
||||||
});
|
|
||||||
inquiryRaw = inquiry.raw;
|
|
||||||
inquiryMapped = inquiry.mapped;
|
|
||||||
this.logger.log(
|
|
||||||
`[TEJARAT] block inquiry raw for request=${req._id}: ${JSON.stringify(inquiryRaw)}`,
|
|
||||||
);
|
);
|
||||||
this.logger.log(
|
|
||||||
`[TEJARAT] block inquiry mapped for request=${req._id}: ${JSON.stringify(inquiryMapped)}`,
|
// Set userId for party if not already set (important for second party)
|
||||||
);
|
if (!party.person) party.person = {} as any;
|
||||||
this.recordPartyCaseInquiryStatus(req, "thirdParty", role, true, {
|
if (!party.person.userId && user?.sub) {
|
||||||
source: "TEJARAT_BLOCK_INQUIRY",
|
party.person.userId = Types.ObjectId.isValid(user.sub)
|
||||||
raw: inquiryRaw,
|
? new Types.ObjectId(user.sub)
|
||||||
mapped: inquiryMapped,
|
: undefined;
|
||||||
});
|
|
||||||
} catch (err: any) {
|
|
||||||
this.logger.error(
|
|
||||||
`[TEJARAT] block inquiry failed for request=${req._id}: ${err?.message || err}`,
|
|
||||||
);
|
|
||||||
if (err?.response) {
|
|
||||||
this.logger.error(
|
|
||||||
`[TEJARAT] block inquiry response for request=${req._id}: status=${
|
|
||||||
err.response.status
|
|
||||||
}, data=${JSON.stringify(err.response.data)}`,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
this.recordPartyCaseInquiryStatus(
|
|
||||||
req,
|
|
||||||
"thirdParty",
|
|
||||||
role,
|
|
||||||
false,
|
|
||||||
{},
|
|
||||||
err,
|
|
||||||
);
|
|
||||||
await (req as any).save();
|
|
||||||
throw new HttpException("Inquiry failed", HttpStatus.BAD_REQUEST);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inquiryMapped?.Error) {
|
// Validation: driver/insurer sameness rules
|
||||||
this.logger.warn(
|
if (body.driverIsInsurer === false) {
|
||||||
`[TEJARAT] block inquiry error for request=${req._id}: ${JSON.stringify(
|
if (
|
||||||
inquiryMapped.Error,
|
body.nationalCodeOfInsurer === body.nationalCodeOfDriver ||
|
||||||
)}`,
|
body.insurerLicense === body.driverLicense
|
||||||
);
|
) {
|
||||||
this.recordPartyCaseInquiryStatus(req, "thirdParty", role, false, {
|
throw new BadRequestException(
|
||||||
source: "TEJARAT_BLOCK_INQUIRY",
|
"Insurer and Driver should be two different persons in this mode.",
|
||||||
raw: inquiryRaw,
|
);
|
||||||
mapped: inquiryMapped,
|
}
|
||||||
});
|
} else if (body.driverIsInsurer === true) {
|
||||||
await (req as any).save();
|
const sameNat =
|
||||||
throw new HttpException(
|
body.nationalCodeOfInsurer === body.nationalCodeOfDriver;
|
||||||
inquiryMapped.Error.Message || "Inquiry returned error",
|
const sameLic = body.insurerLicense === body.driverLicense;
|
||||||
HttpStatus.BAD_REQUEST,
|
const sameBirthday =
|
||||||
);
|
body.driverBirthday == null ||
|
||||||
}
|
String(body.driverBirthday) === String(body.insurerBirthday);
|
||||||
|
if (!sameNat || !sameLic || !sameBirthday) {
|
||||||
|
throw new BadRequestException(
|
||||||
|
"When driverIsInsurer is true, insurer and driver data must be the same.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ---- External inquiry 2: personal identity check (insurer/driver nationalCode + birthDate) ----
|
// ---- External inquiry 1: Tejarat block inquiry ----
|
||||||
// The form sends the birthday as a Jalali date in any of these shapes:
|
let inquiryRaw: any;
|
||||||
// - number 13770624 (packed YYYYMMDD)
|
let inquiryMapped: any;
|
||||||
// - string "1377-06-24" / "1377/06/24" / "13770624"
|
|
||||||
// We forward it as-is; sandHubService.getPersonalInquiry() handles the
|
|
||||||
// Jalali → Gregorian conversion required by the external API.
|
|
||||||
const personalNationalCode =
|
|
||||||
body.nationalCodeOfInsurer || body.nationalCodeOfDriver;
|
|
||||||
const personalBirthDate: number | string | null = (body.insurerBirthday ??
|
|
||||||
body.driverBirthday ??
|
|
||||||
null) as number | string | null;
|
|
||||||
if (
|
|
||||||
!personalNationalCode ||
|
|
||||||
personalBirthDate === null ||
|
|
||||||
personalBirthDate === undefined ||
|
|
||||||
String(personalBirthDate).trim() === ""
|
|
||||||
) {
|
|
||||||
throw new BadRequestException(
|
|
||||||
"Valid nationalCode and birthDate are required for personal inquiry.",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const personalInquiry = await this.sandHubService.getPersonalInquiry(
|
|
||||||
personalNationalCode,
|
|
||||||
personalBirthDate,
|
|
||||||
);
|
|
||||||
this.recordPartyCaseInquiryStatus(
|
|
||||||
req,
|
|
||||||
"person",
|
|
||||||
role,
|
|
||||||
true,
|
|
||||||
personalInquiry,
|
|
||||||
);
|
|
||||||
this.logger.log(
|
|
||||||
`[SANDHUB] personal inquiry success request=${req._id} nationalCode=${personalNationalCode}: ${JSON.stringify(
|
|
||||||
personalInquiry,
|
|
||||||
)}`,
|
|
||||||
);
|
|
||||||
} catch (err: any) {
|
|
||||||
this.logger.error(
|
|
||||||
`[SANDHUB] personal inquiry failed request=${req._id} nationalCode=${personalNationalCode}: ${err?.message || err}`,
|
|
||||||
);
|
|
||||||
this.recordPartyCaseInquiryStatus(req, "person", role, false, {}, err);
|
|
||||||
await (req as any).save();
|
|
||||||
throw new HttpException(
|
|
||||||
"Personal identity inquiry failed",
|
|
||||||
HttpStatus.BAD_REQUEST,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- External inquiry 3: driving license check (insurerLicense + nationalCode) ----
|
|
||||||
// const licenseNationalCode = body.nationalCodeOfInsurer || body.nationalCodeOfDriver;
|
|
||||||
// const licenseNumber = body.insurerLicense;
|
|
||||||
// if (!licenseNationalCode || !licenseNumber) {
|
|
||||||
// throw new BadRequestException(
|
|
||||||
// "nationalCode and insurerLicense are required for driving license inquiry.",
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// try {
|
|
||||||
// const licenseInquiry = await this.sandHubService.getDrivingLicenseInfo(
|
|
||||||
// licenseNationalCode,
|
|
||||||
// licenseNumber,
|
|
||||||
// );
|
|
||||||
// this.logger.log(
|
|
||||||
// `[SANDHUB] license inquiry success request=${req._id} nationalCode=${licenseNationalCode}: ${JSON.stringify(
|
|
||||||
// licenseInquiry,
|
|
||||||
// )}`,
|
|
||||||
// );
|
|
||||||
// } catch (err: any) {
|
|
||||||
// this.logger.error(
|
|
||||||
// `[SANDHUB] license inquiry failed request=${req._id} nationalCode=${licenseNationalCode}: ${err?.message || err}`,
|
|
||||||
// );
|
|
||||||
// throw new HttpException(
|
|
||||||
// "Driving license inquiry failed",
|
|
||||||
// HttpStatus.BAD_REQUEST,
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Find client by company code
|
|
||||||
const clientName = inquiryMapped?.CompanyName;
|
|
||||||
const companyCode = inquiryMapped?.CompanyCode;
|
|
||||||
const client =
|
|
||||||
await this.clientService.findClientWithCompanyCode(+companyCode);
|
|
||||||
|
|
||||||
if (!client) {
|
|
||||||
await this.clientService.addClient({
|
|
||||||
clientName,
|
|
||||||
clientCode: companyCode,
|
|
||||||
useExpertMode: "legal",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// if (!client) {
|
|
||||||
// throw new HttpException("Client not found", HttpStatus.CONFLICT);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Persist inquiry/body data into new model
|
|
||||||
if (!party.person) party.person = {} as any;
|
|
||||||
const resolvedClientId = (client as any)?._id ?? (client as any)?._doc?._id;
|
|
||||||
party.person.clientId = resolvedClientId;
|
|
||||||
party.person.nationalCodeOfInsurer = body.nationalCodeOfInsurer;
|
|
||||||
party.person.nationalCodeOfDriver = body.nationalCodeOfDriver;
|
|
||||||
party.person.insurerLicense = body.insurerLicense;
|
|
||||||
party.person.driverLicense = body.driverLicense;
|
|
||||||
party.person.driverIsInsurer = body.driverIsInsurer;
|
|
||||||
party.person.isNewCar = body.isNewCar;
|
|
||||||
party.person.userNoCertificate = body.userNoCertificate;
|
|
||||||
party.person.insurerBirthday = body.insurerBirthday;
|
|
||||||
party.person.driverBirthday =
|
|
||||||
body.driverBirthday ??
|
|
||||||
(body.driverIsInsurer ? String(body.insurerBirthday) : null);
|
|
||||||
|
|
||||||
if (!party.vehicle) party.vehicle = {} as any;
|
|
||||||
party.vehicle.isNew = body.isNewCar;
|
|
||||||
party.vehicle.plateId =
|
|
||||||
typeof body.plateId === "string"
|
|
||||||
? body.plateId
|
|
||||||
: this.plateToPlateIdString(body.plate);
|
|
||||||
party.vehicle.name = inquiryMapped?.MapTypNam;
|
|
||||||
party.vehicle.type = `${inquiryMapped?.UsageField} / ${inquiryMapped?.MapUsageName || "-"}`;
|
|
||||||
party.vehicle.inquiry = {
|
|
||||||
source: "TEJARAT_BLOCK_INQUIRY",
|
|
||||||
raw: inquiryRaw,
|
|
||||||
mapped: inquiryMapped,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!party.insurance) party.insurance = {} as any;
|
|
||||||
party.insurance.policyNumber = inquiryMapped?.LastCompanyDocumentNumber;
|
|
||||||
party.insurance.company = clientName;
|
|
||||||
party.insurance.financialCeiling = inquiryMapped?.FinancialCvrCptl;
|
|
||||||
party.insurance.startDate = inquiryMapped?.IssueDate;
|
|
||||||
party.insurance.endDate = inquiryMapped?.EndDate;
|
|
||||||
|
|
||||||
// CAR BODY EXTERNAL API INQUIRY
|
|
||||||
if (req.type === BlameRequestType.CAR_BODY && role === PartyRole.FIRST) {
|
|
||||||
let carBodyInfo: any;
|
|
||||||
try {
|
try {
|
||||||
carBodyInfo = await this.sandHubService.getTejaratCarBodyInquiry({
|
const inquiry = await this.sandHubService.getTejaratBlockInquiry({
|
||||||
nationalCodeOfInsurer: body.nationalCodeOfInsurer,
|
|
||||||
plate: body.plate,
|
plate: body.plate,
|
||||||
|
nationalCodeOfInsurer: body.nationalCodeOfInsurer,
|
||||||
});
|
});
|
||||||
this.recordPartyCaseInquiryStatus(req, "carBody", role, true, {
|
inquiryRaw = inquiry.raw;
|
||||||
source: "TEJARAT_CAR_BODY_INQUIRY",
|
inquiryMapped = inquiry.mapped;
|
||||||
raw: carBodyInfo.raw,
|
this.logger.log(
|
||||||
mapped: carBodyInfo.mapped,
|
`[TEJARAT] block inquiry raw for request=${req._id}: ${JSON.stringify(inquiryRaw)}`,
|
||||||
|
);
|
||||||
|
this.logger.log(
|
||||||
|
`[TEJARAT] block inquiry mapped for request=${req._id}: ${JSON.stringify(inquiryMapped)}`,
|
||||||
|
);
|
||||||
|
this.recordPartyCaseInquiryStatus(req, "thirdParty", role, true, {
|
||||||
|
source: "TEJARAT_BLOCK_INQUIRY",
|
||||||
|
raw: inquiryRaw,
|
||||||
|
mapped: inquiryMapped,
|
||||||
});
|
});
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
this.logger.error(
|
this.logger.error(
|
||||||
`[TEJARAT] car body inquiry failed for request=${req._id}: ${err?.message || err}`,
|
`[TEJARAT] block inquiry failed for request=${req._id}: ${err?.message || err}`,
|
||||||
);
|
);
|
||||||
this.recordPartyCaseInquiryStatus(req, "carBody", role, false, {}, err);
|
if (err?.response) {
|
||||||
|
this.logger.error(
|
||||||
|
`[TEJARAT] block inquiry response for request=${req._id}: status=${
|
||||||
|
err.response.status
|
||||||
|
}, data=${JSON.stringify(err.response.data)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
this.recordPartyCaseInquiryStatus(
|
||||||
|
req,
|
||||||
|
"thirdParty",
|
||||||
|
role,
|
||||||
|
false,
|
||||||
|
{},
|
||||||
|
err,
|
||||||
|
);
|
||||||
|
await (req as any).save();
|
||||||
|
throw new HttpException("Inquiry failed", HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inquiryMapped?.Error) {
|
||||||
|
this.logger.warn(
|
||||||
|
`[TEJARAT] block inquiry error for request=${req._id}: ${JSON.stringify(
|
||||||
|
inquiryMapped.Error,
|
||||||
|
)}`,
|
||||||
|
);
|
||||||
|
this.recordPartyCaseInquiryStatus(req, "thirdParty", role, false, {
|
||||||
|
source: "TEJARAT_BLOCK_INQUIRY",
|
||||||
|
raw: inquiryRaw,
|
||||||
|
mapped: inquiryMapped,
|
||||||
|
});
|
||||||
await (req as any).save();
|
await (req as any).save();
|
||||||
throw new HttpException(
|
throw new HttpException(
|
||||||
"Car body inquiry failed",
|
inquiryMapped.Error.Message || "Inquiry returned error",
|
||||||
HttpStatus.BAD_REQUEST,
|
HttpStatus.BAD_REQUEST,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Raw + mapped stored under vehicle (same pattern as third-party inquiry)
|
// ---- External inquiry 2: personal identity check (insurer/driver nationalCode + birthDate) ----
|
||||||
|
// The form sends the birthday as a Jalali date in any of these shapes:
|
||||||
|
// - number 13770624 (packed YYYYMMDD)
|
||||||
|
// - string "1377-06-24" / "1377/06/24" / "13770624"
|
||||||
|
// We forward it as-is; sandHubService.getPersonalInquiry() handles the
|
||||||
|
// Jalali → Gregorian conversion required by the external API.
|
||||||
|
const personalNationalCode =
|
||||||
|
body.nationalCodeOfInsurer || body.nationalCodeOfDriver;
|
||||||
|
const personalBirthDate: number | string | null = (body.insurerBirthday ??
|
||||||
|
body.driverBirthday ??
|
||||||
|
null) as number | string | null;
|
||||||
|
if (
|
||||||
|
!personalNationalCode ||
|
||||||
|
personalBirthDate === null ||
|
||||||
|
personalBirthDate === undefined ||
|
||||||
|
String(personalBirthDate).trim() === ""
|
||||||
|
) {
|
||||||
|
throw new BadRequestException(
|
||||||
|
"Valid nationalCode and birthDate are required for personal inquiry.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const personalInquiry = await this.sandHubService.getPersonalInquiry(
|
||||||
|
personalNationalCode,
|
||||||
|
personalBirthDate,
|
||||||
|
);
|
||||||
|
this.recordPartyCaseInquiryStatus(
|
||||||
|
req,
|
||||||
|
"person",
|
||||||
|
role,
|
||||||
|
true,
|
||||||
|
personalInquiry,
|
||||||
|
);
|
||||||
|
this.logger.log(
|
||||||
|
`[SANDHUB] personal inquiry success request=${req._id} nationalCode=${personalNationalCode}: ${JSON.stringify(
|
||||||
|
personalInquiry,
|
||||||
|
)}`,
|
||||||
|
);
|
||||||
|
} catch (err: any) {
|
||||||
|
this.logger.error(
|
||||||
|
`[SANDHUB] personal inquiry failed request=${req._id} nationalCode=${personalNationalCode}: ${err?.message || err}`,
|
||||||
|
);
|
||||||
|
this.recordPartyCaseInquiryStatus(req, "person", role, false, {}, err);
|
||||||
|
await (req as any).save();
|
||||||
|
throw new HttpException(
|
||||||
|
"Personal identity inquiry failed",
|
||||||
|
HttpStatus.BAD_REQUEST,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- External inquiry 3: driving license check (insurerLicense + nationalCode) ----
|
||||||
|
// const licenseNationalCode = body.nationalCodeOfInsurer || body.nationalCodeOfDriver;
|
||||||
|
// const licenseNumber = body.insurerLicense;
|
||||||
|
// if (!licenseNationalCode || !licenseNumber) {
|
||||||
|
// throw new BadRequestException(
|
||||||
|
// "nationalCode and insurerLicense are required for driving license inquiry.",
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// try {
|
||||||
|
// const licenseInquiry = await this.sandHubService.getDrivingLicenseInfo(
|
||||||
|
// licenseNationalCode,
|
||||||
|
// licenseNumber,
|
||||||
|
// );
|
||||||
|
// this.logger.log(
|
||||||
|
// `[SANDHUB] license inquiry success request=${req._id} nationalCode=${licenseNationalCode}: ${JSON.stringify(
|
||||||
|
// licenseInquiry,
|
||||||
|
// )}`,
|
||||||
|
// );
|
||||||
|
// } catch (err: any) {
|
||||||
|
// this.logger.error(
|
||||||
|
// `[SANDHUB] license inquiry failed request=${req._id} nationalCode=${licenseNationalCode}: ${err?.message || err}`,
|
||||||
|
// );
|
||||||
|
// throw new HttpException(
|
||||||
|
// "Driving license inquiry failed",
|
||||||
|
// HttpStatus.BAD_REQUEST,
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Find client by company code
|
||||||
|
const clientName = inquiryMapped?.CompanyName;
|
||||||
|
if (!clientName) {
|
||||||
|
throw new BadRequestException(
|
||||||
|
`CompanyName missing from inquiry response`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const companyCode = inquiryMapped?.CompanyCode;
|
||||||
|
let client =
|
||||||
|
await this.clientService.findClientWithCompanyCode(+companyCode);
|
||||||
|
|
||||||
|
if (!client) {
|
||||||
|
await this.clientService.addClient({
|
||||||
|
clientName: {
|
||||||
|
persian: clientName,
|
||||||
|
english: null,
|
||||||
|
},
|
||||||
|
clientCode: Number(companyCode),
|
||||||
|
useExpertMode: "legal",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (!client) {
|
||||||
|
// throw new HttpException("Client not found", HttpStatus.CONFLICT);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Persist inquiry/body data into new model
|
||||||
|
if (!party.person) party.person = {} as any;
|
||||||
|
const resolvedClientId =
|
||||||
|
(client as any)?._id ?? (client as any)?._doc?._id;
|
||||||
|
party.person.clientId = resolvedClientId;
|
||||||
|
party.person.nationalCodeOfInsurer = body.nationalCodeOfInsurer;
|
||||||
|
party.person.nationalCodeOfDriver = body.nationalCodeOfDriver;
|
||||||
|
party.person.insurerLicense = body.insurerLicense;
|
||||||
|
party.person.driverLicense = body.driverLicense;
|
||||||
|
party.person.driverIsInsurer = body.driverIsInsurer;
|
||||||
|
party.person.isNewCar = body.isNewCar;
|
||||||
|
party.person.userNoCertificate = body.userNoCertificate;
|
||||||
|
party.person.insurerBirthday = body.insurerBirthday;
|
||||||
|
party.person.driverBirthday =
|
||||||
|
body.driverBirthday ??
|
||||||
|
(body.driverIsInsurer ? String(body.insurerBirthday) : null);
|
||||||
|
|
||||||
|
if (!party.vehicle) party.vehicle = {} as any;
|
||||||
|
party.vehicle.isNew = body.isNewCar;
|
||||||
|
party.vehicle.plateId =
|
||||||
|
typeof body.plateId === "string"
|
||||||
|
? body.plateId
|
||||||
|
: this.plateToPlateIdString(body.plate);
|
||||||
|
party.vehicle.name = inquiryMapped?.MapTypNam;
|
||||||
|
party.vehicle.type = `${inquiryMapped?.UsageField} / ${inquiryMapped?.MapUsageName || "-"}`;
|
||||||
party.vehicle.inquiry = {
|
party.vehicle.inquiry = {
|
||||||
...party.vehicle.inquiry, // preserve existing third-party inquiry if present
|
source: "TEJARAT_BLOCK_INQUIRY",
|
||||||
carBody: {
|
raw: inquiryRaw,
|
||||||
source: "TEJARAT_CAR_BODY_INQUIRY",
|
mapped: inquiryMapped,
|
||||||
raw: carBodyInfo.raw,
|
};
|
||||||
mapped: carBodyInfo.mapped,
|
|
||||||
|
if (!party.insurance) party.insurance = {} as any;
|
||||||
|
party.insurance.policyNumber = inquiryMapped?.LastCompanyDocumentNumber;
|
||||||
|
party.insurance.company = clientName;
|
||||||
|
party.insurance.financialCeiling = inquiryMapped?.FinancialCvrCptl;
|
||||||
|
party.insurance.startDate = inquiryMapped?.IssueDate;
|
||||||
|
party.insurance.endDate = inquiryMapped?.EndDate;
|
||||||
|
|
||||||
|
// CAR BODY EXTERNAL API INQUIRY
|
||||||
|
if (req.type === BlameRequestType.CAR_BODY && role === PartyRole.FIRST) {
|
||||||
|
let carBodyInfo: any;
|
||||||
|
try {
|
||||||
|
carBodyInfo = await this.sandHubService.getTejaratCarBodyInquiry({
|
||||||
|
nationalCodeOfInsurer: body.nationalCodeOfInsurer,
|
||||||
|
plate: body.plate,
|
||||||
|
});
|
||||||
|
this.recordPartyCaseInquiryStatus(req, "carBody", role, true, {
|
||||||
|
source: "TEJARAT_CAR_BODY_INQUIRY",
|
||||||
|
raw: carBodyInfo.raw,
|
||||||
|
mapped: carBodyInfo.mapped,
|
||||||
|
});
|
||||||
|
} catch (err: any) {
|
||||||
|
this.logger.error(
|
||||||
|
`[TEJARAT] car body inquiry failed for request=${req._id}: ${err?.message || err}`,
|
||||||
|
);
|
||||||
|
this.recordPartyCaseInquiryStatus(
|
||||||
|
req,
|
||||||
|
"carBody",
|
||||||
|
role,
|
||||||
|
false,
|
||||||
|
{},
|
||||||
|
err,
|
||||||
|
);
|
||||||
|
await (req as any).save();
|
||||||
|
throw new HttpException(
|
||||||
|
"Car body inquiry failed",
|
||||||
|
HttpStatus.BAD_REQUEST,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Raw + mapped stored under vehicle (same pattern as third-party inquiry)
|
||||||
|
party.vehicle.inquiry = {
|
||||||
|
...party.vehicle.inquiry, // preserve existing third-party inquiry if present
|
||||||
|
carBody: {
|
||||||
|
source: "TEJARAT_CAR_BODY_INQUIRY",
|
||||||
|
raw: carBodyInfo.raw,
|
||||||
|
mapped: carBodyInfo.mapped,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Flat key fields extracted alongside third-party insurance fields
|
||||||
|
const m = carBodyInfo.mapped;
|
||||||
|
(party.insurance as any).carBodyInsurance = {
|
||||||
|
policyNumber: m.policyNumber ?? null,
|
||||||
|
companyId: m.companyId ?? null,
|
||||||
|
companyName: m.CompanyName ?? null,
|
||||||
|
insurerName: m.insurerName ?? null,
|
||||||
|
insurerNationalCode: m.insurerNationalCode ?? null,
|
||||||
|
ownerNationalCode: m.ownerNationalCode ?? null,
|
||||||
|
chassisNumber: m.ChassisNumberField ?? null,
|
||||||
|
vin: m.VinNumberField ?? null,
|
||||||
|
motorNumber: m.EngineNumberField ?? null,
|
||||||
|
vehicleGroup: m.vehicleGroupTitle ?? null,
|
||||||
|
vehicleSystem: m.vehicleSystemTitle ?? null,
|
||||||
|
startDate: m.StartDate ?? null,
|
||||||
|
endDate: m.EndDate ?? null,
|
||||||
|
issueDate: m.IssueDate ?? null,
|
||||||
|
noLossYearsCount: m.noLossYearsCount ?? null,
|
||||||
|
lossDocuments: m.lossDocuments ?? [],
|
||||||
|
hasEndorsement: m.hasEndorsement ?? null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Advance workflow
|
||||||
|
await this.advanceWorkflowToNext(req, stepKey);
|
||||||
|
|
||||||
|
// History
|
||||||
|
if (!Array.isArray(req.history)) req.history = [];
|
||||||
|
req.history.push({
|
||||||
|
type: `${stepKey}_SUBMITTED`,
|
||||||
|
actor: {
|
||||||
|
actorId: Types.ObjectId.isValid(user?.sub)
|
||||||
|
? new Types.ObjectId(user.sub)
|
||||||
|
: undefined,
|
||||||
|
actorName: user?.fullName,
|
||||||
|
actorType: "user",
|
||||||
},
|
},
|
||||||
};
|
metadata: {
|
||||||
|
role,
|
||||||
|
companyCode,
|
||||||
|
companyName: clientName,
|
||||||
|
},
|
||||||
|
} as any);
|
||||||
|
|
||||||
// Flat key fields extracted alongside third-party insurance fields
|
await (req as any).save();
|
||||||
const m = carBodyInfo.mapped;
|
|
||||||
(party.insurance as any).carBodyInsurance = {
|
return {
|
||||||
policyNumber: m.policyNumber ?? null,
|
requestId: req._id,
|
||||||
companyId: m.companyId ?? null,
|
publicId: req.publicId,
|
||||||
companyName: m.CompanyName ?? null,
|
workflow: req.workflow,
|
||||||
insurerName: m.insurerName ?? null,
|
blameStatus: req.blameStatus,
|
||||||
insurerNationalCode: m.insurerNationalCode ?? null,
|
status: req.status,
|
||||||
ownerNationalCode: m.ownerNationalCode ?? null,
|
|
||||||
chassisNumber: m.ChassisNumberField ?? null,
|
|
||||||
vin: m.VinNumberField ?? null,
|
|
||||||
motorNumber: m.EngineNumberField ?? null,
|
|
||||||
vehicleGroup: m.vehicleGroupTitle ?? null,
|
|
||||||
vehicleSystem: m.vehicleSystemTitle ?? null,
|
|
||||||
startDate: m.StartDate ?? null,
|
|
||||||
endDate: m.EndDate ?? null,
|
|
||||||
issueDate: m.IssueDate ?? null,
|
|
||||||
noLossYearsCount: m.noLossYearsCount ?? null,
|
|
||||||
lossDocuments: m.lossDocuments ?? [],
|
|
||||||
hasEndorsement: m.hasEndorsement ?? null,
|
|
||||||
};
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Advance workflow
|
|
||||||
await this.advanceWorkflowToNext(req, stepKey);
|
|
||||||
|
|
||||||
// History
|
|
||||||
if (!Array.isArray(req.history)) req.history = [];
|
|
||||||
req.history.push({
|
|
||||||
type: `${stepKey}_SUBMITTED`,
|
|
||||||
actor: {
|
|
||||||
actorId: Types.ObjectId.isValid(user?.sub)
|
|
||||||
? new Types.ObjectId(user.sub)
|
|
||||||
: undefined,
|
|
||||||
actorName: user?.fullName,
|
|
||||||
actorType: "user",
|
|
||||||
},
|
|
||||||
metadata: {
|
|
||||||
role,
|
|
||||||
companyCode,
|
|
||||||
companyName: clientName,
|
|
||||||
},
|
|
||||||
} as any);
|
|
||||||
|
|
||||||
await (req as any).save();
|
|
||||||
|
|
||||||
return {
|
|
||||||
requestId: req._id,
|
|
||||||
publicId: req.publicId,
|
|
||||||
workflow: req.workflow,
|
|
||||||
blameStatus: req.blameStatus,
|
|
||||||
status: req.status,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async addDetailLocationV2(requestId: string, body: LocationDto, user: any) {
|
async addDetailLocationV2(requestId: string, body: LocationDto, user: any) {
|
||||||
|
|||||||
Reference in New Issue
Block a user