forked from Yara724/api
Map Fanavaran people and licences by party role; allow unlisted base-info lookups.
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>
This commit is contained in:
217
src/claim-request-management/fanavaran-party-roles.ts
Normal file
217
src/claim-request-management/fanavaran-party-roles.ts
Normal file
@@ -0,0 +1,217 @@
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user