forked from Yara724/api
Merge pull request 'Fixed Participants' (#314) from s.yahyaee/yara724-api:main into main
Reviewed-on: Yara724/api#314
This commit is contained in:
@@ -4,6 +4,7 @@ import {
|
||||
} from "src/helpers/blame-damaged-party";
|
||||
import { toJalaliDateAndTime } from "src/helpers/date-jalali";
|
||||
import { PartyRole } from "src/request-management/entities/schema/partyRole.enum";
|
||||
import { sanitizeStoredInquiryParticipants } from "src/request-management/inquiry-participant-resolver";
|
||||
import {
|
||||
InsurerFileReportField,
|
||||
InsurerFileReportSection,
|
||||
@@ -611,9 +612,8 @@ function buildParticipantRolesSection(
|
||||
title: string,
|
||||
party: ReportParty | null | undefined,
|
||||
): InsurerFileReportSection | undefined {
|
||||
const participants = Array.isArray(party?.participants)
|
||||
? party.participants
|
||||
: [];
|
||||
const participants =
|
||||
sanitizeStoredInquiryParticipants(party?.participants) ?? [];
|
||||
const assignments = party?.participantRoles;
|
||||
if (!assignments || participants.length === 0) return undefined;
|
||||
|
||||
@@ -627,9 +627,7 @@ function buildParticipantRolesSection(
|
||||
const participant = participants.find(
|
||||
(candidate) => String(candidate.participantId) === String(participantId),
|
||||
);
|
||||
const value = participant?.unknown
|
||||
? PR.unknown
|
||||
: [
|
||||
const value = [
|
||||
asString(participant?.fullName),
|
||||
participant?.nationalCode
|
||||
? `${PR.nationalCode}: ${asString(participant.nationalCode)}`
|
||||
|
||||
@@ -67,7 +67,6 @@ export const PR = {
|
||||
vehicleOwnerRole: "مالک وسیله نقلیه",
|
||||
thirdPartyPolicyholderRole: "بیمهگذار شخص ثالث",
|
||||
carBodyPolicyholderRole: "بیمهگذار بدنه",
|
||||
unknown: "نامشخص",
|
||||
data: "اطلاعات",
|
||||
date: "تاریخ",
|
||||
time: "زمان",
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
IsNotEmpty,
|
||||
IsOptional,
|
||||
IsString,
|
||||
MaxLength,
|
||||
Length,
|
||||
ValidateNested,
|
||||
} from "class-validator";
|
||||
|
||||
@@ -51,14 +51,6 @@ export class InquiryParticipantInputDto {
|
||||
@IsEnum(InquiryParticipantRole)
|
||||
sameAs?: InquiryParticipantRole;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description:
|
||||
"Only for the damaged party's third-party policyholder when the identity is genuinely unknown.",
|
||||
})
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
unknown?: boolean;
|
||||
|
||||
@ApiPropertyOptional({ example: "0012345678" })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@@ -111,10 +103,23 @@ export class InquiryVehicleInputDto {
|
||||
@Type(() => InquiryPlateDto)
|
||||
previousPlate?: InquiryPlateDto;
|
||||
|
||||
@ApiPropertyOptional({ maxLength: 17, example: "NAAM01E15HK123456" })
|
||||
@ApiPropertyOptional({
|
||||
description:
|
||||
"National code of the policyholder associated with previousPlate. Required only for RECENTLY_TRANSFERRED vehicles.",
|
||||
example: "0012345678",
|
||||
})
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@MaxLength(17)
|
||||
previousPolicyholderNationalCode?: string;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
minLength: 17,
|
||||
maxLength: 17,
|
||||
example: "NAAM01E15HK123456",
|
||||
})
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@Length(17, 17)
|
||||
vin?: string;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
|
||||
@@ -92,6 +92,7 @@ import { ClaimWorkflowStep } from "src/Types&Enums/claim-request-management/clai
|
||||
import { ClaimRequiredDocumentType } from "src/Types&Enums/claim-request-management/required-document-type.enum";
|
||||
import { BlameRequestType } from "src/Types&Enums/blame-request-management/blameRequestType.enum";
|
||||
import { PartyRole } from "src/request-management/entities/schema/partyRole.enum";
|
||||
import { sanitizeStoredInquiryParticipants } from "src/request-management/inquiry-participant-resolver";
|
||||
import {
|
||||
GetClaimListV2ResponseDto,
|
||||
ClaimListItemV2Dto,
|
||||
@@ -4551,7 +4552,12 @@ export class ExpertClaimService {
|
||||
(doc as Record<string, unknown>).updatedAtFormatted =
|
||||
`${updatedDate} ${updatedTime}`;
|
||||
|
||||
docRec.parties = enrichBlamePartiesForAgreementView(docRec);
|
||||
docRec.parties = enrichBlamePartiesForAgreementView(docRec).map(
|
||||
(party) => ({
|
||||
...party,
|
||||
participants: sanitizeStoredInquiryParticipants(party.participants),
|
||||
}),
|
||||
);
|
||||
|
||||
return docRec;
|
||||
}
|
||||
@@ -4577,7 +4583,11 @@ export class ExpertClaimService {
|
||||
if (Array.isArray(parties)) {
|
||||
out.parties = parties.map((p: any) =>
|
||||
p && typeof p === "object"
|
||||
? { ...p, vehicle: this.sanitizeVehicleInquiryForApi(p.vehicle) }
|
||||
? {
|
||||
...p,
|
||||
participants: sanitizeStoredInquiryParticipants(p.participants),
|
||||
vehicle: this.sanitizeVehicleInquiryForApi(p.vehicle),
|
||||
}
|
||||
: p,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ import { toJalaliDateAndTime } from "src/helpers/date-jalali";
|
||||
import { enrichBlamePartiesForAgreementView } from "src/helpers/blame-party-agreement-decision";
|
||||
import { BlameRequestType } from "src/Types&Enums/blame-request-management/blameRequestType.enum";
|
||||
import { PartyRole } from "src/request-management/entities/schema/partyRole.enum";
|
||||
import { sanitizeStoredInquiryParticipants } from "src/request-management/inquiry-participant-resolver";
|
||||
import { ListQueryV2Dto } from "src/common/dto/list-query-v2.dto";
|
||||
import {
|
||||
UnifiedFileStatusReportDto,
|
||||
@@ -449,6 +450,7 @@ export class ExpertInsurerService {
|
||||
if (!party || typeof party !== "object") return party;
|
||||
return {
|
||||
...party,
|
||||
participants: sanitizeStoredInquiryParticipants(party.participants),
|
||||
person: this.mapPersonForInsurer(party.person, clientNames),
|
||||
vehicle: this.sanitizeVehicleInquiry(party.vehicle),
|
||||
};
|
||||
|
||||
@@ -66,7 +66,6 @@ export class InquiryParticipant {
|
||||
@Prop({ type: Boolean }) hasDrivingLicense?: boolean;
|
||||
@Prop() licenseNumber?: string;
|
||||
@Prop() licenseType?: string;
|
||||
@Prop({ type: Boolean }) unknown?: boolean;
|
||||
}
|
||||
export const InquiryParticipantSchema =
|
||||
SchemaFactory.createForClass(InquiryParticipant);
|
||||
@@ -94,6 +93,7 @@ export class Vehicle {
|
||||
@Prop({ enum: ["CURRENT", "RECENTLY_TRANSFERRED"] })
|
||||
registrationState?: "CURRENT" | "RECENTLY_TRANSFERRED";
|
||||
@Prop() previousPlateId?: string;
|
||||
@Prop() previousPolicyholderNationalCode?: string;
|
||||
|
||||
/**
|
||||
* Full external inquiry payload (Tejarat/SandHub) stored as-is
|
||||
|
||||
@@ -161,6 +161,7 @@ export class FirstPartyDetail {
|
||||
|
||||
@Prop() registrationState?: string;
|
||||
@Prop() previousPlateId?: string;
|
||||
@Prop() previousPolicyholderNationalCode?: string;
|
||||
@Prop() vehicleVin?: string;
|
||||
}
|
||||
|
||||
@@ -209,6 +210,7 @@ export class SecondPartyDetail {
|
||||
|
||||
@Prop() registrationState?: string;
|
||||
@Prop() previousPlateId?: string;
|
||||
@Prop() previousPolicyholderNationalCode?: string;
|
||||
@Prop() vehicleVin?: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -115,4 +115,34 @@ describe("inquiry participant persistence", () => {
|
||||
|
||||
expect(() => (query as any)._castUpdate(query.getUpdate())).not.toThrow();
|
||||
});
|
||||
|
||||
it("stores the previous policyholder national code with transferred vehicle data", () => {
|
||||
const BlameRequestModel = model<BlameRequest>(
|
||||
"PreviousPolicyholderPersistenceTest",
|
||||
BlameRequestSchema,
|
||||
);
|
||||
const request = new BlameRequestModel({
|
||||
requestNo: "BL-PREVIOUS-POLICYHOLDER",
|
||||
publicId: "PREVIOUS-POLICYHOLDER",
|
||||
type: BlameRequestType.THIRD_PARTY,
|
||||
parties: [
|
||||
{
|
||||
role: PartyRole.FIRST,
|
||||
person: {},
|
||||
vehicle: {
|
||||
plateId: "22-44-ب-111",
|
||||
previousPlateId: "33-55-ج-222",
|
||||
previousPolicyholderNationalCode: "0098765432",
|
||||
registrationState: "RECENTLY_TRANSFERRED",
|
||||
vin: "NAAM01E15HK123456",
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(request.validateSync()).toBeUndefined();
|
||||
expect(request.parties[0].vehicle?.previousPolicyholderNationalCode).toBe(
|
||||
"0098765432",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
resolveInquirySubjects,
|
||||
resolveInquiryVehicle,
|
||||
runPlateInquiryWithFallback,
|
||||
sanitizeStoredInquiryParticipants,
|
||||
vehiclePlateCandidates,
|
||||
} from "./inquiry-participant-resolver";
|
||||
|
||||
@@ -55,6 +56,27 @@ describe("inquiry participant resolver", () => {
|
||||
).toThrow(BadRequestException);
|
||||
});
|
||||
|
||||
it("requires the previous policyholder national code for a recent transfer", () => {
|
||||
expect(() =>
|
||||
resolveInquiryVehicle({
|
||||
registrationState: VehicleRegistrationState.RECENTLY_TRANSFERRED,
|
||||
currentPlate: {
|
||||
leftDigits: "44",
|
||||
centerAlphabet: "ب",
|
||||
centerDigits: "111",
|
||||
ir: "22",
|
||||
},
|
||||
previousPlate: {
|
||||
leftDigits: "55",
|
||||
centerAlphabet: "ج",
|
||||
centerDigits: "222",
|
||||
ir: "33",
|
||||
},
|
||||
vin: "NAAM01E15HK123456",
|
||||
}),
|
||||
).toThrow("previousPolicyholderNationalCode");
|
||||
});
|
||||
|
||||
it("defaults an omitted registration state to CURRENT", () => {
|
||||
expect(
|
||||
resolveInquiryVehicle({
|
||||
@@ -68,6 +90,75 @@ describe("inquiry participant resolver", () => {
|
||||
).toBe(VehicleRegistrationState.CURRENT);
|
||||
});
|
||||
|
||||
it("requires an exact 17-character VIN whenever VIN is provided", () => {
|
||||
expect(() =>
|
||||
resolveInquiryVehicle({
|
||||
registrationState: VehicleRegistrationState.CURRENT,
|
||||
currentPlate: {
|
||||
leftDigits: "44",
|
||||
centerAlphabet: "ب",
|
||||
centerDigits: "111",
|
||||
ir: "22",
|
||||
},
|
||||
vin: "TOO-SHORT",
|
||||
}),
|
||||
).toThrow("vehicle.vin must contain exactly 17 characters.");
|
||||
});
|
||||
|
||||
it("rejects an incomplete current plate", () => {
|
||||
expect(() =>
|
||||
resolveInquiryVehicle({
|
||||
currentPlate: {
|
||||
leftDigits: "44",
|
||||
centerAlphabet: "ب",
|
||||
centerDigits: "",
|
||||
ir: "22",
|
||||
},
|
||||
}),
|
||||
).toThrow("vehicle.currentPlate.centerDigits is required.");
|
||||
});
|
||||
|
||||
it("rejects invalid vehicle choice values", () => {
|
||||
expect(() =>
|
||||
resolveInquiryVehicle({
|
||||
registrationState: "SOLD" as any,
|
||||
currentPlate: {
|
||||
leftDigits: "44",
|
||||
centerAlphabet: "ب",
|
||||
centerDigits: "111",
|
||||
ir: "22",
|
||||
},
|
||||
}),
|
||||
).toThrow("vehicle.registrationState must be CURRENT or RECENTLY_TRANSFERRED.");
|
||||
|
||||
expect(() =>
|
||||
resolveInquiryVehicle({
|
||||
currentPlate: {
|
||||
leftDigits: "44",
|
||||
centerAlphabet: "ب",
|
||||
centerDigits: "111",
|
||||
ir: "22",
|
||||
},
|
||||
isNewCar: "false" as any,
|
||||
}),
|
||||
).toThrow("vehicle.isNewCar must be a boolean.");
|
||||
});
|
||||
|
||||
it("rejects a previous policyholder national code for a current registration", () => {
|
||||
expect(() =>
|
||||
resolveInquiryVehicle({
|
||||
registrationState: VehicleRegistrationState.CURRENT,
|
||||
currentPlate: {
|
||||
leftDigits: "44",
|
||||
centerAlphabet: "ب",
|
||||
centerDigits: "111",
|
||||
ir: "22",
|
||||
},
|
||||
previousPolicyholderNationalCode: "0098765432",
|
||||
}),
|
||||
).toThrow(BadRequestException);
|
||||
});
|
||||
|
||||
it("rejects the removed flat inquiry contract", () => {
|
||||
expect(() =>
|
||||
resolveInquiryParticipants(BlameRequestType.THIRD_PARTY, {
|
||||
@@ -238,6 +329,7 @@ describe("inquiry participant resolver", () => {
|
||||
currentPlate,
|
||||
previousPlate,
|
||||
vin: "NAAM01E15HK123456",
|
||||
previousPolicyholderNationalCode: "0098765432",
|
||||
}),
|
||||
).toEqual([
|
||||
{ kind: "CURRENT", plate: currentPlate },
|
||||
@@ -293,6 +385,7 @@ describe("inquiry participant resolver", () => {
|
||||
currentPlate,
|
||||
previousPlate,
|
||||
vin: "NAAM01E15HK123456",
|
||||
previousPolicyholderNationalCode: "0098765432",
|
||||
},
|
||||
fallbackCurrentPlate: currentPlate,
|
||||
query,
|
||||
@@ -301,6 +394,8 @@ describe("inquiry participant resolver", () => {
|
||||
});
|
||||
|
||||
expect(query).toHaveBeenCalledTimes(2);
|
||||
expect(query).toHaveBeenNthCalledWith(1, currentPlate, "CURRENT");
|
||||
expect(query).toHaveBeenNthCalledWith(2, previousPlate, "PREVIOUS");
|
||||
expect(result.plateKind).toBe("PREVIOUS");
|
||||
expect(result.attempts).toMatchObject([
|
||||
{ plateKind: "CURRENT", succeeded: false, error: "not found" },
|
||||
@@ -449,6 +544,7 @@ describe("inquiry participant resolver", () => {
|
||||
currentPlate,
|
||||
previousPlate,
|
||||
vin: "NAAM01E15HK123456",
|
||||
previousPolicyholderNationalCode: "0098765432",
|
||||
}),
|
||||
fallbackCurrentPlate: currentPlate,
|
||||
query,
|
||||
@@ -487,6 +583,7 @@ describe("inquiry participant resolver", () => {
|
||||
currentPlate,
|
||||
previousPlate,
|
||||
vin: "NAAM01E15HK123456",
|
||||
previousPolicyholderNationalCode: "0098765432",
|
||||
}),
|
||||
fallbackCurrentPlate: currentPlate,
|
||||
query: async () => ({ mapped: { CompanyName: "پارسیان" } }),
|
||||
@@ -526,6 +623,7 @@ describe("inquiry participant resolver", () => {
|
||||
ir: "33",
|
||||
},
|
||||
vin: "NAAM01E15HK123456",
|
||||
previousPolicyholderNationalCode: "0098765432",
|
||||
}),
|
||||
fallbackCurrentPlate: currentPlate,
|
||||
query,
|
||||
@@ -536,7 +634,7 @@ describe("inquiry participant resolver", () => {
|
||||
expect(query).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("allows an explicitly unknown policyholder only for a damaged-party submission", () => {
|
||||
it("rejects the removed unknown policyholder option", () => {
|
||||
const input = {
|
||||
driver: {
|
||||
nationalCode: "0012345678",
|
||||
@@ -548,19 +646,24 @@ describe("inquiry participant resolver", () => {
|
||||
};
|
||||
|
||||
expect(() =>
|
||||
resolveInquiryParticipants(BlameRequestType.THIRD_PARTY, input),
|
||||
).toThrow(BadRequestException);
|
||||
resolveInquiryParticipants(BlameRequestType.THIRD_PARTY, input as any),
|
||||
).toThrow("THIRD_PARTY_POLICYHOLDER does not support the unknown option.");
|
||||
});
|
||||
|
||||
const resolved = resolveInquiryParticipants(
|
||||
BlameRequestType.THIRD_PARTY,
|
||||
input,
|
||||
{ allowUnknownThirdPartyPolicyholder: true },
|
||||
);
|
||||
it("strips the removed unknown field from historical participant output", () => {
|
||||
expect(
|
||||
participantForRole(
|
||||
resolved,
|
||||
InquiryParticipantRole.THIRD_PARTY_POLICYHOLDER,
|
||||
),
|
||||
).toMatchObject({ unknown: true });
|
||||
sanitizeStoredInquiryParticipants([
|
||||
{
|
||||
participantId: "THIRD_PARTY_POLICYHOLDER",
|
||||
nationalCode: "0012345678",
|
||||
unknown: true,
|
||||
},
|
||||
]),
|
||||
).toEqual([
|
||||
{
|
||||
participantId: "THIRD_PARTY_POLICYHOLDER",
|
||||
nationalCode: "0012345678",
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -18,11 +18,6 @@ export interface ResolvedInquiryParticipant {
|
||||
hasDrivingLicense?: boolean;
|
||||
licenseNumber?: string;
|
||||
licenseType?: string;
|
||||
unknown?: boolean;
|
||||
}
|
||||
|
||||
export interface InquiryParticipantResolutionOptions {
|
||||
allowUnknownThirdPartyPolicyholder?: boolean;
|
||||
}
|
||||
|
||||
export interface InquiryParticipantRoleAssignments {
|
||||
@@ -96,6 +91,22 @@ export type ResolvedInquiryVehicle = InquiryVehicleInputDto & {
|
||||
registrationState: VehicleRegistrationState;
|
||||
};
|
||||
|
||||
function assertCompleteInquiryPlate(
|
||||
plate: InquiryVehicleInputDto["currentPlate"],
|
||||
path: "vehicle.currentPlate" | "vehicle.previousPlate",
|
||||
): void {
|
||||
for (const field of [
|
||||
"leftDigits",
|
||||
"centerAlphabet",
|
||||
"centerDigits",
|
||||
"ir",
|
||||
] as const) {
|
||||
if (plate?.[field] == null || String(plate[field]).trim() === "") {
|
||||
throw new BadRequestException(`${path}.${field} is required.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function participantForRole(
|
||||
resolved: ResolvedInquiryParticipants,
|
||||
role: InquiryParticipantRole,
|
||||
@@ -127,6 +138,23 @@ export function participantForStoredPartyRole(
|
||||
);
|
||||
}
|
||||
|
||||
/** Remove fields retired from the participant contract from historical records. */
|
||||
export function sanitizeStoredInquiryParticipants(
|
||||
participants: unknown,
|
||||
): Array<Record<string, any>> | undefined {
|
||||
if (!Array.isArray(participants)) return undefined;
|
||||
return participants.map((participant) => {
|
||||
const plain =
|
||||
participant &&
|
||||
typeof participant === "object" &&
|
||||
typeof (participant as { toObject?: unknown }).toObject === "function"
|
||||
? (participant as { toObject: () => Record<string, any> }).toObject()
|
||||
: { ...(participant as Record<string, any>) };
|
||||
delete plain.unknown;
|
||||
return plain;
|
||||
});
|
||||
}
|
||||
|
||||
const ROLE_FIELDS: Record<
|
||||
InquiryParticipantRole,
|
||||
keyof InquiryParticipantFieldsDto
|
||||
@@ -185,7 +213,6 @@ function requiredIdentity(
|
||||
export function resolveInquiryParticipants(
|
||||
caseType: BlameRequestType,
|
||||
input: Partial<InquiryParticipantFieldsDto> & Record<string, any>,
|
||||
options: InquiryParticipantResolutionOptions = {},
|
||||
): ResolvedInquiryParticipants {
|
||||
assertStructuredInquiryInput(input);
|
||||
const hasRoleCompleteInput = Object.values(ROLE_FIELDS).some(
|
||||
@@ -232,32 +259,7 @@ export function resolveInquiryParticipants(
|
||||
|
||||
resolving.add(role);
|
||||
let participantId: string;
|
||||
if (
|
||||
role === InquiryParticipantRole.THIRD_PARTY_POLICYHOLDER &&
|
||||
value.unknown === true
|
||||
) {
|
||||
if (!options.allowUnknownThirdPartyPolicyholder) {
|
||||
throw new BadRequestException(
|
||||
"An unknown third-party policyholder is allowed only for the damaged party.",
|
||||
);
|
||||
}
|
||||
const hasIdentityFields = Object.entries(value).some(
|
||||
([field, fieldValue]) => field !== "unknown" && fieldValue != null,
|
||||
);
|
||||
if (hasIdentityFields) {
|
||||
throw new BadRequestException(
|
||||
`${role} must contain either unknown=true or person fields, not both.`,
|
||||
);
|
||||
}
|
||||
const participant: ResolvedInquiryParticipant = {
|
||||
participantId: role,
|
||||
nationalCode: "",
|
||||
birthday: "",
|
||||
unknown: true,
|
||||
};
|
||||
participants.set(participant.participantId, participant);
|
||||
participantId = participant.participantId;
|
||||
} else if (value.unknown != null) {
|
||||
if (Object.prototype.hasOwnProperty.call(value, "unknown")) {
|
||||
throw new BadRequestException(
|
||||
`${role} does not support the unknown option.`,
|
||||
);
|
||||
@@ -314,28 +316,59 @@ export function resolveInquiryParticipants(
|
||||
export function resolveInquiryVehicle(
|
||||
input: InquiryVehicleInputDto,
|
||||
): ResolvedInquiryVehicle {
|
||||
if (!input) {
|
||||
throw new BadRequestException("vehicle is required.");
|
||||
}
|
||||
const registrationState =
|
||||
input.registrationState ?? VehicleRegistrationState.CURRENT;
|
||||
if (!Object.values(VehicleRegistrationState).includes(registrationState)) {
|
||||
throw new BadRequestException(
|
||||
"vehicle.registrationState must be CURRENT or RECENTLY_TRANSFERRED.",
|
||||
);
|
||||
}
|
||||
if (input.isNewCar != null && typeof input.isNewCar !== "boolean") {
|
||||
throw new BadRequestException("vehicle.isNewCar must be a boolean.");
|
||||
}
|
||||
const previousPolicyholderNationalCode = String(
|
||||
input.previousPolicyholderNationalCode ?? "",
|
||||
).trim();
|
||||
if (!input.currentPlate) {
|
||||
throw new BadRequestException("vehicle.currentPlate is required.");
|
||||
}
|
||||
assertCompleteInquiryPlate(input.currentPlate, "vehicle.currentPlate");
|
||||
const vin = String(input.vin ?? "").trim();
|
||||
if (vin && vin.length !== 17) {
|
||||
throw new BadRequestException(
|
||||
"vehicle.vin must contain exactly 17 characters.",
|
||||
);
|
||||
}
|
||||
if (
|
||||
registrationState === VehicleRegistrationState.RECENTLY_TRANSFERRED &&
|
||||
(!input.previousPlate || !String(input.vin ?? "").trim())
|
||||
(!input.previousPlate || !vin || !previousPolicyholderNationalCode)
|
||||
) {
|
||||
throw new BadRequestException(
|
||||
"RECENTLY_TRANSFERRED requires previousPlate and vin.",
|
||||
"RECENTLY_TRANSFERRED requires previousPlate, vin, and previousPolicyholderNationalCode.",
|
||||
);
|
||||
}
|
||||
if (
|
||||
registrationState !== VehicleRegistrationState.RECENTLY_TRANSFERRED &&
|
||||
input.previousPlate
|
||||
(input.previousPlate || input.previousPolicyholderNationalCode != null)
|
||||
) {
|
||||
throw new BadRequestException(
|
||||
"previousPlate is only allowed for RECENTLY_TRANSFERRED vehicles.",
|
||||
"previousPlate and previousPolicyholderNationalCode are only allowed for RECENTLY_TRANSFERRED vehicles.",
|
||||
);
|
||||
}
|
||||
return { ...input, registrationState };
|
||||
if (input.previousPlate) {
|
||||
assertCompleteInquiryPlate(input.previousPlate, "vehicle.previousPlate");
|
||||
}
|
||||
return {
|
||||
...input,
|
||||
registrationState,
|
||||
...(vin ? { vin } : {}),
|
||||
...(previousPolicyholderNationalCode
|
||||
? { previousPolicyholderNationalCode }
|
||||
: {}),
|
||||
};
|
||||
}
|
||||
|
||||
export function vehiclePlateCandidates(input?: ResolvedInquiryVehicle): Array<{
|
||||
@@ -459,7 +492,10 @@ export function isPolicyNotFoundError(error: unknown): boolean {
|
||||
export async function runPlateInquiryWithFallback<T>(options: {
|
||||
vehicle?: ResolvedInquiryVehicle;
|
||||
fallbackCurrentPlate: InquiryVehicleInputDto["currentPlate"];
|
||||
query: (plate: InquiryVehicleInputDto["currentPlate"]) => Promise<T>;
|
||||
query: (
|
||||
plate: InquiryVehicleInputDto["currentPlate"],
|
||||
plateKind: "CURRENT" | "PREVIOUS",
|
||||
) => Promise<T>;
|
||||
isUsable: (value: T) => boolean;
|
||||
mappedValue: (value: T) => Record<string, any>;
|
||||
shouldFallbackOnError?: (error: unknown) => boolean;
|
||||
@@ -492,7 +528,7 @@ export async function runPlateInquiryWithFallback<T>(options: {
|
||||
const candidate = candidates[index];
|
||||
const isLast = index === candidates.length - 1;
|
||||
try {
|
||||
const value = await options.query(candidate.plate);
|
||||
const value = await options.query(candidate.plate, candidate.kind);
|
||||
const usable = options.isUsable(value);
|
||||
if (!usable) {
|
||||
attempts.push({
|
||||
@@ -562,13 +598,8 @@ export async function runPlateInquiryWithFallback<T>(options: {
|
||||
export function normalizeInquirySubmission<T extends Record<string, any>>(
|
||||
caseType: BlameRequestType,
|
||||
input: T,
|
||||
options: InquiryParticipantResolutionOptions = {},
|
||||
): NormalizedInquirySubmission<T> {
|
||||
const participants = resolveInquiryParticipants(
|
||||
caseType,
|
||||
input as any,
|
||||
options,
|
||||
);
|
||||
const participants = resolveInquiryParticipants(caseType, input as any);
|
||||
const driver = participantForRole(
|
||||
participants,
|
||||
InquiryParticipantRole.DRIVER,
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import { RequestManagementService } from "./request-management.service";
|
||||
|
||||
describe("damaged-party inquiry requirements", () => {
|
||||
const getService = () => {
|
||||
const service = Object.create(
|
||||
RequestManagementService.prototype,
|
||||
) as RequestManagementService;
|
||||
(service as any).sandHubService = {
|
||||
getShebaValidation: jest.fn().mockResolvedValue({ valid: true }),
|
||||
};
|
||||
return service;
|
||||
};
|
||||
|
||||
it("requires Sheba when the claimant/damaged-party validation runs", async () => {
|
||||
const service = getService();
|
||||
|
||||
await expect(
|
||||
(service as any).validateShebaV3(
|
||||
undefined,
|
||||
"0012345678",
|
||||
"client-id",
|
||||
),
|
||||
).rejects.toThrow("sheba is required for the damaged party.");
|
||||
expect(
|
||||
(service as any).sandHubService.getShebaValidation,
|
||||
).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("validates damaged-party Sheba against the vehicle owner's national code", async () => {
|
||||
const service = getService();
|
||||
|
||||
await (service as any).validateShebaV3(
|
||||
"IR123456789012345678901234",
|
||||
"0012345678",
|
||||
"client-id",
|
||||
);
|
||||
|
||||
expect(
|
||||
(service as any).sandHubService.getShebaValidation,
|
||||
).toHaveBeenCalledWith("0012345678", "IR123456789012345678901234", {
|
||||
clientId: "client-id",
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,130 @@
|
||||
import { BlameRequestType } from "src/Types&Enums/blame-request-management/blameRequestType.enum";
|
||||
import { RequestManagementService } from "./request-management.service";
|
||||
|
||||
describe("RequestManagementService previous policyholder routing", () => {
|
||||
const currentPlate = {
|
||||
leftDigits: "44",
|
||||
centerAlphabet: "ب",
|
||||
centerDigits: "111",
|
||||
ir: "22",
|
||||
};
|
||||
const previousPlate = {
|
||||
leftDigits: "55",
|
||||
centerAlphabet: "ج",
|
||||
centerDigits: "222",
|
||||
ir: "33",
|
||||
};
|
||||
const vehicle = {
|
||||
registrationState: "RECENTLY_TRANSFERRED",
|
||||
currentPlate,
|
||||
previousPlate,
|
||||
previousPolicyholderNationalCode: "0098765432",
|
||||
vin: "NAAM01E15HK123456",
|
||||
};
|
||||
|
||||
function participantInput(caseType: BlameRequestType) {
|
||||
return {
|
||||
driver: {
|
||||
nationalCode: "0011111111",
|
||||
birthday: "1370/01/01",
|
||||
hasDrivingLicense: false,
|
||||
},
|
||||
vehicleOwner: { sameAs: "DRIVER" },
|
||||
thirdPartyPolicyholder: {
|
||||
nationalCode: "0022222222",
|
||||
birthday: "1360/02/02",
|
||||
},
|
||||
...(caseType === BlameRequestType.CAR_BODY
|
||||
? {
|
||||
carBodyPolicyholder: {
|
||||
nationalCode: "0033333333",
|
||||
birthday: "1350/03/03",
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
vehicle,
|
||||
};
|
||||
}
|
||||
|
||||
it("uses the previous code only for the previous-plate third-party lookup", async () => {
|
||||
const service = Object.create(RequestManagementService.prototype) as any;
|
||||
service.sandHubService = {
|
||||
getTejaratBlockInquiry: jest
|
||||
.fn()
|
||||
.mockResolvedValueOnce({ raw: {}, mapped: {} })
|
||||
.mockResolvedValueOnce({
|
||||
raw: {},
|
||||
mapped: {
|
||||
CompanyName: "پارسیان",
|
||||
VinNumberField: vehicle.vin,
|
||||
},
|
||||
}),
|
||||
};
|
||||
const submission = service.normalizeInquiryInput(
|
||||
BlameRequestType.THIRD_PARTY,
|
||||
participantInput(BlameRequestType.THIRD_PARTY),
|
||||
);
|
||||
|
||||
const result = await service.getThirdPartyPlateInquiry(submission);
|
||||
|
||||
expect(result.plateKind).toBe("PREVIOUS");
|
||||
expect(
|
||||
service.sandHubService.getTejaratBlockInquiry,
|
||||
).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
expect.objectContaining({
|
||||
plate: currentPlate,
|
||||
nationalCodeOfInsurer: "0022222222",
|
||||
}),
|
||||
undefined,
|
||||
);
|
||||
expect(
|
||||
service.sandHubService.getTejaratBlockInquiry,
|
||||
).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
expect.objectContaining({
|
||||
plate: previousPlate,
|
||||
nationalCodeOfInsurer: "0098765432",
|
||||
}),
|
||||
undefined,
|
||||
);
|
||||
});
|
||||
|
||||
it("uses the previous code only for the previous-plate car-body lookup", async () => {
|
||||
const service = Object.create(RequestManagementService.prototype) as any;
|
||||
service.sandHubService = {
|
||||
getCarBodyInquiry: jest
|
||||
.fn()
|
||||
.mockResolvedValueOnce({ raw: {}, mapped: {} })
|
||||
.mockResolvedValueOnce({
|
||||
raw: {},
|
||||
mapped: {
|
||||
policyNumber: "BODY-1",
|
||||
VinNumberField: vehicle.vin,
|
||||
},
|
||||
}),
|
||||
};
|
||||
const submission = service.normalizeInquiryInput(
|
||||
BlameRequestType.CAR_BODY,
|
||||
participantInput(BlameRequestType.CAR_BODY),
|
||||
);
|
||||
|
||||
const result = await service.getCarBodyPlateInquiry(submission);
|
||||
|
||||
expect(result.plateKind).toBe("PREVIOUS");
|
||||
expect(service.sandHubService.getCarBodyInquiry).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
expect.objectContaining({
|
||||
plate: currentPlate,
|
||||
nationalCodeOfInsurer: "0033333333",
|
||||
}),
|
||||
);
|
||||
expect(service.sandHubService.getCarBodyInquiry).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
expect.objectContaining({
|
||||
plate: previousPlate,
|
||||
nationalCodeOfInsurer: "0098765432",
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -130,6 +130,7 @@ import {
|
||||
normalizeInquirySubmission,
|
||||
resolveInquirySubjects,
|
||||
runPlateInquiryWithFallback,
|
||||
sanitizeStoredInquiryParticipants,
|
||||
} from "./inquiry-participant-resolver";
|
||||
|
||||
/**
|
||||
@@ -311,12 +312,8 @@ export class RequestManagementService {
|
||||
private normalizeInquiryInput<T extends Record<string, any>>(
|
||||
type: BlameRequestType,
|
||||
input: T,
|
||||
partyRole?: PartyRole,
|
||||
): NormalizedInquirySubmission<T> {
|
||||
return normalizeInquirySubmission(type, input, {
|
||||
allowUnknownThirdPartyPolicyholder:
|
||||
type === BlameRequestType.THIRD_PARTY && partyRole === PartyRole.SECOND,
|
||||
});
|
||||
return normalizeInquirySubmission(type, input);
|
||||
}
|
||||
|
||||
private applyInquiryParticipantsToParty(
|
||||
@@ -334,6 +331,8 @@ export class RequestManagementService {
|
||||
party.vehicle.previousPlateId = submission.vehicle.previousPlate
|
||||
? this.plateToPlateIdString(submission.vehicle.previousPlate)
|
||||
: undefined;
|
||||
party.vehicle.previousPolicyholderNationalCode =
|
||||
submission.vehicle.previousPolicyholderNationalCode;
|
||||
if (submission.vehicle.vin) party.vehicle.vin = submission.vehicle.vin;
|
||||
}
|
||||
}
|
||||
@@ -354,24 +353,18 @@ 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 subjects = resolveInquirySubjects(submission);
|
||||
const result = await runPlateInquiryWithFallback({
|
||||
vehicle: submission.vehicle,
|
||||
fallbackCurrentPlate: submission.dto.plate,
|
||||
query: (plate) =>
|
||||
query: (plate, plateKind) =>
|
||||
this.sandHubService.getTejaratBlockInquiry(
|
||||
{
|
||||
plate: plate as any,
|
||||
nationalCodeOfInsurer: subjects.thirdPartyPolicyNationalCode,
|
||||
nationalCodeOfInsurer:
|
||||
plateKind === "PREVIOUS"
|
||||
? submission.vehicle!.previousPolicyholderNationalCode!
|
||||
: subjects.thirdPartyPolicyNationalCode,
|
||||
},
|
||||
options,
|
||||
),
|
||||
@@ -401,9 +394,12 @@ export class RequestManagementService {
|
||||
const result = await runPlateInquiryWithFallback({
|
||||
vehicle: submission.vehicle,
|
||||
fallbackCurrentPlate: submission.dto.plate,
|
||||
query: (plate) =>
|
||||
query: (plate, plateKind) =>
|
||||
this.sandHubService.getCarBodyInquiry({
|
||||
nationalCodeOfInsurer: policyholderNationalCode,
|
||||
nationalCodeOfInsurer:
|
||||
plateKind === "PREVIOUS"
|
||||
? submission.vehicle!.previousPolicyholderNationalCode!
|
||||
: policyholderNationalCode,
|
||||
plate: plate as any,
|
||||
}),
|
||||
isUsable: (value) =>
|
||||
@@ -435,9 +431,6 @@ export class RequestManagementService {
|
||||
"vehicle.vin is required for a VIN/chassis inquiry.",
|
||||
);
|
||||
}
|
||||
if (submission.thirdPartyPolicyholder.unknown) {
|
||||
return { raw: null, mapped: {}, skipped: true };
|
||||
}
|
||||
const subjects = resolveInquirySubjects(submission);
|
||||
const result = await this.sandHubService.getPolicyByChassisInquiry(
|
||||
{
|
||||
@@ -446,7 +439,7 @@ export class RequestManagementService {
|
||||
},
|
||||
options,
|
||||
);
|
||||
return { ...result, skipped: false };
|
||||
return result;
|
||||
}
|
||||
|
||||
private async runParticipantPersonalInquiries(
|
||||
@@ -457,9 +450,7 @@ export class RequestManagementService {
|
||||
): Promise<void> {
|
||||
const participants = submission.participants.legacy
|
||||
? [submission.thirdPartyPolicyholder]
|
||||
: submission.participants.participants.filter(
|
||||
(participant) => !participant.unknown,
|
||||
);
|
||||
: submission.participants.participants;
|
||||
const results: Record<string, unknown> = {};
|
||||
|
||||
for (const participant of participants) {
|
||||
@@ -598,7 +589,6 @@ export class RequestManagementService {
|
||||
|
||||
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,
|
||||
@@ -664,6 +654,8 @@ export class RequestManagementService {
|
||||
[`${prefix}.previousPlateId`]: submission.vehicle?.previousPlate
|
||||
? this.plateToPlateIdString(submission.vehicle.previousPlate)
|
||||
: undefined,
|
||||
[`${prefix}.previousPolicyholderNationalCode`]:
|
||||
submission.vehicle?.previousPolicyholderNationalCode,
|
||||
[`${prefix}.vehicleVin`]: submission.vehicle?.vin,
|
||||
[`${prefix}.policyInquiry`]: audit,
|
||||
},
|
||||
@@ -1732,12 +1724,9 @@ export class RequestManagementService {
|
||||
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) {
|
||||
@@ -1801,19 +1790,13 @@ export class RequestManagementService {
|
||||
req,
|
||||
"thirdParty",
|
||||
role,
|
||||
!inquiry.skipped,
|
||||
true,
|
||||
{
|
||||
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) {
|
||||
@@ -1882,7 +1865,7 @@ export class RequestManagementService {
|
||||
|
||||
// Find client by company code
|
||||
const clientName = inquiryMapped?.CompanyName;
|
||||
if (!clientName && !policyholderUnknown) {
|
||||
if (!clientName) {
|
||||
const error = new BadRequestException(
|
||||
`CompanyName missing from inquiry response`,
|
||||
);
|
||||
@@ -2183,13 +2166,10 @@ export class RequestManagementService {
|
||||
const inquirySubmission = this.normalizeInquiryInput(
|
||||
req.type,
|
||||
body,
|
||||
role,
|
||||
);
|
||||
body = inquirySubmission.dto as unknown as InitialFormVinDto;
|
||||
const subjects = resolveInquirySubjects(inquirySubmission);
|
||||
this.applyInquiryParticipantsToParty(party, inquirySubmission);
|
||||
const policyholderUnknown =
|
||||
inquirySubmission.thirdPartyPolicyholder.unknown === true;
|
||||
|
||||
// Validation: driver/insurer sameness rules (identical to plate path)
|
||||
if (body.driverIsInsurer === false) {
|
||||
@@ -2246,17 +2226,11 @@ export class RequestManagementService {
|
||||
req,
|
||||
"thirdParty",
|
||||
role,
|
||||
!inquiry.skipped,
|
||||
true,
|
||||
{
|
||||
source: "ESG_VIN_INQUIRY",
|
||||
raw: inquiryRaw,
|
||||
mapped: inquiryMapped,
|
||||
...(inquiry.skipped
|
||||
? {
|
||||
skipped: true,
|
||||
reason: "Third-party policyholder is unknown",
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
);
|
||||
} catch (err: any) {
|
||||
@@ -2319,7 +2293,7 @@ export class RequestManagementService {
|
||||
|
||||
// Resolve insurer client from inquiry response
|
||||
const clientName = inquiryMapped?.CompanyName;
|
||||
if (!clientName && !policyholderUnknown) {
|
||||
if (!clientName) {
|
||||
const error = new BadRequestException(
|
||||
"CompanyName missing from VIN inquiry response",
|
||||
);
|
||||
@@ -3482,8 +3456,6 @@ export class RequestManagementService {
|
||||
const clientName = sandHubReport?.CompanyName;
|
||||
const companyCode = sandHubReport?.CompanyCode;
|
||||
|
||||
const policyholderUnknown =
|
||||
inquirySubmission?.thirdPartyPolicyholder.unknown === true;
|
||||
const client = clientName
|
||||
? await this.clientService.findOrCreateClientByCompanyCode(
|
||||
companyCode,
|
||||
@@ -3491,7 +3463,7 @@ export class RequestManagementService {
|
||||
)
|
||||
: null;
|
||||
|
||||
if (!client && !policyholderUnknown) {
|
||||
if (!client) {
|
||||
throw new HttpException("Client not found", HttpStatus.CONFLICT);
|
||||
}
|
||||
|
||||
@@ -3529,9 +3501,7 @@ export class RequestManagementService {
|
||||
plate: body.plate,
|
||||
nationalCode: body.nationalCodeOfInsurer,
|
||||
};
|
||||
const sandHubDoc = policyInquiry?.skipped
|
||||
? null
|
||||
: await this.sandHubService.sandHubDocumentCreator(
|
||||
const sandHubDoc = await this.sandHubService.sandHubDocumentCreator(
|
||||
user.sub,
|
||||
request["_doc"]._id,
|
||||
sandHubDocData,
|
||||
@@ -3592,6 +3562,8 @@ export class RequestManagementService {
|
||||
?.previousPlate
|
||||
? this.plateToPlateIdString(inquirySubmission.vehicle.previousPlate)
|
||||
: undefined;
|
||||
setFields[`${partyDetails}.previousPolicyholderNationalCode`] =
|
||||
inquirySubmission.vehicle?.previousPolicyholderNationalCode;
|
||||
setFields[`${partyDetails}.vehicleVin`] =
|
||||
inquirySubmission.vehicle?.vin;
|
||||
}
|
||||
@@ -3704,7 +3676,6 @@ export class RequestManagementService {
|
||||
const inquirySubmission = this.normalizeInquiryInput(
|
||||
request.type as BlameRequestType,
|
||||
body,
|
||||
partyRole,
|
||||
);
|
||||
body = inquirySubmission.dto as unknown as AddPlateDto;
|
||||
|
||||
@@ -6641,7 +6612,6 @@ export class RequestManagementService {
|
||||
participantId: participant.participantId,
|
||||
fullName: participant.fullName,
|
||||
hasDrivingLicense: participant.hasDrivingLicense,
|
||||
unknown: participant.unknown,
|
||||
}))
|
||||
: undefined;
|
||||
const evidenceRaw = party.evidence as Record<string, unknown> | undefined;
|
||||
@@ -6951,7 +6921,6 @@ export class RequestManagementService {
|
||||
const inquirySubmission = this.normalizeInquiryInput(
|
||||
req.type,
|
||||
formData.firstPartyPlate,
|
||||
PartyRole.FIRST,
|
||||
);
|
||||
this.recordInquiryParticipantContext(
|
||||
req,
|
||||
@@ -7139,6 +7108,8 @@ export class RequestManagementService {
|
||||
previousPlateId: inquirySubmission.vehicle?.previousPlate
|
||||
? this.plateToPlateIdString(inquirySubmission.vehicle.previousPlate)
|
||||
: undefined,
|
||||
previousPolicyholderNationalCode:
|
||||
inquirySubmission.vehicle?.previousPolicyholderNationalCode,
|
||||
vin: inquirySubmission.vehicle?.vin,
|
||||
inquiry: {
|
||||
...sandHubReport,
|
||||
@@ -7279,7 +7250,6 @@ export class RequestManagementService {
|
||||
const inquirySubmission = this.normalizeInquiryInput(
|
||||
req.type,
|
||||
plateDto,
|
||||
role,
|
||||
);
|
||||
this.recordInquiryParticipantContext(req, role, inquirySubmission);
|
||||
const existingPartyIndex = this.getPartyIndex(req, role);
|
||||
@@ -7290,8 +7260,6 @@ export class RequestManagementService {
|
||||
);
|
||||
}
|
||||
plateDto = inquirySubmission.dto;
|
||||
const policyholderUnknown =
|
||||
inquirySubmission.thirdPartyPolicyholder.unknown === true;
|
||||
let inquiry: any;
|
||||
try {
|
||||
if (inquirySubmission.participants.legacy) {
|
||||
@@ -7332,19 +7300,13 @@ export class RequestManagementService {
|
||||
req,
|
||||
"thirdParty",
|
||||
role,
|
||||
!inquiry.skipped,
|
||||
true,
|
||||
{
|
||||
source: inquiry.offline?.source ?? "TEJARAT_BLOCK_INQUIRY",
|
||||
plateKind: inquiry.plateKind,
|
||||
attempts: inquiry.attempts,
|
||||
raw: inquiry.raw,
|
||||
mapped: inquiry.mapped,
|
||||
...(inquiry.skipped
|
||||
? {
|
||||
skipped: true,
|
||||
reason: "Third-party policyholder is unknown",
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
);
|
||||
const clientName =
|
||||
@@ -7358,7 +7320,7 @@ export class RequestManagementService {
|
||||
)
|
||||
: await this.clientService.findOne({ clientName })
|
||||
: null;
|
||||
if (!client && !policyholderUnknown) {
|
||||
if (!client) {
|
||||
const error = new NotFoundException(
|
||||
`Client not found for company: ${clientName}`,
|
||||
);
|
||||
@@ -7434,6 +7396,8 @@ export class RequestManagementService {
|
||||
previousPlateId: inquirySubmission.vehicle?.previousPlate
|
||||
? this.plateToPlateIdString(inquirySubmission.vehicle.previousPlate)
|
||||
: undefined,
|
||||
previousPolicyholderNationalCode:
|
||||
inquirySubmission.vehicle?.previousPolicyholderNationalCode,
|
||||
vin: inquirySubmission.vehicle?.vin,
|
||||
inquiry: sandHubReport,
|
||||
},
|
||||
@@ -8071,7 +8035,6 @@ export class RequestManagementService {
|
||||
const firstInquirySubmission = this.normalizeInquiryInput(
|
||||
BlameRequestType.THIRD_PARTY,
|
||||
formData.firstPartyPlate,
|
||||
PartyRole.FIRST,
|
||||
);
|
||||
const firstPartyPlate = firstInquirySubmission.dto;
|
||||
let firstPolicyInquiry: any;
|
||||
@@ -8179,6 +8142,8 @@ export class RequestManagementService {
|
||||
firstInquirySubmission.vehicle.previousPlate,
|
||||
)
|
||||
: undefined;
|
||||
firstPartyDetails.previousPolicyholderNationalCode =
|
||||
firstInquirySubmission.vehicle?.previousPolicyholderNationalCode;
|
||||
firstPartyDetails.vehicleVin = firstInquirySubmission.vehicle?.vin;
|
||||
}
|
||||
|
||||
@@ -8209,7 +8174,6 @@ export class RequestManagementService {
|
||||
const secondInquirySubmission = this.normalizeInquiryInput(
|
||||
BlameRequestType.THIRD_PARTY,
|
||||
formData.secondParty.plate,
|
||||
PartyRole.SECOND,
|
||||
);
|
||||
let secondPolicyInquiry: any;
|
||||
try {
|
||||
@@ -8243,8 +8207,6 @@ export class RequestManagementService {
|
||||
const companyCode = sandHubReport?.CompanyCode;
|
||||
|
||||
// Try to find client by company code first (more reliable)
|
||||
const policyholderUnknown =
|
||||
secondInquirySubmission.thirdPartyPolicyholder.unknown === true;
|
||||
const client = clientName
|
||||
? companyCode
|
||||
? await this.clientService.findOrCreateClientByCompanyCode(
|
||||
@@ -8254,7 +8216,7 @@ export class RequestManagementService {
|
||||
: await this.clientService.findOne({ clientName: clientName })
|
||||
: null;
|
||||
|
||||
if (!client && !policyholderUnknown) {
|
||||
if (!client) {
|
||||
const error = new NotFoundException(
|
||||
`Client not found for company: ${clientName}${companyCode ? ` (code: ${companyCode})` : ""}`,
|
||||
);
|
||||
@@ -8319,6 +8281,8 @@ export class RequestManagementService {
|
||||
secondInquirySubmission.vehicle.previousPlate,
|
||||
)
|
||||
: undefined;
|
||||
secondPartyDetails.previousPolicyholderNationalCode =
|
||||
secondInquirySubmission.vehicle?.previousPolicyholderNationalCode;
|
||||
secondPartyDetails.vehicleVin = secondInquirySubmission.vehicle?.vin;
|
||||
}
|
||||
|
||||
@@ -8554,7 +8518,6 @@ export class RequestManagementService {
|
||||
const firstInquirySubmission = this.normalizeInquiryInput(
|
||||
BlameRequestType.CAR_BODY,
|
||||
formData.firstPartyPlate,
|
||||
PartyRole.FIRST,
|
||||
);
|
||||
const firstPartyPlate = firstInquirySubmission.dto;
|
||||
let thirdPartyPolicyInquiry: any;
|
||||
@@ -8687,6 +8650,8 @@ export class RequestManagementService {
|
||||
firstInquirySubmission.vehicle.previousPlate,
|
||||
)
|
||||
: undefined;
|
||||
firstPartyDetails.previousPolicyholderNationalCode =
|
||||
firstInquirySubmission.vehicle?.previousPolicyholderNationalCode;
|
||||
firstPartyDetails.vehicleVin = firstInquirySubmission.vehicle?.vin;
|
||||
}
|
||||
firstPartyDetails.firstPartyClient.clientId =
|
||||
@@ -9805,13 +9770,10 @@ export class RequestManagementService {
|
||||
const inquirySubmission = this.normalizeInquiryInput(
|
||||
req.type,
|
||||
partyData,
|
||||
partyRole,
|
||||
);
|
||||
partyData = inquirySubmission.dto as RunInquiriesV3Dto;
|
||||
const subjects = resolveInquirySubjects(inquirySubmission);
|
||||
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) =>
|
||||
@@ -9838,19 +9800,13 @@ export class RequestManagementService {
|
||||
req,
|
||||
"thirdParty",
|
||||
partyRole,
|
||||
!inquiry.skipped,
|
||||
true,
|
||||
{
|
||||
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) {
|
||||
@@ -9900,7 +9856,7 @@ export class RequestManagementService {
|
||||
|
||||
const clientName = inquiryMapped?.CompanyName;
|
||||
const companyCode = inquiryMapped?.CompanyCode;
|
||||
if (!clientName && !policyholderUnknown) {
|
||||
if (!clientName) {
|
||||
const error = new BadRequestException(
|
||||
`CompanyName missing from ${roleLabel} party inquiry response`,
|
||||
);
|
||||
@@ -10162,10 +10118,14 @@ export class RequestManagementService {
|
||||
vehicleOwnerNationalCode: string,
|
||||
clientId?: string,
|
||||
): Promise<void> {
|
||||
if (!sheba) return;
|
||||
if (!String(sheba ?? "").trim()) {
|
||||
throw new BadRequestException(
|
||||
"sheba is required for the damaged party.",
|
||||
);
|
||||
}
|
||||
await this.sandHubService.getShebaValidation(
|
||||
vehicleOwnerNationalCode,
|
||||
sheba,
|
||||
sheba!.trim(),
|
||||
clientId ? { clientId } : undefined,
|
||||
);
|
||||
}
|
||||
@@ -10738,13 +10698,10 @@ export class RequestManagementService {
|
||||
const inquirySubmission = this.normalizeInquiryInput(
|
||||
req.type,
|
||||
partyData,
|
||||
partyRole,
|
||||
);
|
||||
partyData = inquirySubmission.dto as RunInquiriesVinV3Dto;
|
||||
const subjects = resolveInquirySubjects(inquirySubmission);
|
||||
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) =>
|
||||
@@ -10763,17 +10720,11 @@ export class RequestManagementService {
|
||||
req,
|
||||
"thirdParty",
|
||||
partyRole,
|
||||
!inquiry.skipped,
|
||||
true,
|
||||
{
|
||||
source: "ESG_VIN_INQUIRY",
|
||||
raw: inquiryRaw,
|
||||
mapped: inquiryMapped,
|
||||
...(inquiry.skipped
|
||||
? {
|
||||
skipped: true,
|
||||
reason: "Third-party policyholder is unknown",
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
);
|
||||
} catch (err: any) {
|
||||
@@ -10817,7 +10768,7 @@ export class RequestManagementService {
|
||||
|
||||
const clientName = inquiryMapped?.CompanyName;
|
||||
const companyCode = inquiryMapped?.CompanyCode;
|
||||
if (!clientName && !policyholderUnknown) {
|
||||
if (!clientName) {
|
||||
const error = new BadRequestException(
|
||||
`CompanyName missing from ${roleLabel} party VIN inquiry response`,
|
||||
);
|
||||
@@ -12320,12 +12271,16 @@ export class RequestManagementService {
|
||||
message:
|
||||
"استعلام طرف مقصر با موفقیت انجام شد. اکنون میتوانید لینک تقصیر را برای کاربر ارسال کنید.",
|
||||
guiltyParty: {
|
||||
participants: firstPartyAfter?.participants,
|
||||
participants: sanitizeStoredInquiryParticipants(
|
||||
firstPartyAfter?.participants,
|
||||
),
|
||||
participantRoles: firstPartyAfter?.participantRoles,
|
||||
vehicle: firstPartyAfter?.vehicle
|
||||
? {
|
||||
plateId: firstPartyAfter.vehicle.plateId,
|
||||
previousPlateId: firstPartyAfter.vehicle.previousPlateId,
|
||||
previousPolicyholderNationalCode:
|
||||
firstPartyAfter.vehicle.previousPolicyholderNationalCode,
|
||||
registrationState: firstPartyAfter.vehicle.registrationState,
|
||||
vin: firstPartyAfter.vehicle.vin,
|
||||
name: firstPartyAfter.vehicle.name,
|
||||
@@ -12435,13 +12390,17 @@ export class RequestManagementService {
|
||||
message:
|
||||
"استعلام VIN طرف مقصر با موفقیت انجام شد. اکنون میتوانید لینک تقصیر را برای کاربر ارسال کنید.",
|
||||
guiltyParty: {
|
||||
participants: firstPartyAfter?.participants,
|
||||
participants: sanitizeStoredInquiryParticipants(
|
||||
firstPartyAfter?.participants,
|
||||
),
|
||||
participantRoles: firstPartyAfter?.participantRoles,
|
||||
vehicle: firstPartyAfter?.vehicle
|
||||
? {
|
||||
vin: firstPartyAfter.vehicle.vin,
|
||||
plateId: firstPartyAfter.vehicle.plateId,
|
||||
previousPlateId: firstPartyAfter.vehicle.previousPlateId,
|
||||
previousPolicyholderNationalCode:
|
||||
firstPartyAfter.vehicle.previousPolicyholderNationalCode,
|
||||
registrationState: firstPartyAfter.vehicle.registrationState,
|
||||
name: firstPartyAfter.vehicle.name,
|
||||
type: firstPartyAfter.vehicle.type,
|
||||
@@ -12684,7 +12643,7 @@ export class RequestManagementService {
|
||||
skipInitialFormStep: (req as any).skipInitialFormStep,
|
||||
parties: ((req as any).parties ?? []).map((p: any) => ({
|
||||
role: p.role,
|
||||
participants: p.participants,
|
||||
participants: sanitizeStoredInquiryParticipants(p.participants),
|
||||
participantRoles: p.participantRoles,
|
||||
person: {
|
||||
phoneNumber: p.person?.phoneNumber,
|
||||
@@ -12709,6 +12668,8 @@ export class RequestManagementService {
|
||||
? {
|
||||
plateId: p.vehicle.plateId,
|
||||
previousPlateId: p.vehicle.previousPlateId,
|
||||
previousPolicyholderNationalCode:
|
||||
p.vehicle.previousPolicyholderNationalCode,
|
||||
registrationState: p.vehicle.registrationState,
|
||||
vin: p.vehicle.vin,
|
||||
name: p.vehicle.name,
|
||||
@@ -12844,7 +12805,7 @@ export class RequestManagementService {
|
||||
requiresFileMakerApproval: plain.requiresFileMakerApproval,
|
||||
parties: (plain.parties ?? []).map((p: any) => ({
|
||||
role: p.role,
|
||||
participants: p.participants,
|
||||
participants: sanitizeStoredInquiryParticipants(p.participants),
|
||||
participantRoles: p.participantRoles,
|
||||
person: {
|
||||
fullName: p.person?.fullName,
|
||||
@@ -12873,6 +12834,8 @@ export class RequestManagementService {
|
||||
? {
|
||||
plateId: p.vehicle.plateId,
|
||||
previousPlateId: p.vehicle.previousPlateId,
|
||||
previousPolicyholderNationalCode:
|
||||
p.vehicle.previousPolicyholderNationalCode,
|
||||
registrationState: p.vehicle.registrationState,
|
||||
vin: p.vehicle.vin,
|
||||
name: p.vehicle.name,
|
||||
@@ -13027,7 +12990,7 @@ export class RequestManagementService {
|
||||
: null,
|
||||
parties: (plain.parties ?? []).map((p: any) => ({
|
||||
role: p.role,
|
||||
participants: p.participants,
|
||||
participants: sanitizeStoredInquiryParticipants(p.participants),
|
||||
participantRoles: p.participantRoles,
|
||||
person: {
|
||||
fullName: p.person?.fullName,
|
||||
@@ -13056,6 +13019,8 @@ export class RequestManagementService {
|
||||
? {
|
||||
plateId: p.vehicle.plateId,
|
||||
previousPlateId: p.vehicle.previousPlateId,
|
||||
previousPolicyholderNationalCode:
|
||||
p.vehicle.previousPolicyholderNationalCode,
|
||||
registrationState: p.vehicle.registrationState,
|
||||
vin: p.vehicle.vin,
|
||||
name: p.vehicle.name,
|
||||
|
||||
Reference in New Issue
Block a user