forked from Yara724/api
YARA-947, YARA-1038
This commit is contained in:
@@ -18,6 +18,7 @@ import {
|
||||
CarBodyFormDto,
|
||||
CarBodySecondForm,
|
||||
InitialFormDto,
|
||||
InitialFormVinDto,
|
||||
} from "src/request-management/dto/create-request-management.dto";
|
||||
import { BlameVideoDbService } from "src/request-management/entities/db-service/blame-video.db.service";
|
||||
import { BlameVoiceDbService } from "src/request-management/entities/db-service/blame.voice.db.service";
|
||||
@@ -1475,6 +1476,313 @@ export class RequestManagementService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* V2 initial-form submitted with a VIN/chassis number instead of a plate.
|
||||
*
|
||||
* Performs:
|
||||
* 1. ESG `/inquiry/policyByChassis` (or its mock when `vinChassis` is off)
|
||||
* 2. Personal identity inquiry (same as the plate path)
|
||||
*
|
||||
* The inquiry result is mapped through the same `mapEsgPolicyByPlateToOldFormat`
|
||||
* normaliser used by `getTejaratBlockInquiry`, so all downstream party/vehicle
|
||||
* /insurance field assignments are identical.
|
||||
*/
|
||||
async initialFormVinV2(
|
||||
requestId: string,
|
||||
body: InitialFormVinDto,
|
||||
user: any,
|
||||
expectedRole?: string,
|
||||
) {
|
||||
try {
|
||||
const req = await this.blameRequestDbService.findById(requestId);
|
||||
if (!req) throw new NotFoundException("Request not found");
|
||||
|
||||
if (!req.workflow?.currentStep) {
|
||||
throw new BadRequestException("Request workflow is not initialized");
|
||||
}
|
||||
|
||||
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}`,
|
||||
);
|
||||
}
|
||||
|
||||
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`,
|
||||
req,
|
||||
);
|
||||
this.assertExpectedPartyRole(
|
||||
expectedRole,
|
||||
role,
|
||||
this.isBlameOnBehalfActor(req, user),
|
||||
);
|
||||
|
||||
if (!party.person) party.person = {} as any;
|
||||
if (
|
||||
!party.person.userId &&
|
||||
user?.sub &&
|
||||
!this.isBlameOnBehalfActor(req, user)
|
||||
) {
|
||||
party.person.userId = Types.ObjectId.isValid(user.sub)
|
||||
? new Types.ObjectId(user.sub)
|
||||
: undefined;
|
||||
}
|
||||
|
||||
// Validation: driver/insurer sameness rules (identical to plate path)
|
||||
if (body.driverIsInsurer === false) {
|
||||
if (
|
||||
body.nationalCodeOfInsurer === body.nationalCodeOfDriver ||
|
||||
body.insurerLicense === body.driverLicense
|
||||
) {
|
||||
throw new BadRequestException(
|
||||
"Insurer and Driver should be two different persons in this mode.",
|
||||
);
|
||||
}
|
||||
} else if (body.driverIsInsurer === true) {
|
||||
const sameNat =
|
||||
body.nationalCodeOfInsurer === body.nationalCodeOfDriver;
|
||||
const sameLic = body.insurerLicense === body.driverLicense;
|
||||
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: ESG policyByChassis ----
|
||||
const inquiryClientId = party.person?.clientId
|
||||
? String(party.person.clientId)
|
||||
: undefined;
|
||||
const inquiryOptions = inquiryClientId
|
||||
? { clientId: inquiryClientId }
|
||||
: undefined;
|
||||
|
||||
let inquiryRaw: any;
|
||||
let inquiryMapped: any;
|
||||
try {
|
||||
const inquiry = await this.sandHubService.getPolicyByChassisInquiry(
|
||||
body.vin,
|
||||
inquiryOptions,
|
||||
);
|
||||
inquiryRaw = inquiry.raw;
|
||||
inquiryMapped = inquiry.mapped;
|
||||
this.logger.log(
|
||||
`[ESG] policyByChassis raw for request=${req._id}: ${JSON.stringify(inquiryRaw)}`,
|
||||
);
|
||||
this.logger.log(
|
||||
`[ESG] policyByChassis mapped for request=${req._id}: ${JSON.stringify(inquiryMapped)}`,
|
||||
);
|
||||
this.recordPartyCaseInquiryStatus(req, "thirdParty", role, true, {
|
||||
source: "ESG_VIN_INQUIRY",
|
||||
raw: inquiryRaw,
|
||||
mapped: inquiryMapped,
|
||||
});
|
||||
} catch (err: any) {
|
||||
this.logger.error(
|
||||
`[ESG] policyByChassis failed for request=${req._id}: ${err?.message || err}`,
|
||||
);
|
||||
if (err?.response) {
|
||||
this.logger.error(
|
||||
`[ESG] policyByChassis response for request=${req._id}: status=${
|
||||
err.response.status
|
||||
}, data=${JSON.stringify(err.response.data)}`,
|
||||
);
|
||||
}
|
||||
this.recordPartyCaseInquiryStatus(
|
||||
req,
|
||||
"thirdParty",
|
||||
role,
|
||||
false,
|
||||
{},
|
||||
err,
|
||||
);
|
||||
await this.blameRequestDbService.findByIdAndUpdate(req._id, {
|
||||
$set: { inquiries: req.inquiries },
|
||||
});
|
||||
throw new HttpException("VIN inquiry failed", HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
if (inquiryMapped?.Error) {
|
||||
this.logger.warn(
|
||||
`[ESG] policyByChassis error for request=${req._id}: ${JSON.stringify(inquiryMapped.Error)}`,
|
||||
);
|
||||
this.recordPartyCaseInquiryStatus(req, "thirdParty", role, false, {
|
||||
source: "ESG_VIN_INQUIRY",
|
||||
raw: inquiryRaw,
|
||||
mapped: inquiryMapped,
|
||||
});
|
||||
await this.blameRequestDbService.findByIdAndUpdate(req._id, {
|
||||
$set: { inquiries: req.inquiries },
|
||||
});
|
||||
throw new HttpException(
|
||||
inquiryMapped.Error.Message || "VIN inquiry returned error",
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
}
|
||||
|
||||
// ---- External inquiry 2: personal identity (same as plate path) ----
|
||||
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,
|
||||
inquiryOptions,
|
||||
);
|
||||
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 this.blameRequestDbService.findByIdAndUpdate(req._id, {
|
||||
$set: { inquiries: req.inquiries },
|
||||
});
|
||||
throw new HttpException(
|
||||
"Personal identity inquiry failed",
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
}
|
||||
|
||||
// Resolve insurer client from inquiry response
|
||||
const clientName = inquiryMapped?.CompanyName;
|
||||
if (!clientName) {
|
||||
throw new BadRequestException(
|
||||
"CompanyName missing from VIN inquiry response",
|
||||
);
|
||||
}
|
||||
const companyCode = inquiryMapped?.CompanyCode;
|
||||
const client = await this.clientService.findOrCreateClientByCompanyCode(
|
||||
companyCode,
|
||||
clientName,
|
||||
);
|
||||
if (!client) {
|
||||
throw new BadRequestException(
|
||||
"CompanyCode missing or invalid in VIN inquiry response",
|
||||
);
|
||||
}
|
||||
|
||||
// Persist party data
|
||||
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;
|
||||
// Store VIN as the vehicle identifier instead of plate
|
||||
party.vehicle.plateId = body.vin;
|
||||
party.vehicle.name = inquiryMapped?.MapTypNam;
|
||||
party.vehicle.type = `${inquiryMapped?.UsageField} / ${inquiryMapped?.MapUsageName || "-"}`;
|
||||
party.vehicle.inquiry = {
|
||||
source: "ESG_VIN_INQUIRY",
|
||||
raw: inquiryRaw,
|
||||
mapped: inquiryMapped,
|
||||
};
|
||||
|
||||
if (!party.insurance) party.insurance = {} as any;
|
||||
party.insurance.policyNumber = inquiryMapped?.PrntPlcyCmpDocNo;
|
||||
party.insurance.company = clientName;
|
||||
party.insurance.financialCeiling = inquiryMapped?.FinancialCvrCptl;
|
||||
party.insurance.startDate = inquiryMapped?.IssueDate;
|
||||
party.insurance.endDate = inquiryMapped?.EndDate;
|
||||
|
||||
// Advance workflow
|
||||
await this.advanceWorkflowToNext(req, stepKey);
|
||||
|
||||
const historyEntry: any = {
|
||||
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,
|
||||
inquiryType: "VIN",
|
||||
vin: body.vin,
|
||||
},
|
||||
};
|
||||
|
||||
await this.blameRequestDbService.findByIdAndUpdate(req._id, {
|
||||
$set: {
|
||||
[`parties.${idx}.person`]: party.person,
|
||||
[`parties.${idx}.vehicle`]: party.vehicle,
|
||||
[`parties.${idx}.insurance`]: party.insurance,
|
||||
inquiries: req.inquiries,
|
||||
"workflow.completedSteps": req.workflow.completedSteps,
|
||||
"workflow.currentStep": req.workflow.currentStep,
|
||||
"workflow.nextStep": req.workflow.nextStep,
|
||||
status: req.status,
|
||||
},
|
||||
$push: { history: historyEntry },
|
||||
});
|
||||
|
||||
return {
|
||||
requestId: req._id,
|
||||
publicId: req.publicId,
|
||||
workflow: req.workflow,
|
||||
blameStatus: req.blameStatus,
|
||||
status: req.status,
|
||||
};
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async addDetailLocationV2(
|
||||
requestId: string,
|
||||
body: LocationDto,
|
||||
|
||||
Reference in New Issue
Block a user