forked from Yara724/api
Harden inquiry participant edge cases
This commit is contained in:
@@ -124,6 +124,7 @@ import { resolveLinkedUserIdStrings } from "src/helpers/user-access-resolver";
|
||||
import { normalizePlateText } from "src/utils/plate-normalizer/plate-normalizer.service";
|
||||
import { fanavaranClaimReferences } from "src/claim-request-management/fanavaran-claim-references";
|
||||
import {
|
||||
isMappedPolicyCurrent,
|
||||
NormalizedInquirySubmission,
|
||||
normalizeInquirySubmission,
|
||||
runPlateInquiryWithFallback,
|
||||
@@ -308,8 +309,12 @@ export class RequestManagementService {
|
||||
private normalizeInquiryInput<T extends Record<string, any>>(
|
||||
type: BlameRequestType,
|
||||
input: T,
|
||||
partyRole?: PartyRole,
|
||||
): NormalizedInquirySubmission<T> {
|
||||
return normalizeInquirySubmission(type, input);
|
||||
return normalizeInquirySubmission(type, input, {
|
||||
allowUnknownThirdPartyPolicyholder:
|
||||
type === BlameRequestType.THIRD_PARTY && partyRole === PartyRole.SECOND,
|
||||
});
|
||||
}
|
||||
|
||||
private applyInquiryParticipantsToParty(
|
||||
@@ -335,6 +340,15 @@ export class RequestManagementService {
|
||||
submission: NormalizedInquirySubmission<Record<string, any>>,
|
||||
options?: Record<string, any>,
|
||||
): Promise<any> {
|
||||
if (submission.thirdPartyPolicyholder.unknown) {
|
||||
return {
|
||||
raw: null,
|
||||
mapped: {},
|
||||
skipped: true,
|
||||
plateKind: "CURRENT",
|
||||
attempts: [],
|
||||
};
|
||||
}
|
||||
const result = await runPlateInquiryWithFallback({
|
||||
vehicle: submission.vehicle,
|
||||
fallbackCurrentPlate: submission.dto.plate,
|
||||
@@ -348,7 +362,9 @@ export class RequestManagementService {
|
||||
options,
|
||||
),
|
||||
isUsable: (value) =>
|
||||
!value?.mapped?.Error && !!value?.mapped?.CompanyName,
|
||||
!value?.mapped?.Error &&
|
||||
!!value?.mapped?.CompanyName &&
|
||||
isMappedPolicyCurrent(value.mapped),
|
||||
mappedValue: (value) => value?.mapped ?? {},
|
||||
});
|
||||
return {
|
||||
@@ -373,6 +389,7 @@ export class RequestManagementService {
|
||||
}),
|
||||
isUsable: (value) =>
|
||||
!!value?.mapped &&
|
||||
isMappedPolicyCurrent(value.mapped) &&
|
||||
!!(
|
||||
value.mapped.policyNumber ||
|
||||
value.mapped.CompanyName ||
|
||||
@@ -395,7 +412,9 @@ export class RequestManagementService {
|
||||
): Promise<void> {
|
||||
const participants = submission.participants.legacy
|
||||
? [submission.thirdPartyPolicyholder]
|
||||
: submission.participants.participants;
|
||||
: submission.participants.participants.filter(
|
||||
(participant) => !participant.unknown,
|
||||
);
|
||||
const results: Record<string, unknown> = {};
|
||||
|
||||
for (const participant of participants) {
|
||||
@@ -535,6 +554,7 @@ export class RequestManagementService {
|
||||
message: err?.message || String(err),
|
||||
status: err?.response?.status,
|
||||
data: err?.response?.data,
|
||||
...(Array.isArray(err?.attempts) ? { attempts: err.attempts } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1575,9 +1595,15 @@ export class RequestManagementService {
|
||||
: undefined;
|
||||
}
|
||||
|
||||
const inquirySubmission = this.normalizeInquiryInput(req.type, body);
|
||||
const inquirySubmission = this.normalizeInquiryInput(
|
||||
req.type,
|
||||
body,
|
||||
role,
|
||||
);
|
||||
body = inquirySubmission.dto as unknown as AddPlateDto;
|
||||
this.applyInquiryParticipantsToParty(party, inquirySubmission);
|
||||
const policyholderUnknown =
|
||||
inquirySubmission.thirdPartyPolicyholder.unknown === true;
|
||||
|
||||
// Validation: driver/insurer sameness rules
|
||||
if (body.driverIsInsurer === false) {
|
||||
@@ -1637,13 +1663,25 @@ export class RequestManagementService {
|
||||
this.logger.log(
|
||||
`[TEJARAT] block inquiry mapped for request=${req._id}: ${JSON.stringify(inquiryMapped)}`,
|
||||
);
|
||||
this.recordPartyCaseInquiryStatus(req, "thirdParty", role, true, {
|
||||
source: inquirySource,
|
||||
plateKind: inquiry.plateKind,
|
||||
attempts: inquiry.attempts,
|
||||
raw: inquiryRaw,
|
||||
mapped: inquiryMapped,
|
||||
});
|
||||
this.recordPartyCaseInquiryStatus(
|
||||
req,
|
||||
"thirdParty",
|
||||
role,
|
||||
!inquiry.skipped,
|
||||
{
|
||||
source: inquirySource,
|
||||
plateKind: inquiry.plateKind,
|
||||
attempts: inquiry.attempts,
|
||||
raw: inquiryRaw,
|
||||
mapped: inquiryMapped,
|
||||
...(inquiry.skipped
|
||||
? {
|
||||
skipped: true,
|
||||
reason: "Third-party policyholder is unknown",
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
);
|
||||
if (inquiry.offline?.fanavaranDriverId != null) {
|
||||
if (!party.person) party.person = {} as any;
|
||||
party.person.fanavaranDriverId = inquiry.offline.fanavaranDriverId;
|
||||
@@ -1714,17 +1752,19 @@ export class RequestManagementService {
|
||||
|
||||
// Find client by company code
|
||||
const clientName = inquiryMapped?.CompanyName;
|
||||
if (!clientName) {
|
||||
if (!clientName && !policyholderUnknown) {
|
||||
throw new BadRequestException(
|
||||
`CompanyName missing from inquiry response`,
|
||||
);
|
||||
}
|
||||
const companyCode = inquiryMapped?.CompanyCode;
|
||||
const client = await this.clientService.findOrCreateClientByCompanyCode(
|
||||
companyCode,
|
||||
clientName,
|
||||
);
|
||||
if (!client) {
|
||||
const client = clientName
|
||||
? await this.clientService.findOrCreateClientByCompanyCode(
|
||||
companyCode,
|
||||
clientName,
|
||||
)
|
||||
: null;
|
||||
if (clientName && !client) {
|
||||
throw new BadRequestException(
|
||||
`CompanyCode missing or invalid in inquiry response`,
|
||||
);
|
||||
@@ -1734,7 +1774,7 @@ export class RequestManagementService {
|
||||
if (!party.person) party.person = {} as any;
|
||||
const resolvedClientId =
|
||||
(client as any)?._id ?? (client as any)?._doc?._id;
|
||||
party.person.clientId = resolvedClientId;
|
||||
if (resolvedClientId) party.person.clientId = resolvedClientId;
|
||||
party.person.nationalCodeOfInsurer = body.nationalCodeOfInsurer;
|
||||
party.person.nationalCodeOfDriver = body.nationalCodeOfDriver;
|
||||
party.person.insurerLicense = body.insurerLicense;
|
||||
@@ -1980,7 +2020,11 @@ export class RequestManagementService {
|
||||
: undefined;
|
||||
}
|
||||
|
||||
const inquirySubmission = this.normalizeInquiryInput(req.type, body);
|
||||
const inquirySubmission = this.normalizeInquiryInput(
|
||||
req.type,
|
||||
body,
|
||||
role,
|
||||
);
|
||||
body = inquirySubmission.dto as unknown as InitialFormVinDto;
|
||||
this.applyInquiryParticipantsToParty(party, inquirySubmission);
|
||||
|
||||
@@ -2123,7 +2167,7 @@ export class RequestManagementService {
|
||||
// Persist party data
|
||||
const resolvedClientId =
|
||||
(client as any)?._id ?? (client as any)?._doc?._id;
|
||||
party.person.clientId = resolvedClientId;
|
||||
if (resolvedClientId) party.person.clientId = resolvedClientId;
|
||||
party.person.nationalCodeOfInsurer = body.nationalCodeOfInsurer;
|
||||
party.person.nationalCodeOfDriver = body.nationalCodeOfDriver;
|
||||
party.person.insurerLicense = body.insurerLicense;
|
||||
@@ -6578,6 +6622,7 @@ export class RequestManagementService {
|
||||
const inquirySubmission = this.normalizeInquiryInput(
|
||||
req.type,
|
||||
formData.firstPartyPlate,
|
||||
PartyRole.FIRST,
|
||||
);
|
||||
const firstPartyPlate = inquirySubmission.dto;
|
||||
let sandHubReport: any;
|
||||
@@ -6586,8 +6631,7 @@ export class RequestManagementService {
|
||||
? await (async () => {
|
||||
const response = await this.sandHubService.getSandHubResponse({
|
||||
plate: firstPartyPlate.plate,
|
||||
nationalCodeOfInsurer:
|
||||
firstPartyPlate.nationalCodeOfInsurer,
|
||||
nationalCodeOfInsurer: firstPartyPlate.nationalCodeOfInsurer,
|
||||
});
|
||||
const mapped = response["_doc"] || response;
|
||||
return {
|
||||
@@ -6857,8 +6901,14 @@ export class RequestManagementService {
|
||||
desc: string,
|
||||
role: PartyRole,
|
||||
) => {
|
||||
const inquirySubmission = this.normalizeInquiryInput(req.type, plateDto);
|
||||
const inquirySubmission = this.normalizeInquiryInput(
|
||||
req.type,
|
||||
plateDto,
|
||||
role,
|
||||
);
|
||||
plateDto = inquirySubmission.dto;
|
||||
const policyholderUnknown =
|
||||
inquirySubmission.thirdPartyPolicyholder.unknown === true;
|
||||
let inquiry: any;
|
||||
try {
|
||||
if (inquirySubmission.participants.legacy) {
|
||||
@@ -6871,9 +6921,7 @@ export class RequestManagementService {
|
||||
raw: mapped,
|
||||
mapped,
|
||||
plateKind: "CURRENT",
|
||||
attempts: [
|
||||
{ plateKind: "CURRENT", succeeded: true, usable: true },
|
||||
],
|
||||
attempts: [{ plateKind: "CURRENT", succeeded: true, usable: true }],
|
||||
};
|
||||
} else {
|
||||
inquiry = await this.getThirdPartyPlateInquiry(inquirySubmission);
|
||||
@@ -6897,23 +6945,37 @@ export class RequestManagementService {
|
||||
"THIRD_PARTY",
|
||||
);
|
||||
}
|
||||
this.recordPartyCaseInquiryStatus(req, "thirdParty", role, true, {
|
||||
this.recordPartyCaseInquiryStatus(
|
||||
req,
|
||||
"thirdParty",
|
||||
role,
|
||||
!inquiry.skipped,
|
||||
{
|
||||
source: inquiry.offline?.source ?? "TEJARAT_BLOCK_INQUIRY",
|
||||
plateKind: inquiry.plateKind,
|
||||
attempts: inquiry.attempts,
|
||||
raw: inquiry.raw,
|
||||
mapped: inquiry.mapped,
|
||||
});
|
||||
raw: inquiry.raw,
|
||||
mapped: inquiry.mapped,
|
||||
...(inquiry.skipped
|
||||
? {
|
||||
skipped: true,
|
||||
reason: "Third-party policyholder is unknown",
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
);
|
||||
const clientName =
|
||||
sandHubReport?.CompanyName || sandHubReport?.LastCompanyName;
|
||||
const companyCode = sandHubReport?.CompanyCode;
|
||||
const client = companyCode
|
||||
? await this.clientService.findOrCreateClientByCompanyCode(
|
||||
companyCode,
|
||||
clientName,
|
||||
)
|
||||
: await this.clientService.findOne({ clientName });
|
||||
if (!client) {
|
||||
const client = clientName
|
||||
? companyCode
|
||||
? await this.clientService.findOrCreateClientByCompanyCode(
|
||||
companyCode,
|
||||
clientName,
|
||||
)
|
||||
: await this.clientService.findOne({ clientName })
|
||||
: null;
|
||||
if (!client && !policyholderUnknown) {
|
||||
throw new NotFoundException(
|
||||
`Client not found for company: ${clientName}`,
|
||||
);
|
||||
@@ -9112,9 +9174,15 @@ export class RequestManagementService {
|
||||
partyRole: PartyRole,
|
||||
party: any,
|
||||
): Promise<{ clientId?: string }> {
|
||||
const inquirySubmission = this.normalizeInquiryInput(req.type, partyData);
|
||||
const inquirySubmission = this.normalizeInquiryInput(
|
||||
req.type,
|
||||
partyData,
|
||||
partyRole,
|
||||
);
|
||||
partyData = inquirySubmission.dto as RunInquiriesV3Dto;
|
||||
this.applyInquiryParticipantsToParty(party, inquirySubmission);
|
||||
const policyholderUnknown =
|
||||
inquirySubmission.thirdPartyPolicyholder.unknown === true;
|
||||
const roleLabel = partyRole === PartyRole.FIRST ? "FIRST" : "SECOND";
|
||||
let clientId: string | undefined;
|
||||
const inquiryOptions = (cid?: string) =>
|
||||
@@ -9137,13 +9205,25 @@ export class RequestManagementService {
|
||||
if (inquiry.offline?.source) {
|
||||
inquirySource = inquiry.offline.source;
|
||||
}
|
||||
this.recordPartyCaseInquiryStatus(req, "thirdParty", partyRole, true, {
|
||||
source: inquirySource,
|
||||
plateKind: inquiry.plateKind,
|
||||
attempts: inquiry.attempts,
|
||||
raw: inquiryRaw,
|
||||
mapped: inquiryMapped,
|
||||
});
|
||||
this.recordPartyCaseInquiryStatus(
|
||||
req,
|
||||
"thirdParty",
|
||||
partyRole,
|
||||
!inquiry.skipped,
|
||||
{
|
||||
source: inquirySource,
|
||||
plateKind: inquiry.plateKind,
|
||||
attempts: inquiry.attempts,
|
||||
raw: inquiryRaw,
|
||||
mapped: inquiryMapped,
|
||||
...(inquiry.skipped
|
||||
? {
|
||||
skipped: true,
|
||||
reason: "Third-party policyholder is unknown",
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
);
|
||||
if (inquiry.offline?.fanavaranDriverId != null) {
|
||||
if (!party.person) party.person = {} as any;
|
||||
party.person.fanavaranDriverId = inquiry.offline.fanavaranDriverId;
|
||||
@@ -9174,16 +9254,18 @@ export class RequestManagementService {
|
||||
|
||||
const clientName = inquiryMapped?.CompanyName;
|
||||
const companyCode = inquiryMapped?.CompanyCode;
|
||||
if (!clientName) {
|
||||
if (!clientName && !policyholderUnknown) {
|
||||
throw new BadRequestException(
|
||||
`CompanyName missing from ${roleLabel} party inquiry response`,
|
||||
);
|
||||
}
|
||||
const client = await this.clientService.findOrCreateClientByCompanyCode(
|
||||
companyCode,
|
||||
clientName,
|
||||
);
|
||||
if (!client) {
|
||||
const client = clientName
|
||||
? await this.clientService.findOrCreateClientByCompanyCode(
|
||||
companyCode,
|
||||
clientName,
|
||||
)
|
||||
: null;
|
||||
if (clientName && !client) {
|
||||
throw new BadRequestException(
|
||||
`CompanyCode missing or invalid in ${roleLabel} party inquiry response`,
|
||||
);
|
||||
@@ -9192,7 +9274,7 @@ export class RequestManagementService {
|
||||
clientId = resolvedClientId ? String(resolvedClientId) : undefined;
|
||||
|
||||
if (!party.person) party.person = {} as any;
|
||||
party.person.clientId = resolvedClientId;
|
||||
if (resolvedClientId) party.person.clientId = resolvedClientId;
|
||||
party.person.nationalCodeOfInsurer = partyData.nationalCodeOfInsurer;
|
||||
party.person.nationalCodeOfDriver = partyData.nationalCodeOfDriver;
|
||||
party.person.driverIsInsurer = partyData.driverIsInsurer;
|
||||
@@ -9337,7 +9419,22 @@ export class RequestManagementService {
|
||||
? partyData.insurerLicense
|
||||
: partyData.driverLicense;
|
||||
|
||||
if (!licenseNumber) {
|
||||
if (
|
||||
!inquirySubmission.participants.legacy &&
|
||||
inquirySubmission.driver.hasDrivingLicense === false
|
||||
) {
|
||||
this.recordPartyCaseInquiryStatus(
|
||||
req,
|
||||
"drivingLicence",
|
||||
partyRole,
|
||||
true,
|
||||
{
|
||||
participantId: inquirySubmission.driver.participantId,
|
||||
skipped: true,
|
||||
reason: "Driver declared no driving licence",
|
||||
},
|
||||
);
|
||||
} else if (!licenseNumber) {
|
||||
this.recordPartyCaseInquiryStatus(
|
||||
req,
|
||||
"drivingLicence",
|
||||
@@ -9683,12 +9780,12 @@ export class RequestManagementService {
|
||||
if (!req) throw new NotFoundException("Blame request not found");
|
||||
this.assertBlameV3ExpertInPerson(req);
|
||||
await this.verifyExpertAccessForBlameV2(req, actor);
|
||||
dto = this.normalizeInquiryInput(req.type, dto).dto as RunInquiriesV3Dto;
|
||||
|
||||
const partyRole = this.resolveV3InquiryPartyRole(req);
|
||||
if (!partyRole) {
|
||||
throw new ConflictException("All party inquiries are already complete.");
|
||||
}
|
||||
dto = this.normalizeInquiryInput(req.type, dto, partyRole)
|
||||
.dto as RunInquiriesV3Dto;
|
||||
|
||||
if (partyRole === PartyRole.FIRST) {
|
||||
const firstIdx = this.getPartyIndex(req, PartyRole.FIRST);
|
||||
@@ -9820,12 +9917,12 @@ export class RequestManagementService {
|
||||
if (!req) throw new NotFoundException("Blame request not found");
|
||||
this.assertBlameV3ExpertInPerson(req);
|
||||
await this.verifyExpertAccessForBlameV2(req, actor);
|
||||
dto = this.normalizeInquiryInput(req.type, dto).dto as RunInquiriesVinV3Dto;
|
||||
|
||||
const partyRole = this.resolveV3InquiryPartyRole(req);
|
||||
if (!partyRole) {
|
||||
throw new ConflictException("All party inquiries are already complete.");
|
||||
}
|
||||
dto = this.normalizeInquiryInput(req.type, dto, partyRole)
|
||||
.dto as RunInquiriesVinV3Dto;
|
||||
|
||||
if (partyRole === PartyRole.FIRST) {
|
||||
const firstIdx = this.getPartyIndex(req, PartyRole.FIRST);
|
||||
@@ -9952,7 +10049,11 @@ export class RequestManagementService {
|
||||
partyRole: PartyRole,
|
||||
party: any,
|
||||
): Promise<{ clientId?: string }> {
|
||||
const inquirySubmission = this.normalizeInquiryInput(req.type, partyData);
|
||||
const inquirySubmission = this.normalizeInquiryInput(
|
||||
req.type,
|
||||
partyData,
|
||||
partyRole,
|
||||
);
|
||||
partyData = inquirySubmission.dto as RunInquiriesVinV3Dto;
|
||||
this.applyInquiryParticipantsToParty(party, inquirySubmission);
|
||||
const roleLabel = partyRole === PartyRole.FIRST ? "FIRST" : "SECOND";
|
||||
@@ -10018,7 +10119,7 @@ export class RequestManagementService {
|
||||
clientId = resolvedClientId ? String(resolvedClientId) : undefined;
|
||||
|
||||
if (!party.person) party.person = {} as any;
|
||||
party.person.clientId = resolvedClientId;
|
||||
if (resolvedClientId) party.person.clientId = resolvedClientId;
|
||||
party.person.nationalCodeOfInsurer = partyData.nationalCodeOfInsurer;
|
||||
party.person.nationalCodeOfDriver = partyData.nationalCodeOfDriver;
|
||||
party.person.driverIsInsurer = partyData.driverIsInsurer;
|
||||
@@ -10174,7 +10275,22 @@ export class RequestManagementService {
|
||||
? partyData.insurerLicense
|
||||
: partyData.driverLicense;
|
||||
|
||||
if (!licenseNumber) {
|
||||
if (
|
||||
!inquirySubmission.participants.legacy &&
|
||||
inquirySubmission.driver.hasDrivingLicense === false
|
||||
) {
|
||||
this.recordPartyCaseInquiryStatus(
|
||||
req,
|
||||
"drivingLicence",
|
||||
partyRole,
|
||||
true,
|
||||
{
|
||||
participantId: inquirySubmission.driver.participantId,
|
||||
skipped: true,
|
||||
reason: "Driver declared no driving licence",
|
||||
},
|
||||
);
|
||||
} else if (!licenseNumber) {
|
||||
this.recordPartyCaseInquiryStatus(
|
||||
req,
|
||||
"drivingLicence",
|
||||
@@ -11403,7 +11519,7 @@ export class RequestManagementService {
|
||||
if (firstIdx === -1) throw new BadRequestException("First party not found");
|
||||
const firstParty = req.parties[firstIdx];
|
||||
|
||||
dto = this.normalizeInquiryInput(req.type, dto).dto;
|
||||
dto = this.normalizeInquiryInput(req.type, dto, PartyRole.FIRST).dto;
|
||||
|
||||
await this.runPartyInquiriesV3Internal(
|
||||
req,
|
||||
@@ -11526,7 +11642,7 @@ export class RequestManagementService {
|
||||
if (firstIdx === -1) throw new BadRequestException("First party not found");
|
||||
const firstParty = req.parties[firstIdx];
|
||||
|
||||
dto = this.normalizeInquiryInput(req.type, dto).dto;
|
||||
dto = this.normalizeInquiryInput(req.type, dto, PartyRole.FIRST).dto;
|
||||
|
||||
await this.runPartyInquiriesVinV3Internal(
|
||||
req,
|
||||
|
||||
Reference in New Issue
Block a user