forked from Yara724/api
Fix transferred vehicle inquiry routing
This commit is contained in:
68
src/request-management/inquiry-refresh.service.spec.ts
Normal file
68
src/request-management/inquiry-refresh.service.spec.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import { InquiryRefreshService } from "./inquiry-refresh.service";
|
||||
|
||||
describe("InquiryRefreshService current-identifier routing", () => {
|
||||
const createService = () => {
|
||||
const service = Object.create(InquiryRefreshService.prototype) as any;
|
||||
service.plateNormalizer = {
|
||||
normalizePlateText: (value: string) => value,
|
||||
};
|
||||
service.cleanString = (value: unknown) =>
|
||||
value === undefined || value === null ? "" : String(value).trim();
|
||||
return service;
|
||||
};
|
||||
|
||||
it("uses the participant-linked current policyholder, not transfer metadata", () => {
|
||||
const service = createService();
|
||||
const party = {
|
||||
participants: [
|
||||
{
|
||||
participantId: "THIRD_PARTY_POLICYHOLDER",
|
||||
nationalCode: "0011111111",
|
||||
},
|
||||
],
|
||||
participantRoles: {
|
||||
thirdPartyPolicyholder: "THIRD_PARTY_POLICYHOLDER",
|
||||
},
|
||||
person: {
|
||||
nationalCodeOfInsurer: "0099999999",
|
||||
nationalCodeOfDriver: "0088888888",
|
||||
},
|
||||
vehicle: {
|
||||
registrationState: "RECENTLY_TRANSFERRED",
|
||||
plateId: "22-44-ب-111",
|
||||
previousPlateId: "33-55-ج-222",
|
||||
previousPolicyholderNationalCode: "0077777777",
|
||||
},
|
||||
};
|
||||
|
||||
expect(service.resolveCurrentPolicyholderNationalCode(party)).toBe(
|
||||
"0011111111",
|
||||
);
|
||||
expect(service.resolvePartyPlate(party)).toEqual({
|
||||
ir: 22,
|
||||
leftDigits: 44,
|
||||
centerAlphabet: "ب",
|
||||
centerDigits: 111,
|
||||
});
|
||||
});
|
||||
|
||||
it("fails closed instead of rebuilding a plate from an old inquiry payload", () => {
|
||||
const service = createService();
|
||||
const party = {
|
||||
vehicle: {
|
||||
registrationState: "RECENTLY_TRANSFERRED",
|
||||
previousPlateId: "33-55-ج-222",
|
||||
inquiry: {
|
||||
mapped: {
|
||||
Plk1: 55,
|
||||
Plk2: "ج",
|
||||
Plk3: 222,
|
||||
PlkSrl: 33,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(service.resolvePartyPlate(party)).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -18,6 +18,10 @@ import {
|
||||
ReinquiryPartyResultDto,
|
||||
} from "./dto/reinquiry-inquiries.dto";
|
||||
import { getInquiryErrorMessage } from "src/common/utils/inquiry-error";
|
||||
import { InquiryParticipantRole } from "src/common/dto/inquiry-participants.dto";
|
||||
import {
|
||||
participantForStoredPartyRole,
|
||||
} from "./inquiry-participant-resolver";
|
||||
|
||||
type PlateParts = {
|
||||
leftDigits: number;
|
||||
@@ -193,10 +197,10 @@ export class InquiryRefreshService {
|
||||
let partyChanged = false;
|
||||
let inquiriesChanged = false;
|
||||
|
||||
// Re-inquiry must use the canonical current identifiers only. Previous
|
||||
// transfer metadata and old provider payloads are never valid lookup input.
|
||||
const plate = this.resolvePartyPlate(party);
|
||||
const nationalCode =
|
||||
this.cleanString(party?.person?.nationalCodeOfInsurer) ||
|
||||
this.cleanString(party?.person?.nationalCodeOfDriver);
|
||||
const nationalCode = this.resolveCurrentPolicyholderNationalCode(party);
|
||||
const birthDate =
|
||||
party?.person?.insurerBirthday ??
|
||||
party?.person?.driverBirthday ??
|
||||
@@ -619,12 +623,14 @@ export class InquiryRefreshService {
|
||||
);
|
||||
if (fromPlateId) return fromPlateId;
|
||||
|
||||
const candidates = [
|
||||
party?.vehicle?.inquiry?.mapped,
|
||||
party?.vehicle?.inquiry?.raw,
|
||||
party?.vehicle?.inquiry,
|
||||
party?.vehicle,
|
||||
].filter(Boolean);
|
||||
// A missing plateId is intentionally not recovered from an inquiry
|
||||
// response: that response may belong to a previous plate on transferred
|
||||
// vehicles. The refresh operation must fail closed instead of mixing
|
||||
// identifiers.
|
||||
const currentPlate = party?.vehicle?.currentPlate;
|
||||
if (!currentPlate || typeof currentPlate !== "object") return null;
|
||||
|
||||
const candidates = [currentPlate];
|
||||
|
||||
for (const candidate of candidates) {
|
||||
const leftDigits = this.firstPresent(
|
||||
@@ -675,6 +681,19 @@ export class InquiryRefreshService {
|
||||
return null;
|
||||
}
|
||||
|
||||
private resolveCurrentPolicyholderNationalCode(
|
||||
party: Record<string, any>,
|
||||
): string {
|
||||
const participant = participantForStoredPartyRole(
|
||||
party,
|
||||
InquiryParticipantRole.THIRD_PARTY_POLICYHOLDER,
|
||||
);
|
||||
return (
|
||||
this.cleanString(participant?.nationalCode) ||
|
||||
this.cleanString(party?.person?.nationalCodeOfInsurer)
|
||||
);
|
||||
}
|
||||
|
||||
private parsePlateFromCompactString(plateId?: string): PlateParts | null {
|
||||
if (!plateId) return null;
|
||||
const parts = String(plateId).split("-");
|
||||
|
||||
@@ -215,7 +215,7 @@ describe("RequestManagementService policyholder inquiry routing", () => {
|
||||
expect(service.getThirdPartyVinInquiry).toHaveBeenCalledTimes(1);
|
||||
expect(service.sandHubService.getCarBodyInquiry).toHaveBeenCalledWith({
|
||||
nationalCodeOfInsurer: "0033333333",
|
||||
plate: vehicle.vin,
|
||||
plate: currentPlate,
|
||||
});
|
||||
expect(req.inquiries.thirdParty.has).toBe(true);
|
||||
expect(req.inquiries.carBody.has).toBe(true);
|
||||
|
||||
@@ -14,6 +14,7 @@ import { ExternalExceptionFilter } from "@nestjs/core/exceptions/external-except
|
||||
import { Types } from "mongoose";
|
||||
import { ClientService } from "src/client/client.service";
|
||||
import { AddPlateDto } from "src/profile/dto/user/AddPlateDto";
|
||||
import { VehicleRegistrationState } from "src/common/dto/inquiry-participants.dto";
|
||||
import {
|
||||
CarBodyFormDto,
|
||||
CarBodySecondForm,
|
||||
@@ -466,6 +467,29 @@ export class RequestManagementService {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* A VIN submission still carries the canonical current plate. For a recent
|
||||
* transfer, the car-body policy must be checked against that current plate
|
||||
* and the current policyholder; the previous plate is historical metadata
|
||||
* only. For ordinary VIN submissions we retain the dedicated VIN lookup.
|
||||
*/
|
||||
private async getCarBodyInquiryForVinSubmission(
|
||||
submission: NormalizedInquirySubmission<Record<string, any>>,
|
||||
): Promise<any> {
|
||||
if (
|
||||
submission.vehicle?.registrationState ===
|
||||
VehicleRegistrationState.RECENTLY_TRANSFERRED
|
||||
) {
|
||||
return this.getCarBodyPlateInquiry(submission);
|
||||
}
|
||||
|
||||
const subjects = resolveInquirySubjects(submission);
|
||||
return this.sandHubService.getCarBodyInquiry({
|
||||
nationalCodeOfInsurer: subjects.carBodyPolicyNationalCode!,
|
||||
plate: submission.vehicle?.vin as any,
|
||||
});
|
||||
}
|
||||
|
||||
private async getThirdPartyVinInquiry(
|
||||
submission: NormalizedInquirySubmission<Record<string, any>>,
|
||||
options?: Record<string, any>,
|
||||
@@ -2519,10 +2543,8 @@ export class RequestManagementService {
|
||||
// flow before the case is allowed to advance.
|
||||
if (req.type === BlameRequestType.CAR_BODY && role === PartyRole.FIRST) {
|
||||
try {
|
||||
const carBodyInfo = await this.sandHubService.getCarBodyInquiry({
|
||||
nationalCodeOfInsurer: subjects.carBodyPolicyNationalCode!,
|
||||
plate: body.vin,
|
||||
});
|
||||
const carBodyInfo =
|
||||
await this.getCarBodyInquiryForVinSubmission(inquirySubmission);
|
||||
this.recordPartyCaseInquiryStatus(req, "carBody", role, true, {
|
||||
source: carBodyInfo.source,
|
||||
raw: carBodyInfo.raw,
|
||||
@@ -11117,10 +11139,8 @@ export class RequestManagementService {
|
||||
partyRole === PartyRole.FIRST
|
||||
) {
|
||||
try {
|
||||
const carBodyInfo = await this.sandHubService.getCarBodyInquiry({
|
||||
nationalCodeOfInsurer: subjects.carBodyPolicyNationalCode!,
|
||||
plate: partyData.vin as any, // VIN used as identifier for CAR_BODY
|
||||
});
|
||||
const carBodyInfo =
|
||||
await this.getCarBodyInquiryForVinSubmission(inquirySubmission);
|
||||
this.recordPartyCaseInquiryStatus(req, "carBody", partyRole, true, {
|
||||
source: carBodyInfo.source,
|
||||
raw: carBodyInfo.raw,
|
||||
|
||||
Reference in New Issue
Block a user