forked from Yara724/api
Resolve driver, owner, and policyholder from participants when they differ, prefer real licence / driverLicenseDate over dummies, and fall back unknown /lookups/fanavaran/{name} to car/base-info/{name} (including vehicle-groups).
Co-authored-by: Cursor <cursoragent@cursor.com>
218 lines
7.1 KiB
TypeScript
218 lines
7.1 KiB
TypeScript
import { InquiryParticipantRole } from "src/common/dto/inquiry-participants.dto";
|
|
import { participantForStoredPartyRole } from "src/request-management/inquiry-participant-resolver";
|
|
import {
|
|
normalizeNationalCode,
|
|
parseJalaliDateParts,
|
|
} from "./fanavaran-driver-inquiry";
|
|
import type { FanavaranClaimProduct } from "./fanavaran-claim-product";
|
|
|
|
/** Fanavaran dummy licence issue date when the user did not supply one. */
|
|
export const FANAVARAN_DUMMY_LICENCE_ISSU_DATE = "1394/10/13";
|
|
|
|
export type FanavaranRoleIdentity = {
|
|
nationalCode: string | null;
|
|
birthday: string | number | null;
|
|
/** Digits-only licence number when known. */
|
|
licenseNo: string | null;
|
|
licenseType: string | null;
|
|
/**
|
|
* Jalali `YYYY/MM/DD` from `driverLicenseDate` when present.
|
|
* Not a persisted model field — read opportunistically from party/person/participant.
|
|
*/
|
|
licenseIssuDate: string | null;
|
|
};
|
|
|
|
type PartyLike = {
|
|
participants?: Array<Record<string, any>>;
|
|
participantRoles?: Record<string, string | undefined>;
|
|
person?: Record<string, any> | null;
|
|
};
|
|
|
|
function nonEmptyText(value: unknown): string | null {
|
|
if (value == null) return null;
|
|
const text = String(value).trim();
|
|
if (!text || text.toLowerCase() === "null" || text.toLowerCase() === "undefined") {
|
|
return null;
|
|
}
|
|
return text;
|
|
}
|
|
|
|
/** Digits-only licence number, or null when empty / non-digit. */
|
|
export function extractLicenceDigits(value: unknown): string | null {
|
|
const text = nonEmptyText(value);
|
|
if (!text) return null;
|
|
const digits = text.replace(/[^\d]/g, "");
|
|
return digits || null;
|
|
}
|
|
|
|
/**
|
|
* Normalize a stored `driverLicenseDate` (or similar) to Fanavaran Jalali `YYYY/MM/DD`.
|
|
* Returns null when missing / unparseable so callers can fall back to the dummy.
|
|
*/
|
|
export function formatFanavaranLicenceIssuDate(value: unknown): string | null {
|
|
const parsed = parseJalaliDateParts(value);
|
|
if (!parsed) return null;
|
|
const month = String(parsed.month).padStart(2, "0");
|
|
const day = String(parsed.day).padStart(2, "0");
|
|
return `${parsed.year}/${month}/${day}`;
|
|
}
|
|
|
|
function readDriverLicenseDate(
|
|
...sources: Array<Record<string, any> | null | undefined>
|
|
): string | null {
|
|
for (const source of sources) {
|
|
if (!source) continue;
|
|
const formatted = formatFanavaranLicenceIssuDate(
|
|
source.driverLicenseDate ?? source.licenseDate ?? source.licenceIssuDate,
|
|
);
|
|
if (formatted) return formatted;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function fromParticipant(
|
|
participant: Record<string, any> | undefined,
|
|
): FanavaranRoleIdentity {
|
|
if (!participant) {
|
|
return {
|
|
nationalCode: null,
|
|
birthday: null,
|
|
licenseNo: null,
|
|
licenseType: null,
|
|
licenseIssuDate: null,
|
|
};
|
|
}
|
|
return {
|
|
nationalCode: normalizeNationalCode(participant.nationalCode),
|
|
birthday: nonEmptyText(participant.birthday) ?? participant.birthday ?? null,
|
|
licenseNo: extractLicenceDigits(participant.licenseNumber),
|
|
licenseType: nonEmptyText(participant.licenseType),
|
|
licenseIssuDate: readDriverLicenseDate(participant),
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Damaged/guilty party DRIVER: participants first, then legacy person.* fields.
|
|
* Licence number prefers real user values; callers fall back to the Fanavaran dummy.
|
|
*/
|
|
export function resolveDriverFromParty(party: PartyLike | null | undefined): FanavaranRoleIdentity {
|
|
const fromRole = fromParticipant(
|
|
participantForStoredPartyRole(
|
|
party ?? {},
|
|
InquiryParticipantRole.DRIVER,
|
|
) as Record<string, any> | undefined,
|
|
);
|
|
const person = party?.person ?? undefined;
|
|
|
|
return {
|
|
nationalCode:
|
|
fromRole.nationalCode ??
|
|
normalizeNationalCode(person?.nationalCodeOfDriver) ??
|
|
normalizeNationalCode(person?.nationalCode),
|
|
birthday:
|
|
(fromRole.birthday != null && String(fromRole.birthday).trim() !== ""
|
|
? fromRole.birthday
|
|
: null) ??
|
|
nonEmptyText(person?.driverBirthday) ??
|
|
nonEmptyText(person?.birthday) ??
|
|
person?.driverBirthday ??
|
|
person?.birthday ??
|
|
null,
|
|
licenseNo:
|
|
fromRole.licenseNo ??
|
|
extractLicenceDigits(person?.driverLicense) ??
|
|
extractLicenceDigits(person?.insurerLicense),
|
|
licenseType: fromRole.licenseType ?? nonEmptyText(person?.licenseType),
|
|
licenseIssuDate:
|
|
fromRole.licenseIssuDate ?? readDriverLicenseDate(person),
|
|
};
|
|
}
|
|
|
|
/** Vehicle owner for OwnerId / DriverIsOwner / Sheba national-code routing. */
|
|
export function resolveOwnerFromParty(party: PartyLike | null | undefined): FanavaranRoleIdentity {
|
|
const fromRole = fromParticipant(
|
|
participantForStoredPartyRole(
|
|
party ?? {},
|
|
InquiryParticipantRole.VEHICLE_OWNER,
|
|
) as Record<string, any> | undefined,
|
|
);
|
|
if (fromRole.nationalCode || fromRole.birthday) {
|
|
return fromRole;
|
|
}
|
|
// Legacy parties had no dedicated owner fields.
|
|
return {
|
|
nationalCode: null,
|
|
birthday: null,
|
|
licenseNo: null,
|
|
licenseType: null,
|
|
licenseIssuDate: null,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Policyholder national code for GEN.03 PolicyId inquiry:
|
|
* THIRD_PARTY → third-party policyholder; CAR_BODY → car-body policyholder.
|
|
*/
|
|
export function resolvePolicyholderFromParty(
|
|
party: PartyLike | null | undefined,
|
|
product: FanavaranClaimProduct,
|
|
): FanavaranRoleIdentity {
|
|
const role =
|
|
product === "car-body"
|
|
? InquiryParticipantRole.CAR_BODY_POLICYHOLDER
|
|
: InquiryParticipantRole.THIRD_PARTY_POLICYHOLDER;
|
|
const fromRole = fromParticipant(
|
|
participantForStoredPartyRole(party ?? {}, role) as
|
|
| Record<string, any>
|
|
| undefined,
|
|
);
|
|
const person = party?.person ?? undefined;
|
|
|
|
return {
|
|
nationalCode:
|
|
fromRole.nationalCode ??
|
|
normalizeNationalCode(person?.nationalCodeOfInsurer) ??
|
|
normalizeNationalCode(person?.nationalCode),
|
|
birthday:
|
|
(fromRole.birthday != null && String(fromRole.birthday).trim() !== ""
|
|
? fromRole.birthday
|
|
: null) ??
|
|
nonEmptyText(person?.insurerBirthday) ??
|
|
person?.insurerBirthday ??
|
|
null,
|
|
licenseNo:
|
|
fromRole.licenseNo ?? extractLicenceDigits(person?.insurerLicense),
|
|
licenseType: fromRole.licenseType ?? null,
|
|
licenseIssuDate: fromRole.licenseIssuDate,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* GEN.12 `DriverIsOwner`: driver national code equals vehicle-owner national code.
|
|
* Falls back to legacy `person.driverIsInsurer` when owner role is absent.
|
|
*/
|
|
export function resolveDriverIsOwner(party: PartyLike | null | undefined): boolean {
|
|
const driver = resolveDriverFromParty(party);
|
|
const owner = resolveOwnerFromParty(party);
|
|
if (driver.nationalCode && owner.nationalCode) {
|
|
return driver.nationalCode === owner.nationalCode;
|
|
}
|
|
return party?.person?.driverIsInsurer === true;
|
|
}
|
|
|
|
/** Licence number for Fanavaran; never empty — uses `dummy` when user value is missing. */
|
|
export function resolveFanavaranLicenceNo(
|
|
identity: Pick<FanavaranRoleIdentity, "licenseNo">,
|
|
dummy: string,
|
|
): string {
|
|
return identity.licenseNo || dummy;
|
|
}
|
|
|
|
/** Licence issue date for Fanavaran; uses dummy when `driverLicenseDate` is absent. */
|
|
export function resolveFanavaranLicenceIssuDate(
|
|
identity: Pick<FanavaranRoleIdentity, "licenseIssuDate">,
|
|
dummy: string = FANAVARAN_DUMMY_LICENCE_ISSU_DATE,
|
|
): string {
|
|
return identity.licenseIssuDate || dummy;
|
|
}
|