forked from Yara724/api
579 lines
19 KiB
TypeScript
579 lines
19 KiB
TypeScript
import { BadRequestException } from "@nestjs/common";
|
||
import { BlameRequestType } from "src/Types&Enums/blame-request-management/blameRequestType.enum";
|
||
import { jalaliToGregorianDate } from "src/helpers/date-jalali";
|
||
import { gregorianDateInIran } from "src/helpers/iran-datetime";
|
||
import {
|
||
InquiryParticipantFieldsDto,
|
||
InquiryParticipantInputDto,
|
||
InquiryParticipantRole,
|
||
InquiryVehicleInputDto,
|
||
VehicleRegistrationState,
|
||
} from "src/common/dto/inquiry-participants.dto";
|
||
|
||
export interface ResolvedInquiryParticipant {
|
||
participantId: string;
|
||
nationalCode: string;
|
||
birthday: string;
|
||
fullName?: string;
|
||
hasDrivingLicense?: boolean;
|
||
licenseNumber?: string;
|
||
licenseType?: string;
|
||
}
|
||
|
||
export interface InquiryParticipantRoleAssignments {
|
||
driver: string;
|
||
vehicleOwner?: string;
|
||
thirdPartyPolicyholder: string;
|
||
carBodyPolicyholder?: string;
|
||
}
|
||
|
||
export interface ResolvedInquiryParticipants {
|
||
participants: ResolvedInquiryParticipant[];
|
||
roles: InquiryParticipantRoleAssignments;
|
||
legacy: boolean;
|
||
}
|
||
|
||
export interface NormalizedInquirySubmission<T extends Record<string, any>> {
|
||
dto: T & {
|
||
nationalCodeOfDriver: string;
|
||
driverBirthday: string;
|
||
driverLicense?: string;
|
||
licenseType?: string;
|
||
userNoCertificate?: boolean;
|
||
nationalCodeOfInsurer: string;
|
||
insurerBirthday: string;
|
||
driverIsInsurer: boolean;
|
||
insurerLicense?: string;
|
||
};
|
||
participants: ResolvedInquiryParticipants;
|
||
driver: ResolvedInquiryParticipant;
|
||
vehicleOwner?: ResolvedInquiryParticipant;
|
||
thirdPartyPolicyholder: ResolvedInquiryParticipant;
|
||
carBodyPolicyholder?: ResolvedInquiryParticipant;
|
||
vehicle?: ResolvedInquiryVehicle;
|
||
}
|
||
|
||
export interface InquirySubjects {
|
||
thirdPartyPolicyNationalCode: string;
|
||
carBodyPolicyNationalCode?: string;
|
||
shebaNationalCode: string;
|
||
driverNationalCode: string;
|
||
}
|
||
|
||
const PARTICIPANT_ROLE_LABELS: Record<InquiryParticipantRole, string> = {
|
||
[InquiryParticipantRole.DRIVER]: "راننده",
|
||
[InquiryParticipantRole.VEHICLE_OWNER]: "مالک خودرو",
|
||
[InquiryParticipantRole.THIRD_PARTY_POLICYHOLDER]: "بیمهگذار شخص ثالث",
|
||
[InquiryParticipantRole.CAR_BODY_POLICYHOLDER]: "بیمهگذار بدنه",
|
||
};
|
||
|
||
const PLATE_FIELD_LABELS = {
|
||
leftDigits: "دو رقم سمت چپ پلاک",
|
||
centerAlphabet: "حرف پلاک",
|
||
centerDigits: "سه رقم میانی پلاک",
|
||
ir: "کد ایران پلاک",
|
||
} as const;
|
||
|
||
/**
|
||
* The single routing seam for external inquiries. Policy checks belong to
|
||
* their policyholder, Sheba belongs to the vehicle owner, and licence data
|
||
* belongs to the driver even when those roles resolve to different people.
|
||
*/
|
||
export function resolveInquirySubjects(
|
||
submission: NormalizedInquirySubmission<Record<string, any>>,
|
||
): InquirySubjects {
|
||
if (!submission.vehicleOwner) {
|
||
throw new BadRequestException(
|
||
"اطلاعات هویتی مالک خودرو برای استعلام شبا الزامی است.",
|
||
);
|
||
}
|
||
return {
|
||
thirdPartyPolicyNationalCode:
|
||
submission.thirdPartyPolicyholder.nationalCode,
|
||
...(submission.carBodyPolicyholder
|
||
? {
|
||
carBodyPolicyNationalCode:
|
||
submission.carBodyPolicyholder.nationalCode,
|
||
}
|
||
: {}),
|
||
shebaNationalCode: submission.vehicleOwner.nationalCode,
|
||
driverNationalCode: submission.driver.nationalCode,
|
||
};
|
||
}
|
||
|
||
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() === "") {
|
||
const plateLabel =
|
||
path === "vehicle.currentPlate" ? "پلاک فعلی" : "پلاک قبلی";
|
||
throw new BadRequestException(
|
||
`${PLATE_FIELD_LABELS[field]} در ${plateLabel} الزامی است.`,
|
||
);
|
||
}
|
||
}
|
||
}
|
||
|
||
export function participantForRole(
|
||
resolved: ResolvedInquiryParticipants,
|
||
role: InquiryParticipantRole,
|
||
): ResolvedInquiryParticipant | undefined {
|
||
const roleField = ROLE_FIELDS[
|
||
role
|
||
] as keyof InquiryParticipantRoleAssignments;
|
||
const participantId = resolved.roles[roleField];
|
||
return resolved.participants.find(
|
||
(participant) => participant.participantId === participantId,
|
||
);
|
||
}
|
||
|
||
/** Resolve a participant from the role links persisted on a blame party. */
|
||
export function participantForStoredPartyRole(
|
||
party: {
|
||
participants?: Array<Record<string, any>>;
|
||
participantRoles?: Partial<InquiryParticipantRoleAssignments>;
|
||
},
|
||
role: InquiryParticipantRole,
|
||
): Record<string, any> | undefined {
|
||
const roleField = ROLE_FIELDS[
|
||
role
|
||
] as keyof InquiryParticipantRoleAssignments;
|
||
const participantId = party.participantRoles?.[roleField];
|
||
if (!participantId || !Array.isArray(party.participants)) return undefined;
|
||
return party.participants.find(
|
||
(participant) => participant?.participantId === participantId,
|
||
);
|
||
}
|
||
|
||
/** 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
|
||
> = {
|
||
[InquiryParticipantRole.DRIVER]: "driver",
|
||
[InquiryParticipantRole.VEHICLE_OWNER]: "vehicleOwner",
|
||
[InquiryParticipantRole.THIRD_PARTY_POLICYHOLDER]: "thirdPartyPolicyholder",
|
||
[InquiryParticipantRole.CAR_BODY_POLICYHOLDER]: "carBodyPolicyholder",
|
||
};
|
||
|
||
function requiredIdentity(
|
||
role: InquiryParticipantRole,
|
||
input: InquiryParticipantInputDto,
|
||
): ResolvedInquiryParticipant {
|
||
if (Object.prototype.hasOwnProperty.call(input, "phoneNumber")) {
|
||
throw new BadRequestException(
|
||
`شماره همراه ${PARTICIPANT_ROLE_LABELS[role]} باید جدا از اطلاعات هویتی استعلام ارسال شود.`,
|
||
);
|
||
}
|
||
const nationalCode = String(input.nationalCode ?? "").trim();
|
||
const birthday = String(input.birthday ?? "").trim();
|
||
if (!nationalCode || !birthday) {
|
||
throw new BadRequestException(
|
||
`کد ملی و تاریخ تولد ${PARTICIPANT_ROLE_LABELS[role]} الزامی است.`,
|
||
);
|
||
}
|
||
if (
|
||
role === InquiryParticipantRole.DRIVER &&
|
||
typeof input.hasDrivingLicense !== "boolean"
|
||
) {
|
||
throw new BadRequestException("وضعیت داشتن گواهینامه راننده الزامی است.");
|
||
}
|
||
if (
|
||
role === InquiryParticipantRole.DRIVER &&
|
||
input.hasDrivingLicense === true &&
|
||
(!String(input.licenseNumber ?? "").trim() ||
|
||
!String(input.licenseType ?? "").trim())
|
||
) {
|
||
throw new BadRequestException(
|
||
"شماره و نوع گواهینامه برای راننده دارای گواهینامه الزامی است.",
|
||
);
|
||
}
|
||
return {
|
||
participantId: role,
|
||
nationalCode,
|
||
birthday,
|
||
...(input.fullName ? { fullName: input.fullName } : {}),
|
||
...(input.hasDrivingLicense != null
|
||
? { hasDrivingLicense: input.hasDrivingLicense }
|
||
: {}),
|
||
...(input.licenseNumber ? { licenseNumber: input.licenseNumber } : {}),
|
||
...(input.licenseType ? { licenseType: input.licenseType } : {}),
|
||
};
|
||
}
|
||
|
||
export function resolveInquiryParticipants(
|
||
caseType: BlameRequestType,
|
||
input: Partial<InquiryParticipantFieldsDto> & Record<string, any>,
|
||
): ResolvedInquiryParticipants {
|
||
assertStructuredInquiryInput(input);
|
||
const hasRoleCompleteInput = Object.values(ROLE_FIELDS).some(
|
||
(field) => input[field] != null,
|
||
);
|
||
if (!hasRoleCompleteInput) {
|
||
throw new BadRequestException(
|
||
"اطلاعات راننده، مالک خودرو و بیمهگذار شخص ثالث برای استعلام الزامی است.",
|
||
);
|
||
}
|
||
if (
|
||
caseType === BlameRequestType.THIRD_PARTY &&
|
||
input.carBodyPolicyholder != null
|
||
) {
|
||
throw new BadRequestException(
|
||
"بیمهگذار بدنه برای پرونده شخص ثالث قابل ثبت نیست.",
|
||
);
|
||
}
|
||
|
||
const requiredRoles = [
|
||
InquiryParticipantRole.DRIVER,
|
||
InquiryParticipantRole.VEHICLE_OWNER,
|
||
InquiryParticipantRole.THIRD_PARTY_POLICYHOLDER,
|
||
...(caseType === BlameRequestType.CAR_BODY
|
||
? [InquiryParticipantRole.CAR_BODY_POLICYHOLDER]
|
||
: []),
|
||
];
|
||
const participants = new Map<string, ResolvedInquiryParticipant>();
|
||
const resolvedRoleIds = new Map<InquiryParticipantRole, string>();
|
||
const resolving = new Set<InquiryParticipantRole>();
|
||
|
||
const resolveRole = (role: InquiryParticipantRole): string => {
|
||
const existing = resolvedRoleIds.get(role);
|
||
if (existing) return existing;
|
||
if (resolving.has(role)) {
|
||
throw new BadRequestException(
|
||
"ارتباط اشخاص یکسان در اطلاعات استعلام نامعتبر است.",
|
||
);
|
||
}
|
||
const value = input[ROLE_FIELDS[role]] as
|
||
| InquiryParticipantInputDto
|
||
| undefined;
|
||
if (!value) {
|
||
throw new BadRequestException(
|
||
`اطلاعات ${PARTICIPANT_ROLE_LABELS[role]} الزامی است.`,
|
||
);
|
||
}
|
||
|
||
resolving.add(role);
|
||
let participantId: string;
|
||
if (Object.prototype.hasOwnProperty.call(value, "unknown")) {
|
||
throw new BadRequestException(
|
||
`ثبت ${PARTICIPANT_ROLE_LABELS[role]} بهصورت نامشخص امکانپذیر نیست.`,
|
||
);
|
||
} else if (value.sameAs) {
|
||
const hasPersonSpecificFields = Object.entries(value).some(
|
||
([field, fieldValue]) => field !== "sameAs" && fieldValue != null,
|
||
);
|
||
if (hasPersonSpecificFields) {
|
||
throw new BadRequestException(
|
||
`برای ${PARTICIPANT_ROLE_LABELS[role]} باید فقط ارتباط با شخص دیگر یا اطلاعات هویتی مستقل ارسال شود.`,
|
||
);
|
||
}
|
||
participantId = resolveRole(value.sameAs);
|
||
} else {
|
||
const participant = requiredIdentity(role, value);
|
||
const duplicate = [...participants.values()].find(
|
||
(item) => item.nationalCode === participant.nationalCode,
|
||
);
|
||
if (duplicate) {
|
||
throw new BadRequestException(
|
||
`کد ملی ${PARTICIPANT_ROLE_LABELS[role]} تکراری است؛ ارتباط با شخص ثبتشده را انتخاب کنید.`,
|
||
);
|
||
}
|
||
participants.set(participant.participantId, participant);
|
||
participantId = participant.participantId;
|
||
}
|
||
resolving.delete(role);
|
||
resolvedRoleIds.set(role, participantId);
|
||
return participantId;
|
||
};
|
||
|
||
for (const role of requiredRoles) resolveRole(role);
|
||
|
||
return {
|
||
participants: [...participants.values()],
|
||
roles: {
|
||
driver: resolvedRoleIds.get(InquiryParticipantRole.DRIVER)!,
|
||
vehicleOwner: resolvedRoleIds.get(InquiryParticipantRole.VEHICLE_OWNER)!,
|
||
thirdPartyPolicyholder: resolvedRoleIds.get(
|
||
InquiryParticipantRole.THIRD_PARTY_POLICYHOLDER,
|
||
)!,
|
||
...(caseType === BlameRequestType.CAR_BODY
|
||
? {
|
||
carBodyPolicyholder: resolvedRoleIds.get(
|
||
InquiryParticipantRole.CAR_BODY_POLICYHOLDER,
|
||
)!,
|
||
}
|
||
: {}),
|
||
},
|
||
legacy: false,
|
||
};
|
||
}
|
||
|
||
export function resolveInquiryVehicle(
|
||
input: InquiryVehicleInputDto,
|
||
): ResolvedInquiryVehicle {
|
||
if (!input) {
|
||
throw new BadRequestException("اطلاعات خودرو برای استعلام الزامی است.");
|
||
}
|
||
const registrationState =
|
||
input.registrationState ?? VehicleRegistrationState.CURRENT;
|
||
if (!Object.values(VehicleRegistrationState).includes(registrationState)) {
|
||
throw new BadRequestException(
|
||
"وضعیت پلاک خودرو باید «فعلی» یا «تازه تعویضشده» باشد.",
|
||
);
|
||
}
|
||
if (input.isNewCar != null && typeof input.isNewCar !== "boolean") {
|
||
throw new BadRequestException("وضعیت صفر بودن خودرو نامعتبر است.");
|
||
}
|
||
const previousPolicyholderNationalCode = String(
|
||
input.previousPolicyholderNationalCode ?? "",
|
||
).trim();
|
||
if (!input.currentPlate) {
|
||
throw new BadRequestException("پلاک فعلی خودرو برای استعلام الزامی است.");
|
||
}
|
||
assertCompleteInquiryPlate(input.currentPlate, "vehicle.currentPlate");
|
||
const vin = String(input.vin ?? "").trim();
|
||
if (vin && vin.length !== 17) {
|
||
throw new BadRequestException(
|
||
"شماره شاسی (VIN) باید دقیقاً ۱۷ کاراکتر باشد.",
|
||
);
|
||
}
|
||
if (
|
||
registrationState === VehicleRegistrationState.RECENTLY_TRANSFERRED &&
|
||
(!input.previousPlate || !vin || !previousPolicyholderNationalCode)
|
||
) {
|
||
throw new BadRequestException(
|
||
"برای خودروی تازه تعویضپلاکشده، پلاک قبلی، شماره شاسی (VIN) و کد ملی بیمهگذار قبلی الزامی است.",
|
||
);
|
||
}
|
||
if (
|
||
registrationState !== VehicleRegistrationState.RECENTLY_TRANSFERRED &&
|
||
(input.previousPlate || input.previousPolicyholderNationalCode != null)
|
||
) {
|
||
throw new BadRequestException(
|
||
"پلاک و کد ملی بیمهگذار قبلی فقط برای خودروی تازه تعویضپلاکشده قابل ثبت است.",
|
||
);
|
||
}
|
||
if (input.previousPlate) {
|
||
assertCompleteInquiryPlate(input.previousPlate, "vehicle.previousPlate");
|
||
}
|
||
return {
|
||
...input,
|
||
registrationState,
|
||
...(vin ? { vin } : {}),
|
||
...(previousPolicyholderNationalCode
|
||
? { previousPolicyholderNationalCode }
|
||
: {}),
|
||
};
|
||
}
|
||
|
||
/** A dated result is usable only while the returned policy has not expired. */
|
||
export function isMappedPolicyCurrent(
|
||
mapped: Record<string, any>,
|
||
todayGregorian: string = gregorianDateInIran(new Date()),
|
||
): boolean {
|
||
const rawEndDate =
|
||
mapped?.EndDate ??
|
||
mapped?.HEndDte ??
|
||
mapped?.persianEndDate ??
|
||
mapped?.PolicyEndDate ??
|
||
mapped?.endDate;
|
||
if (rawEndDate == null || String(rawEndDate).trim() === "") return true;
|
||
const endDate = jalaliToGregorianDate(rawEndDate);
|
||
return endDate != null && endDate >= todayGregorian;
|
||
}
|
||
|
||
const LEGACY_INQUIRY_FIELDS = [
|
||
"nationalCodeOfDriver",
|
||
"driverBirthday",
|
||
"driverLicense",
|
||
"licenseType",
|
||
"nationalCodeOfInsurer",
|
||
"insurerBirthday",
|
||
"insurerLicense",
|
||
"driverIsInsurer",
|
||
"userNoCertificate",
|
||
"plate",
|
||
"plateId",
|
||
"vin",
|
||
"isNewCar",
|
||
] as const;
|
||
|
||
function assertStructuredInquiryInput(input: Record<string, any>): void {
|
||
const legacyFields = LEGACY_INQUIRY_FIELDS.filter((field) =>
|
||
Object.prototype.hasOwnProperty.call(input, field),
|
||
);
|
||
if (legacyFields.length > 0) {
|
||
throw new BadRequestException(
|
||
"ساختار قدیمی اطلاعات استعلام پذیرفته نمیشود؛ اطلاعات اشخاص و خودرو را در بخشهای جدید ارسال کنید.",
|
||
);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Run a policy inquiry only for the submitted current plate. Recent-transfer
|
||
* data is retained as case metadata, but must never trigger an inquiry for a
|
||
* previous plate or a previous policyholder.
|
||
*/
|
||
export async function runCurrentPlateInquiry<T>(options: {
|
||
currentPlate: InquiryVehicleInputDto["currentPlate"];
|
||
vin?: string;
|
||
query: (plate: InquiryVehicleInputDto["currentPlate"]) => Promise<T>;
|
||
isUsable: (value: T) => boolean;
|
||
errorMessage?: (value: T) => string | undefined;
|
||
}): Promise<{
|
||
value: T;
|
||
plateKind: "CURRENT";
|
||
attempts: Array<{
|
||
plateKind: "CURRENT";
|
||
plate: InquiryVehicleInputDto["currentPlate"];
|
||
vin?: string;
|
||
succeeded: boolean;
|
||
usable?: boolean;
|
||
error?: string;
|
||
}>;
|
||
}> {
|
||
const attempts: Array<{
|
||
plateKind: "CURRENT";
|
||
plate: InquiryVehicleInputDto["currentPlate"];
|
||
vin?: string;
|
||
succeeded: boolean;
|
||
usable?: boolean;
|
||
error?: string;
|
||
}> = [];
|
||
|
||
try {
|
||
const value = await options.query(options.currentPlate);
|
||
const usable = options.isUsable(value);
|
||
if (!usable) {
|
||
attempts.push({
|
||
plateKind: "CURRENT",
|
||
plate: options.currentPlate,
|
||
...(options.vin ? { vin: options.vin } : {}),
|
||
succeeded: true,
|
||
usable: false,
|
||
});
|
||
const error = new BadRequestException(
|
||
options.errorMessage?.(value) ||
|
||
"بیمهنامه معتبر و فعالی مطابق مشخصات خودرو و کد ملی واردشده یافت نشد.",
|
||
) as BadRequestException & { attempts?: typeof attempts };
|
||
error.attempts = attempts;
|
||
throw error;
|
||
}
|
||
attempts.push({
|
||
plateKind: "CURRENT",
|
||
plate: options.currentPlate,
|
||
...(options.vin ? { vin: options.vin } : {}),
|
||
succeeded: true,
|
||
usable: true,
|
||
});
|
||
return { value, plateKind: "CURRENT", attempts };
|
||
} catch (error) {
|
||
const alreadyRecorded =
|
||
typeof error === "object" &&
|
||
error != null &&
|
||
Array.isArray((error as { attempts?: unknown }).attempts);
|
||
if (!alreadyRecorded) {
|
||
attempts.push({
|
||
plateKind: "CURRENT",
|
||
plate: options.currentPlate,
|
||
...(options.vin ? { vin: options.vin } : {}),
|
||
succeeded: false,
|
||
error: error instanceof Error ? error.message : String(error),
|
||
});
|
||
}
|
||
if (typeof error === "object" && error != null) {
|
||
(error as { attempts?: typeof attempts }).attempts = attempts;
|
||
}
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
export function normalizeInquirySubmission<T extends Record<string, any>>(
|
||
caseType: BlameRequestType,
|
||
input: T,
|
||
): NormalizedInquirySubmission<T> {
|
||
const participants = resolveInquiryParticipants(caseType, input as any);
|
||
const driver = participantForRole(
|
||
participants,
|
||
InquiryParticipantRole.DRIVER,
|
||
);
|
||
const thirdPartyPolicyholder = participantForRole(
|
||
participants,
|
||
InquiryParticipantRole.THIRD_PARTY_POLICYHOLDER,
|
||
);
|
||
if (!driver || !thirdPartyPolicyholder) {
|
||
throw new BadRequestException(
|
||
"اطلاعات هویتی راننده و بیمهگذار شخص ثالث الزامی است.",
|
||
);
|
||
}
|
||
const vehicleOwner = participantForRole(
|
||
participants,
|
||
InquiryParticipantRole.VEHICLE_OWNER,
|
||
);
|
||
const carBodyPolicyholder = participantForRole(
|
||
participants,
|
||
InquiryParticipantRole.CAR_BODY_POLICYHOLDER,
|
||
);
|
||
const vehicle = resolveInquiryVehicle(
|
||
input.vehicle as InquiryVehicleInputDto,
|
||
);
|
||
const sameDriverAndPolicyholder =
|
||
participants.roles.driver === participants.roles.thirdPartyPolicyholder;
|
||
|
||
return {
|
||
dto: {
|
||
...input,
|
||
plate: vehicle.currentPlate,
|
||
vin: vehicle.vin,
|
||
nationalCodeOfDriver: driver.nationalCode,
|
||
driverBirthday: driver.birthday,
|
||
driverLicense: driver.licenseNumber,
|
||
licenseType: driver.licenseType,
|
||
userNoCertificate:
|
||
driver.hasDrivingLicense == null
|
||
? undefined
|
||
: !driver.hasDrivingLicense,
|
||
nationalCodeOfInsurer: thirdPartyPolicyholder.nationalCode,
|
||
insurerBirthday: thirdPartyPolicyholder.birthday,
|
||
driverIsInsurer: sameDriverAndPolicyholder,
|
||
insurerLicense: sameDriverAndPolicyholder
|
||
? driver.licenseNumber
|
||
: undefined,
|
||
isNewCar: vehicle.isNewCar,
|
||
} as NormalizedInquirySubmission<T>["dto"],
|
||
participants,
|
||
driver,
|
||
vehicleOwner,
|
||
thirdPartyPolicyholder,
|
||
carBodyPolicyholder,
|
||
vehicle,
|
||
};
|
||
}
|