Complete inquiry participant flow coverage

This commit is contained in:
SepehrYahyaee
2026-09-13 11:29:43 +03:30
parent d060b6d9e2
commit b5b18114d8
8 changed files with 698 additions and 167 deletions

View File

@@ -542,6 +542,44 @@ export class RequestManagementService {
}
}
/** Inquiry bundle for the still-routable legacy RequestManagement model. */
private async collectLegacyParticipantInquiries(
submission: NormalizedInquirySubmission<Record<string, any>>,
): Promise<Record<string, unknown> | undefined> {
if (submission.participants.legacy) return undefined;
const personal: Record<string, unknown> = {};
for (const participant of submission.participants.participants) {
if (participant.unknown) continue;
personal[participant.participantId] =
await this.sandHubService.getPersonalInquiry(
participant.nationalCode,
participant.birthday,
);
}
const plate = submission.vehicle?.currentPlate ?? submission.dto.plate;
const ownership =
submission.vehicleOwner && plate
? await this.sandHubService.getCarOwnershipInfo(
plate,
submission.vehicleOwner.nationalCode,
)
: undefined;
const drivingLicence =
submission.driver.hasDrivingLicense === false
? {
skipped: true,
reason: "Driver declared no driving licence",
}
: await this.sandHubService.getDrivingLicenseInfo(
submission.driver.nationalCode,
submission.driver.licenseNumber!,
);
return { personal, ownership, drivingLicence };
}
private getPartyIndex(req: any, role: PartyRole): number {
return Array.isArray(req.parties)
? req.parties.findIndex((p) => p?.role === role)
@@ -3271,6 +3309,9 @@ export class RequestManagementService {
user: any,
body: AddPlateDto,
partyType: "firstParty" | "secondParty",
inquirySubmission?: NormalizedInquirySubmission<Record<string, any>>,
policyInquiry?: Record<string, any>,
participantInquiries?: Record<string, unknown>,
) {
if (request.type === "THIRD_PARTY" && partyType === "firstParty") {
this.sandHubService.assertInsuranceMatchesDeployment(
@@ -3282,12 +3323,16 @@ export class RequestManagementService {
const clientName = sandHubReport?.CompanyName;
const companyCode = sandHubReport?.CompanyCode;
const client = await this.clientService.findOrCreateClientByCompanyCode(
companyCode,
clientName,
);
const policyholderUnknown =
inquirySubmission?.thirdPartyPolicyholder.unknown === true;
const client = clientName
? await this.clientService.findOrCreateClientByCompanyCode(
companyCode,
clientName,
)
: null;
if (!client) {
if (!client && !policyholderUnknown) {
throw new HttpException("Client not found", HttpStatus.CONFLICT);
}
@@ -3313,23 +3358,27 @@ export class RequestManagementService {
}
try {
await this.userDbService.findOneAndUpdate(
{ _id: user.sub },
{ clientKey: client["_doc"]._id },
);
if (client) {
await this.userDbService.findOneAndUpdate(
{ _id: user.sub },
{ clientKey: (client as any)["_doc"]?._id ?? (client as any)._id },
);
}
const sandHubDocData = {
...sandHubReport,
plate: body.plate,
nationalCode: body.nationalCodeOfInsurer,
};
const sandHubDoc = await this.sandHubService.sandHubDocumentCreator(
user.sub,
request["_doc"]._id,
sandHubDocData,
);
const sandHubDoc = policyInquiry?.skipped
? null
: await this.sandHubService.sandHubDocumentCreator(
user.sub,
request["_doc"]._id,
sandHubDocData,
);
if (partyType === "firstParty") {
if (partyType === "firstParty" && sandHubDoc) {
await this.requestManagementDbService.findAndUpdate(
{ _id: request._id },
{
@@ -3352,8 +3401,6 @@ export class RequestManagementService {
body.driverBirthday,
[`${partyDetails}.${partyType}CarDetail.carType`]: `${sandHubReport.UsageField} / ${sandHubReport.MapUsageName || "-"}`,
[`${partyDetails}.${partyType}CarDetail.isNewCar`]: body.isNewCar,
[`${partyDetails}.${partyType}Client.clientId`]: client["_doc"]._id,
[`${partyDetails}.${partyType}Client.clientName`]: clientName,
[`${partyDetails}.${partyType}Certificate`]: body.userNoCertificate,
[`${partyDetails}.${partyType}InsuranceDetail.insuranceId`]:
sandHubReport.LastCompanyDocumentNumber,
@@ -3367,12 +3414,38 @@ export class RequestManagementService {
sandHubReport.EndDate,
};
if (client) {
setFields[`${partyDetails}.${partyType}Client.clientId`] =
(client as any)["_doc"]?._id ?? (client as any)._id;
setFields[`${partyDetails}.${partyType}Client.clientName`] = clientName;
}
if (inquirySubmission && !inquirySubmission.participants.legacy) {
setFields[`${partyDetails}.participants`] =
inquirySubmission.participants.participants;
setFields[`${partyDetails}.participantRoles`] =
inquirySubmission.participants.roles;
setFields[`${partyDetails}.participantInquiries`] =
participantInquiries ?? {};
setFields[`${partyDetails}.policyInquiry`] = policyInquiry ?? {};
setFields[`${partyDetails}.registrationState`] =
inquirySubmission.vehicle?.registrationState;
setFields[`${partyDetails}.previousPlateId`] = inquirySubmission.vehicle
?.previousPlate
? this.plateToPlateIdString(inquirySubmission.vehicle.previousPlate)
: undefined;
setFields[`${partyDetails}.vehicleVin`] =
inquirySubmission.vehicle?.vin;
}
// For CAR_BODY type, persist the provider response and its mapped fields.
if (request.type === "CAR_BODY" && partyType === "firstParty") {
const carBodyInquiry = await this.sandHubService.getCarBodyInquiry({
nationalCodeOfInsurer: body.nationalCodeOfInsurer,
plate: body.plate,
} as any);
const carBodyInquiry =
inquirySubmission && !inquirySubmission.participants.legacy
? await this.getCarBodyPlateInquiry(inquirySubmission)
: await this.sandHubService.getCarBodyInquiry({
nationalCodeOfInsurer: body.nationalCodeOfInsurer,
plate: body.plate,
} as any);
const carBodyInfo = carBodyInquiry.mapped as any;
this.logger.log(
@@ -3400,6 +3473,18 @@ export class RequestManagementService {
carBodyInquiry.raw;
setFields["firstPartyDetails.firstPartyCarBodyInsuranceDetail.source"] =
carBodyInquiry.source;
if (inquirySubmission && !inquirySubmission.participants.legacy) {
setFields[`${partyDetails}.policyInquiry`] = {
thirdParty: policyInquiry,
carBody: {
source: carBodyInquiry.source,
plateKind: carBodyInquiry.plateKind,
attempts: carBodyInquiry.attempts,
raw: carBodyInquiry.raw,
mapped: carBodyInquiry.mapped,
},
};
}
setFields[`${partyDetails}.${partyType}Client.clientId`] =
await this.resolveCarBodyPolicyClientId(carBodyInfo);
}
@@ -3456,6 +3541,13 @@ export class RequestManagementService {
(request.nextStep === StepsEnum.F_addPlate ||
request.currentStep === StepsEnum.F_addPlate) &&
request.firstPartyDetails.firstPartyPhoneNumber === user.username;
const partyRole = isFirstParty ? PartyRole.FIRST : PartyRole.SECOND;
const inquirySubmission = this.normalizeInquiryInput(
request.type as BlameRequestType,
body,
partyRole,
);
body = inquirySubmission.dto as unknown as AddPlateDto;
if (!isFirstParty) {
const firstPartyDetails = request.firstPartyDetails as any;
@@ -3482,10 +3574,23 @@ export class RequestManagementService {
// TODO: Activate
// --- Proceed with existing logic if the check passes ---
// 1) Main third-party/block inquiry (existing behavior)
const sanHubResponse = await this.sandHubService.getSandHubResponse({
plate: body.plate,
nationalCodeOfInsurer: body.nationalCodeOfInsurer,
});
const policyInquiry = inquirySubmission.participants.legacy
? await (async () => {
const response = await this.sandHubService.getSandHubResponse({
plate: body.plate,
nationalCodeOfInsurer: body.nationalCodeOfInsurer,
});
return {
raw: response,
mapped: response["_doc"] || response,
plateKind: "CURRENT",
attempts: [{ plateKind: "CURRENT", succeeded: true, usable: true }],
};
})()
: await this.getThirdPartyPlateInquiry(inquirySubmission);
const sanHubResponse = policyInquiry.mapped;
const participantInquiries =
await this.collectLegacyParticipantInquiries(inquirySubmission);
// if (sanHubResponse.Error) {
// throw new HttpException(
@@ -3520,6 +3625,9 @@ export class RequestManagementService {
user,
body,
isFirstParty ? "firstParty" : "secondParty",
inquirySubmission,
policyInquiry,
participantInquiries,
);
}
@@ -6292,7 +6400,7 @@ export class RequestManagementService {
return none;
}
/** Party payload for user blame detail: no national codes, licenses, phone, birthdays, or vehicle inquiry blobs. */
/** Party payload for user blame detail: role links are visible, but participant PII and inquiry blobs remain private. */
private async sanitizePartyForBlameUserView(
party: any,
): Promise<Record<string, unknown>> {
@@ -6311,12 +6419,22 @@ export class RequestManagementService {
const vehicle = party.vehicle
? {
plateId: party.vehicle.plateId,
previousPlateId: party.vehicle.previousPlateId,
registrationState: party.vehicle.registrationState,
name: party.vehicle.name,
model: party.vehicle.model,
type: party.vehicle.type,
isNew: party.vehicle.isNew,
}
: undefined;
const participants = Array.isArray(party.participants)
? party.participants.map((participant: any) => ({
participantId: participant.participantId,
fullName: participant.fullName,
hasDrivingLicense: participant.hasDrivingLicense,
unknown: participant.unknown,
}))
: undefined;
const evidenceRaw = party.evidence as Record<string, unknown> | undefined;
const evidence: Record<string, unknown> | undefined = evidenceRaw
? { ...evidenceRaw }
@@ -6350,6 +6468,8 @@ export class RequestManagementService {
return {
role: party.role,
participants,
participantRoles: party.participantRoles,
person: safePerson,
carBodyFirstForm: party.carBodyFirstForm,
location: party.location,
@@ -6689,10 +6809,26 @@ export class RequestManagementService {
);
}
const carBodyInquiry = await this.getCarBodyPlateInquiry(inquirySubmission);
let carBodyInquiry: any;
try {
carBodyInquiry = await this.getCarBodyPlateInquiry(inquirySubmission);
} catch (error: any) {
this.recordPartyCaseInquiryStatus(
req,
"carBody",
PartyRole.FIRST,
false,
{ attempts: error?.attempts ?? [] },
error,
);
await (req as any).save();
this.throwCarBodyInquiryFailure(error);
}
const carBodyInfo = carBodyInquiry.mapped as any;
this.recordPartyCaseInquiryStatus(req, "carBody", PartyRole.FIRST, true, {
source: carBodyInquiry.source,
plateKind: carBodyInquiry.plateKind,
attempts: carBodyInquiry.attempts,
raw: carBodyInquiry.raw,
mapped: carBodyInfo,
});
@@ -7669,13 +7805,30 @@ export class RequestManagementService {
stepsToAdd.push(StepsEnum.S_addDescription);
// Process first party plate with SandHub lookup
const firstPartyPlate = formData.firstPartyPlate;
const firstInquirySubmission = this.normalizeInquiryInput(
BlameRequestType.THIRD_PARTY,
formData.firstPartyPlate,
PartyRole.FIRST,
);
const firstPartyPlate = firstInquirySubmission.dto;
try {
const sandHubResponse = await this.sandHubService.getSandHubResponse({
plate: firstPartyPlate.plate,
nationalCodeOfInsurer: firstPartyPlate.nationalCodeOfInsurer,
});
const sandHubReport = sandHubResponse["_doc"] || sandHubResponse;
const firstPolicyInquiry = firstInquirySubmission.participants.legacy
? await (async () => {
const response = await this.sandHubService.getSandHubResponse({
plate: firstPartyPlate.plate,
nationalCodeOfInsurer: firstPartyPlate.nationalCodeOfInsurer,
});
return {
raw: response,
mapped: response["_doc"] || response,
plateKind: "CURRENT",
attempts: [
{ plateKind: "CURRENT", succeeded: true, usable: true },
],
};
})()
: await this.getThirdPartyPlateInquiry(firstInquirySubmission);
const sandHubReport = firstPolicyInquiry.mapped;
this.sandHubService.assertInsuranceMatchesDeployment(
sandHubReport,
"THIRD_PARTY",
@@ -7727,6 +7880,26 @@ export class RequestManagementService {
startDate: sandHubReport?.IssueDate || sandHubReport?.StartDate,
endDate: sandHubReport?.EndDate,
};
if (!firstInquirySubmission.participants.legacy) {
firstPartyDetails.participants =
firstInquirySubmission.participants.participants;
firstPartyDetails.participantRoles =
firstInquirySubmission.participants.roles;
firstPartyDetails.participantInquiries =
await this.collectLegacyParticipantInquiries(
firstInquirySubmission,
);
firstPartyDetails.policyInquiry = firstPolicyInquiry;
firstPartyDetails.registrationState =
firstInquirySubmission.vehicle?.registrationState;
firstPartyDetails.previousPlateId = firstInquirySubmission.vehicle
?.previousPlate
? this.plateToPlateIdString(
firstInquirySubmission.vehicle.previousPlate,
)
: undefined;
firstPartyDetails.vehicleVin = firstInquirySubmission.vehicle?.vin;
}
// Update the firstPartyDetails in the payload with all plate data
updatePayload.$set.firstPartyDetails = firstPartyDetails;
@@ -7742,26 +7915,47 @@ export class RequestManagementService {
// Process second party plate
try {
const secondPartyPlate = formData.secondParty.plate;
const sandHubResponse = await this.sandHubService.getSandHubResponse({
plate: secondPartyPlate.plate,
nationalCodeOfInsurer: secondPartyPlate.nationalCodeOfInsurer,
});
const sandHubReport = sandHubResponse["_doc"] || sandHubResponse;
const secondInquirySubmission = this.normalizeInquiryInput(
BlameRequestType.THIRD_PARTY,
formData.secondParty.plate,
PartyRole.SECOND,
);
const secondPartyPlate = secondInquirySubmission.dto;
const secondPolicyInquiry = secondInquirySubmission.participants.legacy
? await (async () => {
const response = await this.sandHubService.getSandHubResponse({
plate: secondPartyPlate.plate,
nationalCodeOfInsurer: secondPartyPlate.nationalCodeOfInsurer,
});
return {
raw: response,
mapped: response["_doc"] || response,
plateKind: "CURRENT",
attempts: [
{ plateKind: "CURRENT", succeeded: true, usable: true },
],
};
})()
: await this.getThirdPartyPlateInquiry(secondInquirySubmission);
const sandHubReport = secondPolicyInquiry.mapped;
const clientName =
sandHubReport?.CompanyName || sandHubReport?.LastCompanyName;
const companyCode = sandHubReport?.CompanyCode;
// Try to find client by company code first (more reliable)
const client = companyCode
? await this.clientService.findOrCreateClientByCompanyCode(
companyCode,
clientName,
)
: await this.clientService.findOne({ clientName: clientName });
const policyholderUnknown =
secondInquirySubmission.thirdPartyPolicyholder.unknown === true;
const client = clientName
? companyCode
? await this.clientService.findOrCreateClientByCompanyCode(
companyCode,
clientName,
)
: await this.clientService.findOne({ clientName: clientName })
: null;
if (!client) {
if (!client && !policyholderUnknown) {
throw new NotFoundException(
`Client not found for company: ${clientName}${companyCode ? ` (code: ${companyCode})` : ""}`,
);
@@ -7778,10 +7972,12 @@ export class RequestManagementService {
carType: `${sandHubReport?.UsageField || ""} / ${sandHubReport?.MapUsageName || "-"}`,
isNewCar: secondPartyPlate.isNewCar,
};
secondPartyDetails.secondPartyClient = {
clientId: (client as any)["_doc"]?._id || (client as any)._id,
clientName: clientName,
};
if (client) {
secondPartyDetails.secondPartyClient = {
clientId: (client as any)["_doc"]?._id || (client as any)._id,
clientName: clientName,
};
}
secondPartyDetails.secondPartyCertificate =
secondPartyPlate.userNoCertificate;
secondPartyDetails.secondPartyInsuranceDetail = {
@@ -7795,6 +7991,26 @@ export class RequestManagementService {
startDate: sandHubReport?.IssueDate || sandHubReport?.StartDate,
endDate: sandHubReport?.EndDate,
};
if (!secondInquirySubmission.participants.legacy) {
secondPartyDetails.participants =
secondInquirySubmission.participants.participants;
secondPartyDetails.participantRoles =
secondInquirySubmission.participants.roles;
secondPartyDetails.participantInquiries =
await this.collectLegacyParticipantInquiries(
secondInquirySubmission,
);
secondPartyDetails.policyInquiry = secondPolicyInquiry;
secondPartyDetails.registrationState =
secondInquirySubmission.vehicle?.registrationState;
secondPartyDetails.previousPlateId = secondInquirySubmission.vehicle
?.previousPlate
? this.plateToPlateIdString(
secondInquirySubmission.vehicle.previousPlate,
)
: undefined;
secondPartyDetails.vehicleVin = secondInquirySubmission.vehicle?.vin;
}
// Update the secondPartyDetails in the payload
updatePayload.$set.secondPartyDetails = secondPartyDetails;
@@ -8014,13 +8230,31 @@ export class RequestManagementService {
stepsToAdd.push(StepsEnum.F_addDescription);
// Process first party plate with SandHub lookup
const firstPartyPlate = formData.firstPartyPlate;
const firstInquirySubmission = this.normalizeInquiryInput(
BlameRequestType.CAR_BODY,
formData.firstPartyPlate,
PartyRole.FIRST,
);
const firstPartyPlate = firstInquirySubmission.dto;
try {
const sandHubResponse = await this.sandHubService.getSandHubResponse({
plate: firstPartyPlate.plate,
nationalCodeOfInsurer: firstPartyPlate.nationalCodeOfInsurer,
});
const sandHubReport = sandHubResponse["_doc"] || sandHubResponse;
const thirdPartyPolicyInquiry = firstInquirySubmission.participants
.legacy
? await (async () => {
const response = await this.sandHubService.getSandHubResponse({
plate: firstPartyPlate.plate,
nationalCodeOfInsurer: firstPartyPlate.nationalCodeOfInsurer,
});
return {
raw: response,
mapped: response["_doc"] || response,
plateKind: "CURRENT",
attempts: [
{ plateKind: "CURRENT", succeeded: true, usable: true },
],
};
})()
: await this.getThirdPartyPlateInquiry(firstInquirySubmission);
const sandHubReport = thirdPartyPolicyInquiry.mapped;
const clientName =
sandHubReport?.CompanyName || sandHubReport?.LastCompanyName;
@@ -8070,10 +8304,12 @@ export class RequestManagementService {
};
// CAR_BODY specific insurance info
const carBodyInquiry = await this.sandHubService.getCarBodyInquiry({
nationalCodeOfInsurer: firstPartyPlate.nationalCodeOfInsurer,
plate: firstPartyPlate.plate,
} as any);
const carBodyInquiry = firstInquirySubmission.participants.legacy
? await this.sandHubService.getCarBodyInquiry({
nationalCodeOfInsurer: firstPartyPlate.nationalCodeOfInsurer,
plate: firstPartyPlate.plate,
} as any)
: await this.getCarBodyPlateInquiry(firstInquirySubmission);
const carBodyInfo = carBodyInquiry.mapped as any;
firstPartyDetails.firstPartyCarBodyInsuranceDetail = {
@@ -8085,6 +8321,35 @@ export class RequestManagementService {
raw: carBodyInquiry.raw,
source: carBodyInquiry.source,
};
if (!firstInquirySubmission.participants.legacy) {
firstPartyDetails.participants =
firstInquirySubmission.participants.participants;
firstPartyDetails.participantRoles =
firstInquirySubmission.participants.roles;
firstPartyDetails.participantInquiries =
await this.collectLegacyParticipantInquiries(
firstInquirySubmission,
);
firstPartyDetails.policyInquiry = {
thirdParty: thirdPartyPolicyInquiry,
carBody: {
source: carBodyInquiry.source,
plateKind: carBodyInquiry.plateKind,
attempts: carBodyInquiry.attempts,
raw: carBodyInquiry.raw,
mapped: carBodyInquiry.mapped,
},
};
firstPartyDetails.registrationState =
firstInquirySubmission.vehicle?.registrationState;
firstPartyDetails.previousPlateId = firstInquirySubmission.vehicle
?.previousPlate
? this.plateToPlateIdString(
firstInquirySubmission.vehicle.previousPlate,
)
: undefined;
firstPartyDetails.vehicleVin = firstInquirySubmission.vehicle?.vin;
}
firstPartyDetails.firstPartyClient.clientId =
await this.resolveCarBodyPolicyClientId(carBodyInfo);