Merge pull request 'fanavaran driver and owner id (GEN..44 defauts) - culprit type id dynamic' (#343) from s.hajizadeh/yara724api:main into main

Reviewed-on: Yara724/api#343
This commit is contained in:
2026-09-28 16:14:21 +03:30
7 changed files with 303 additions and 36 deletions

View File

@@ -272,6 +272,7 @@ import {
resolveFanavaranLicenceNo, resolveFanavaranLicenceNo,
resolveOwnerFromParty, resolveOwnerFromParty,
resolvePolicyholderFromParty, resolvePolicyholderFromParty,
resolveThirdPartyCulpritTypeId,
} from "./fanavaran-party-roles"; } from "./fanavaran-party-roles";
import { import {
buildFanavaranOtherPeoplePayload, buildFanavaranOtherPeoplePayload,
@@ -4294,10 +4295,10 @@ export class ClaimRequestManagementService {
} }
this.logger.warn( this.logger.warn(
`[resolveDriverFanavaranId] NO MATCH for nationalCode=${nationalCode} driverIsInsurer=${driverIsInsurer}. ` + `[resolveDriverFanavaranId] NO acceptable RoleId 161/166 for nationalCode=${nationalCode} driverIsInsurer=${driverIsInsurer}. ` +
`Available entries: ${asPartyInquiryRows(response) `Available entries: ${asPartyInquiryRows(response)
.map((e) => `{Id=${e.Id}, RoleId=${e.RoleId}, Name=${e.Name} ${e.LastName}}`) .map((e) => `{Id=${e.Id}, RoleId=${e.RoleId}, Name=${e.Name} ${e.LastName}}`)
.join("; ")}`, .join("; ")} — submit will register GEN.44 other-people`,
); );
return null; return null;
} catch (error) { } catch (error) {
@@ -4308,7 +4309,7 @@ export class ClaimRequestManagementService {
} }
} }
/** Best-effort OwnerId from parties inquiry (no GEN.44 create). */ /** Best-effort OwnerId from parties inquiry (RoleId 161/166 only; GEN.44 on submit). */
private async resolveOwnerFanavaranId( private async resolveOwnerFanavaranId(
clientKey: FanavaranClientKey, clientKey: FanavaranClientKey,
nationalCode: string | null | undefined, nationalCode: string | null | undefined,
@@ -4327,7 +4328,20 @@ export class ClaimRequestManagementService {
birthDay: parsed.day, birthDay: parsed.day,
}, },
); );
return selectFanavaranOwnerId(response); const ownerId = selectFanavaranOwnerId(response);
if (ownerId != null) {
this.logger.log(
`[resolveOwnerFanavaranId] MATCH: Id=${ownerId} for nationalCode=${nationalCode}`,
);
return ownerId;
}
this.logger.warn(
`[resolveOwnerFanavaranId] NO acceptable RoleId 161/166 for nationalCode=${nationalCode}. ` +
`Available: ${asPartyInquiryRows(response)
.map((e) => `{Id=${e.Id}, RoleId=${e.RoleId}}`)
.join("; ")} — submit will register GEN.44 other-people`,
);
return null;
} catch (error) { } catch (error) {
this.logger.warn( this.logger.warn(
`[resolveOwnerFanavaranId] inquiry failed for nationalCode=${nationalCode}: ${ `[resolveOwnerFanavaranId] inquiry failed for nationalCode=${nationalCode}: ${
@@ -4877,6 +4891,37 @@ export class ClaimRequestManagementService {
); );
} }
if (
ownerFanavaranId == null &&
input.registerMissingPerson &&
ownerIdentity.nationalCode &&
ownerIdentity.birthday
) {
const registeredOwner = await this.registerFanavaranOtherPerson({
clientKey: input.clientKey,
person: {
nationalCode: ownerIdentity.nationalCode,
nationalCodeOfDriver: ownerIdentity.nationalCode,
birthday: ownerIdentity.birthday,
driverBirthday: ownerIdentity.birthday,
driverIsInsurer: false,
fullName: (damagedParty as any)?.person?.fullName,
phoneNumber: (damagedParty as any)?.person?.phoneNumber,
userId: (damagedParty as any)?.person?.userId,
},
claimCaseId: input.claimCase?._id
? String(input.claimCase._id)
: undefined,
});
if (registeredOwner != null) {
ownerFanavaranId = registeredOwner;
if (otherPersonId == null) otherPersonId = registeredOwner;
this.logger.log(
`[buildFanavaranDamageCasePayload] OwnerId=${ownerFanavaranId} from GEN.44 other-people`,
);
}
}
if (driverFanavaranId && input.blameCase?._id && damagedParty) { if (driverFanavaranId && input.blameCase?._id && damagedParty) {
const partyIndex = (input.blameCase.parties ?? []).findIndex( const partyIndex = (input.blameCase.parties ?? []).findIndex(
(p: any) => p === damagedParty || (p: any) => p === damagedParty ||
@@ -5726,7 +5771,8 @@ export class ClaimRequestManagementService {
} }
/** /**
* GEN.44: create the damaged party in Fanavaran when parties inquiry has no row. * GEN.44: create the damaged party in Fanavaran when parties inquiry has no
* RoleId 161/166 row (missing entirely, or only e.g. 162 بیمه‌شده).
* Failures stay best-effort — GEN.12 submit continues without DriverId. * Failures stay best-effort — GEN.12 submit continues without DriverId.
*/ */
private async registerFanavaranOtherPerson(input: { private async registerFanavaranOtherPerson(input: {
@@ -5736,7 +5782,8 @@ export class ClaimRequestManagementService {
}): Promise<number | null> { }): Promise<number | null> {
const nationalCode = pickPersonNationalCode(input.person); const nationalCode = pickPersonNationalCode(input.person);
const birthday = pickPersonBirthday(input.person); const birthday = pickPersonBirthday(input.person);
if (!nationalCode || !parseJalaliDateParts(birthday)) { const parsedBirthday = parseJalaliDateParts(birthday);
if (!nationalCode || !parsedBirthday) {
this.logger.warn( this.logger.warn(
`[registerFanavaranOtherPerson] SKIP: missing nationalCode or birthday`, `[registerFanavaranOtherPerson] SKIP: missing nationalCode or birthday`,
); );
@@ -5765,12 +5812,77 @@ export class ClaimRequestManagementService {
} }
} }
/** Seed GEN.44 from parties inquiry when the person exists under a non-driver role. */
let inquirySeed: {
name?: string;
lastName?: string;
fatherName?: string;
genderId?: number;
identityNo?: string;
} = {};
try {
const inquiryRows =
await this.fanavaranLookupService.inquiryByUniqueIdentifier(
input.clientKey,
{
nationalCode,
birthYear: parsedBirthday.year,
birthMonth: parsedBirthday.month,
birthDay: parsedBirthday.day,
},
);
const parties = asPartyInquiryRows(inquiryRows);
const seedRow =
parties.find(
(row) =>
(typeof row.Name === "string" && row.Name.trim()) ||
(typeof row.LastName === "string" && row.LastName.trim()),
) ?? parties[0];
if (seedRow) {
const roleId = Number(seedRow.RoleId);
this.logger.log(
`[registerFanavaranOtherPerson] inquiry seed Id=${seedRow.Id} RoleId=${roleId} — creating GEN.44 سایر (adds RoleId 166 entry; existing roles stay)`,
);
inquirySeed = {
name:
typeof seedRow.Name === "string" ? seedRow.Name : undefined,
lastName:
typeof seedRow.LastName === "string"
? seedRow.LastName
: undefined,
fatherName:
typeof seedRow.FatherName === "string"
? seedRow.FatherName
: undefined,
genderId: (() => {
const g = Number(seedRow.GenderId);
return Number.isFinite(g) && g > 0 ? g : undefined;
})(),
identityNo:
typeof seedRow.IdentityNo === "string"
? seedRow.IdentityNo
: undefined,
};
}
} catch (error) {
this.logger.warn(
`[registerFanavaranOtherPerson] pre-create inquiry failed (continuing with local data): ${
error instanceof Error ? error.message : error
}`,
);
}
const lookups = await this.loadFanavaranOtherPeopleLookups(input.clientKey); const lookups = await this.loadFanavaranOtherPeopleLookups(input.clientKey);
const payload = buildFanavaranOtherPeoplePayload( const payload = buildFanavaranOtherPeoplePayload(
{ {
nationalCode, nationalCode,
birthday, birthday,
fullName: input.person?.fullName ?? user?.fullName, fullName: input.person?.fullName ?? user?.fullName,
name: inquirySeed.name,
lastName: inquirySeed.lastName,
fatherName: inquirySeed.fatherName,
identityNo: inquirySeed.identityNo,
genderId: inquirySeed.genderId,
mobile: input.person?.phoneNumber ?? user?.mobile, mobile: input.person?.phoneNumber ?? user?.mobile,
address: user?.address, address: user?.address,
cityName: user?.city, cityName: user?.city,
@@ -6203,7 +6315,9 @@ export class ClaimRequestManagementService {
}, },
}); });
// GEN.03 CulpritLicence*: guilty-party DRIVER for THIRD_PARTY, FIRST-party DRIVER for CAR_BODY. // GEN.03 CulpritLicence* + CulpritTypeId: guilty-party DRIVER for THIRD_PARTY,
// FIRST-party DRIVER for CAR_BODY. CulpritTypeId uses accident-culprit-type
// (337 بیمه‌گذار / 338 راننده غير از بيمه گذار) — hull remaps separately.
{ {
const culpritParty = const culpritParty =
blameCase.type === BlameRequestType.CAR_BODY blameCase.type === BlameRequestType.CAR_BODY
@@ -6226,6 +6340,10 @@ export class ClaimRequestManagementService {
); );
payload.CulpritLicenceIssuDate = payload.CulpritLicenceIssuDate =
resolveFanavaranLicenceIssuDate(culpritDriver); resolveFanavaranLicenceIssuDate(culpritDriver);
if (blameCase.type !== BlameRequestType.CAR_BODY) {
payload.CulpritTypeId =
resolveThirdPartyCulpritTypeId(culpritParty as any);
}
} }
const carBodyAccidentTime = ( const carBodyAccidentTime = (
@@ -6445,7 +6563,7 @@ export class ClaimRequestManagementService {
} }
if (payload.DriverId == null) { if (payload.DriverId == null) {
warnings.push( warnings.push(
"Damaged party is not in Fanavaran yet; submit will register them via other-people (GEN.44).", "Damaged party has no Fanavaran RoleId 161/166 yet; submit will register them via other-people (GEN.44).",
); );
} }

View File

@@ -5,6 +5,7 @@ import {
pickPersonBirthday, pickPersonBirthday,
pickPersonNationalCode, pickPersonNationalCode,
selectFanavaranDriverId, selectFanavaranDriverId,
selectFanavaranOwnerId,
} from "./fanavaran-driver-inquiry"; } from "./fanavaran-driver-inquiry";
describe("selectFanavaranDriverId", () => { describe("selectFanavaranDriverId", () => {
@@ -21,10 +22,32 @@ describe("selectFanavaranDriverId", () => {
expect(selectFanavaranDriverId(rows, false)).toBe(20); expect(selectFanavaranDriverId(rows, false)).toBe(20);
}); });
it("uses the other role when the preferred role is missing", () => { it("falls back to 166 when preferred 161 is missing", () => {
expect(selectFanavaranDriverId([{ Id: 20, RoleId: 166 }], true)).toBe(20); expect(selectFanavaranDriverId([{ Id: 20, RoleId: 166 }], true)).toBe(20);
}); });
it("returns null for RoleId 162 (بیمه‌شده) — GEN.44 required", () => {
expect(
selectFanavaranDriverId(
[
{
Id: 2390312,
RoleId: 162,
Name: "محمدعلي",
LastName: "موسوي",
},
],
false,
),
).toBeNull();
});
it("does not fall back to RoleId 163 for DriverId", () => {
expect(
selectFanavaranDriverId([{ Id: 99, RoleId: 163 }], false),
).toBeNull();
});
it("picks 4773521 from a real Fanavaran unique-identifier response", () => { it("picks 4773521 from a real Fanavaran unique-identifier response", () => {
expect( expect(
selectFanavaranDriverId( selectFanavaranDriverId(
@@ -39,6 +62,19 @@ describe("selectFanavaranDriverId", () => {
}); });
}); });
describe("selectFanavaranOwnerId", () => {
it("accepts only RoleId 161 or 166", () => {
expect(
selectFanavaranOwnerId([
{ Id: 1, RoleId: 162 },
{ Id: 2, RoleId: 163 },
]),
).toBeNull();
expect(selectFanavaranOwnerId([{ Id: 10, RoleId: 161 }])).toBe(10);
expect(selectFanavaranOwnerId([{ Id: 20, RoleId: 166 }])).toBe(20);
});
});
describe("pickPersonNationalCode / birthday", () => { describe("pickPersonNationalCode / birthday", () => {
it("uses insurer national code when driverIsInsurer", () => { it("uses insurer national code when driverIsInsurer", () => {
expect( expect(

View File

@@ -10,6 +10,9 @@ export type FanavaranPartyInquiryRow = {
NationalCode?: unknown; NationalCode?: unknown;
Name?: unknown; Name?: unknown;
LastName?: unknown; LastName?: unknown;
FatherName?: unknown;
GenderId?: unknown;
IdentityNo?: unknown;
}; };
function asPositiveId(value: unknown): number | null { function asPositiveId(value: unknown): number | null {
@@ -34,9 +37,25 @@ export function asPartyInquiryRows(value: unknown): FanavaranPartyInquiryRow[] {
return []; return [];
} }
/** GEN.12 DriverId is only accepted for بیمه‌گذار (161) or سایر (166). */
export const FANAVARAN_DRIVER_ACCEPTED_ROLE_IDS = [
FANAVARAN_INSURER_ROLE_ID,
FANAVARAN_DRIVER_ROLE_ID,
] as const;
export function isAcceptableFanavaranDriverRole(
roleId: unknown,
): boolean {
const id = asPositiveId(roleId);
return (
id === FANAVARAN_INSURER_ROLE_ID || id === FANAVARAN_DRIVER_ROLE_ID
);
}
function selectFanavaranIdByRoles( function selectFanavaranIdByRoles(
rows: unknown, rows: unknown,
preferredRoles: number[], preferredRoles: number[],
options?: { allowFallbackToFirst?: boolean },
): number | null { ): number | null {
const parties = asPartyInquiryRows(rows); const parties = asPartyInquiryRows(rows);
if (parties.length === 0) return null; if (parties.length === 0) return null;
@@ -46,38 +65,38 @@ function selectFanavaranIdByRoles(
if (match) return asPositiveId(match.Id); if (match) return asPositiveId(match.Id);
} }
if (options?.allowFallbackToFirst === false) return null;
return asPositiveId(parties[0].Id); return asPositiveId(parties[0].Id);
} }
/** /**
* DriverId for GEN.12 زیان‌دیده. Prefer insurer (161) or driver (166) * DriverId for GEN.12 زیان‌دیده.
* based on driverIsInsurer, then any returned party id. * Only RoleId 161 (بیمه‌گذار) or 166 (سایر) are accepted by Fanavaran.
* Other roles (e.g. 162 بیمه‌شده) must go through GEN.44 other-people first
* (GEN.44 adds a RoleId-166 entry; it does not replace existing role rows).
*/ */
export function selectFanavaranDriverId( export function selectFanavaranDriverId(
rows: unknown, rows: unknown,
driverIsInsurer: boolean | undefined, driverIsInsurer: boolean | undefined,
): number | null { ): number | null {
const preferredRoles = driverIsInsurer const preferredRoles = driverIsInsurer
? [ ? [FANAVARAN_INSURER_ROLE_ID, FANAVARAN_DRIVER_ROLE_ID]
FANAVARAN_INSURER_ROLE_ID, : [FANAVARAN_DRIVER_ROLE_ID, FANAVARAN_INSURER_ROLE_ID];
FANAVARAN_DRIVER_ROLE_ID, return selectFanavaranIdByRoles(rows, preferredRoles, {
FANAVARAN_PLAQUE_OWNER_ROLE_ID, allowFallbackToFirst: false,
] });
: [
FANAVARAN_DRIVER_ROLE_ID,
FANAVARAN_INSURER_ROLE_ID,
FANAVARAN_PLAQUE_OWNER_ROLE_ID,
];
return selectFanavaranIdByRoles(rows, preferredRoles);
} }
/** OwnerId: prefer plaque-owner role (163), then insurer / driver. */ /**
* OwnerId for GEN.12: same accepted roles as DriverId (161 / 166 only).
* Prefer بیمه‌گذار then سایر. RoleId 162/163/… → null → GEN.44 on submit.
*/
export function selectFanavaranOwnerId(rows: unknown): number | null { export function selectFanavaranOwnerId(rows: unknown): number | null {
return selectFanavaranIdByRoles(rows, [ return selectFanavaranIdByRoles(
FANAVARAN_PLAQUE_OWNER_ROLE_ID, rows,
FANAVARAN_INSURER_ROLE_ID, [FANAVARAN_INSURER_ROLE_ID, FANAVARAN_DRIVER_ROLE_ID],
FANAVARAN_DRIVER_ROLE_ID, { allowFallbackToFirst: false },
]); );
} }
export function normalizeNationalCode(value: unknown): string | null { export function normalizeNationalCode(value: unknown): string | null {

View File

@@ -49,6 +49,29 @@ describe("fanavaran other people (GEN.44)", () => {
}); });
}); });
it("defaults CityId=9131 and PersonKindId=46 when lookups miss", () => {
const payload = buildFanavaranOtherPeoplePayload({
nationalCode: "2002166323",
birthday: { year: 1353, month: 2, day: 15 },
name: "محمدعلي",
lastName: "موسوي",
fatherName: "حسن",
identityNo: "1205",
genderId: 26,
});
expect(payload).toMatchObject({
NationalCode: "2002166323",
CityId: 9131,
PersonKindId: 46,
GenderId: 26,
Name: "محمدعلي",
LastName: "موسوي",
FatherName: "حسن",
IdentityNo: "1205",
});
});
it("returns null when national code or birthday is missing", () => { it("returns null when national code or birthday is missing", () => {
expect( expect(
buildFanavaranOtherPeoplePayload({ buildFanavaranOtherPeoplePayload({

View File

@@ -25,6 +25,9 @@ export type FanavaranOtherPeopleSource = {
nationalCode?: unknown; nationalCode?: unknown;
birthday?: unknown; birthday?: unknown;
fullName?: unknown; fullName?: unknown;
/** Prefer these over splitting fullName when inquiry already returned them. */
name?: unknown;
lastName?: unknown;
fatherName?: unknown; fatherName?: unknown;
mobile?: unknown; mobile?: unknown;
tel?: unknown; tel?: unknown;
@@ -33,7 +36,13 @@ export type FanavaranOtherPeopleSource = {
jobAddress?: unknown; jobAddress?: unknown;
postalCode?: unknown; postalCode?: unknown;
cityName?: unknown; cityName?: unknown;
/** Direct Fanavaran CityId when known; else lookup / default 9131. */
cityId?: unknown;
gender?: unknown; gender?: unknown;
/** Direct Fanavaran GenderId from parties inquiry when available. */
genderId?: unknown;
/** Direct Fanavaran PersonKindId; else lookup / default 46 (حقیقی). */
personKindId?: unknown;
isIranian?: boolean; isIranian?: boolean;
naturalizedCode?: unknown; naturalizedCode?: unknown;
identityNo?: unknown; identityNo?: unknown;
@@ -82,6 +91,10 @@ const ANS_YES_TEXTS = ["بله", "بلی", "yes", "1"];
const NATURAL_PERSON_TEXTS = ["حقیقی", "شخص حقیقی", "طبيعي", "natural"]; const NATURAL_PERSON_TEXTS = ["حقیقی", "شخص حقیقی", "طبيعي", "natural"];
const IRAN_COUNTRY_TEXTS = ["ایران", "ايران", "iran"]; const IRAN_COUNTRY_TEXTS = ["ایران", "ايران", "iran"];
/** Proven Parsian GEN.44 defaults when lookup / city name is missing. */
export const FANAVARAN_OTHER_PEOPLE_DEFAULT_CITY_ID = 9131;
export const FANAVARAN_OTHER_PEOPLE_DEFAULT_PERSON_KIND_ID = 46;
function asLookupRows(value: unknown): FanavaranLookupRow[] { function asLookupRows(value: unknown): FanavaranLookupRow[] {
if (!Array.isArray(value)) return []; if (!Array.isArray(value)) return [];
return value.filter( return value.filter(
@@ -213,8 +226,8 @@ export function buildFanavaranOtherPeoplePayload(
return { return {
NationalCode: nationalCode, NationalCode: nationalCode,
Name: names.name, Name: emptyToNull(source.name) ?? names.name,
LastName: names.lastName, LastName: emptyToNull(source.lastName) ?? names.lastName,
FatherName: emptyToNull(source.fatherName), FatherName: emptyToNull(source.fatherName),
BirthYear: birthday.year, BirthYear: birthday.year,
BirthMonth: birthday.month, BirthMonth: birthday.month,
@@ -235,8 +248,13 @@ export function buildFanavaranOtherPeoplePayload(
CompanyCode: emptyToNull(source.companyCode), CompanyCode: emptyToNull(source.companyCode),
IdentityNo: emptyToNull(source.identityNo), IdentityNo: emptyToNull(source.identityNo),
RegisterNo: emptyToNull(source.registerNo), RegisterNo: emptyToNull(source.registerNo),
CityId: pickLookupIdByCaption(lookups.cities, [source.cityName]), CityId:
GenderId: pickLookupIdByCaption(lookups.gender, genderTexts), asPositiveId(source.cityId) ??
pickLookupIdByCaption(lookups.cities, [source.cityName]) ??
FANAVARAN_OTHER_PEOPLE_DEFAULT_CITY_ID,
GenderId:
asPositiveId(source.genderId) ??
pickLookupIdByCaption(lookups.gender, genderTexts),
MaritalStatus: pickLookupIdByCaption(lookups.maritalStatus, []), MaritalStatus: pickLookupIdByCaption(lookups.maritalStatus, []),
IsIranian: isIranian IsIranian: isIranian
? (pickLookupIdByCaption(lookups.ans, ANS_YES_TEXTS) ?? 1) ? (pickLookupIdByCaption(lookups.ans, ANS_YES_TEXTS) ?? 1)
@@ -244,9 +262,9 @@ export function buildFanavaranOtherPeoplePayload(
NationalityId: isIranian NationalityId: isIranian
? null ? null
: pickLookupIdByCaption(lookups.countries, IRAN_COUNTRY_TEXTS), : pickLookupIdByCaption(lookups.countries, IRAN_COUNTRY_TEXTS),
PersonKindId: pickLookupIdByCaption( PersonKindId:
lookups.personKind, asPositiveId(source.personKindId) ??
NATURAL_PERSON_TEXTS, pickLookupIdByCaption(lookups.personKind, NATURAL_PERSON_TEXTS) ??
), FANAVARAN_OTHER_PEOPLE_DEFAULT_PERSON_KIND_ID,
}; };
} }

View File

@@ -1,5 +1,7 @@
import { InquiryParticipantRole } from "src/common/dto/inquiry-participants.dto"; import { InquiryParticipantRole } from "src/common/dto/inquiry-participants.dto";
import { import {
FANAVARAN_CULPRIT_TYPE_DRIVER_NOT_INSURER,
FANAVARAN_CULPRIT_TYPE_INSURER,
FANAVARAN_DUMMY_LICENCE_ISSU_DATE, FANAVARAN_DUMMY_LICENCE_ISSU_DATE,
formatFanavaranLicenceIssuDate, formatFanavaranLicenceIssuDate,
resolveDriverFromParty, resolveDriverFromParty,
@@ -8,6 +10,7 @@ import {
resolveFanavaranLicenceNo, resolveFanavaranLicenceNo,
resolveOwnerFromParty, resolveOwnerFromParty,
resolvePolicyholderFromParty, resolvePolicyholderFromParty,
resolveThirdPartyCulpritTypeId,
} from "./fanavaran-party-roles"; } from "./fanavaran-party-roles";
describe("fanavaran-party-roles", () => { describe("fanavaran-party-roles", () => {
@@ -105,6 +108,31 @@ describe("fanavaran-party-roles", () => {
).toBe("0080086519"); ).toBe("0080086519");
}); });
it("maps CulpritTypeId 338 when guilty driver ≠ insurer", () => {
expect(resolveThirdPartyCulpritTypeId(partyWithDistinctRoles)).toBe(
FANAVARAN_CULPRIT_TYPE_DRIVER_NOT_INSURER,
);
});
it("maps CulpritTypeId 337 when guilty driver is the insurer", () => {
expect(
resolveThirdPartyCulpritTypeId({
participants: [
{
participantId: "SAME",
nationalCode: "0080086519",
birthday: "1364/06/28",
},
],
participantRoles: {
driver: "SAME",
thirdPartyPolicyholder: "SAME",
},
person: { driverIsInsurer: true },
}),
).toBe(FANAVARAN_CULPRIT_TYPE_INSURER);
});
it("formats compact Jalali driverLicenseDate", () => { it("formats compact Jalali driverLicenseDate", () => {
expect(formatFanavaranLicenceIssuDate(13980601)).toBe("1398/06/01"); expect(formatFanavaranLicenceIssuDate(13980601)).toBe("1398/06/01");
}); });

View File

@@ -201,6 +201,31 @@ export function resolveDriverIsOwner(party: PartyLike | null | undefined): boole
return party?.person?.driverIsInsurer === true; return party?.person?.driverIsInsurer === true;
} }
/** Lookup: `accident-culprit-type` — بیمه‌گذار */
export const FANAVARAN_CULPRIT_TYPE_INSURER = 337;
/** Lookup: `accident-culprit-type` — راننده غير از بيمه گذار */
export const FANAVARAN_CULPRIT_TYPE_DRIVER_NOT_INSURER = 338;
/**
* GEN.03 ثالث `CulpritTypeId` from whether the guilty party's driver is the insurer.
* Never uses 728 (نا معلوم).
*/
export function resolveThirdPartyCulpritTypeId(
party: PartyLike | null | undefined,
): number {
const driver = resolveDriverFromParty(party);
const insurer = resolvePolicyholderFromParty(party, "third-party");
if (driver.nationalCode && insurer.nationalCode) {
return driver.nationalCode === insurer.nationalCode
? FANAVARAN_CULPRIT_TYPE_INSURER
: FANAVARAN_CULPRIT_TYPE_DRIVER_NOT_INSURER;
}
if (party?.person?.driverIsInsurer === false) {
return FANAVARAN_CULPRIT_TYPE_DRIVER_NOT_INSURER;
}
return FANAVARAN_CULPRIT_TYPE_INSURER;
}
/** Licence number for Fanavaran; never empty — uses `dummy` when user value is missing. */ /** Licence number for Fanavaran; never empty — uses `dummy` when user value is missing. */
export function resolveFanavaranLicenceNo( export function resolveFanavaranLicenceNo(
identity: Pick<FanavaranRoleIdentity, "licenseNo">, identity: Pick<FanavaranRoleIdentity, "licenseNo">,