forked from Yara724/api
920 lines
27 KiB
TypeScript
920 lines
27 KiB
TypeScript
import {
|
|
resolveClaimOwnerParty,
|
|
resolveDamagedPartyRow,
|
|
} from "src/helpers/blame-damaged-party";
|
|
import { toJalaliDateAndTime } from "src/helpers/date-jalali";
|
|
import { PartyRole } from "src/request-management/entities/schema/partyRole.enum";
|
|
import {
|
|
InsurerFileReportField,
|
|
InsurerFileReportSection,
|
|
InsurerFileReportViewModel,
|
|
} from "./case-expert-report.types";
|
|
import { PR, persianFieldPath, persianStatus } from "./persian-report-labels";
|
|
|
|
const SKIP_FLATTEN_KEYS = new Set([
|
|
"_id",
|
|
"__v",
|
|
"history",
|
|
"workflow",
|
|
"evidence",
|
|
"confirmation",
|
|
"inquiries",
|
|
"raw",
|
|
]);
|
|
|
|
type ReportRecord = Record<string, unknown>;
|
|
type ReportParty = ReportRecord & {
|
|
role?: string;
|
|
person?: ReportRecord;
|
|
insurance?: ReportRecord & { carBodyInsurance?: ReportRecord };
|
|
vehicle?: ReportRecord;
|
|
statement?: ReportRecord;
|
|
location?: { lat?: number; lon?: number };
|
|
};
|
|
|
|
function asString(value: unknown): string | undefined {
|
|
if (value === undefined || value === null || value === "") return undefined;
|
|
if (value instanceof Date) {
|
|
const [d, t] = toJalaliDateAndTime(value);
|
|
return `${d} ${t}`;
|
|
}
|
|
if (typeof value === "object") {
|
|
if (typeof (value as { toString?: () => string }).toString === "function") {
|
|
const s = String(value);
|
|
if (s !== "[object Object]") return s;
|
|
}
|
|
return undefined;
|
|
}
|
|
return String(value);
|
|
}
|
|
|
|
function formatBirthDate(value: unknown): string | undefined {
|
|
if (value === undefined || value === null || value === "") return undefined;
|
|
const raw = String(value);
|
|
if (/^\d{8}$/.test(raw)) {
|
|
return `${raw.slice(0, 4)}/${raw.slice(4, 6)}/${raw.slice(6, 8)}`;
|
|
}
|
|
return raw;
|
|
}
|
|
|
|
function formatDateTime(value: unknown): string | undefined {
|
|
if (value === undefined || value === null || value === "") return undefined;
|
|
const date = value instanceof Date ? value : new Date(value as string | number);
|
|
if (Number.isNaN(date.getTime())) return asString(value);
|
|
const [d, t] = toJalaliDateAndTime(date);
|
|
return `${d} ${t}`;
|
|
}
|
|
|
|
function firstDefined(...values: unknown[]): string | undefined {
|
|
for (const value of values) {
|
|
const str = asString(value);
|
|
if (str) return str;
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
function normalizeListValue(value: unknown): string | undefined {
|
|
if (!Array.isArray(value) || !value.length) return undefined;
|
|
const items = value
|
|
.map((item) => asString(item) ?? JSON.stringify(item))
|
|
.filter(Boolean);
|
|
return items.length ? items.join("، ") : undefined;
|
|
}
|
|
|
|
function flattenObject(
|
|
obj: unknown,
|
|
prefix = "",
|
|
depth = 0,
|
|
): InsurerFileReportField[] {
|
|
if (obj == null) return [];
|
|
if (depth > 4) {
|
|
return [{ label: persianFieldPath(prefix), value: asString(obj) }];
|
|
}
|
|
|
|
if (Array.isArray(obj)) {
|
|
const value = normalizeListValue(obj);
|
|
return value
|
|
? [{ label: persianFieldPath(prefix || "items"), value }]
|
|
: [];
|
|
}
|
|
|
|
if (typeof obj !== "object") {
|
|
return [{ label: persianFieldPath(prefix || "value"), value: asString(obj) }];
|
|
}
|
|
|
|
const rows: InsurerFileReportField[] = [];
|
|
const objRecord = obj as Record<string, unknown>;
|
|
const hasMapped =
|
|
objRecord.mapped != null && typeof objRecord.mapped === "object";
|
|
|
|
for (const [key, value] of Object.entries(objRecord)) {
|
|
if (SKIP_FLATTEN_KEYS.has(key)) continue;
|
|
if (key === "raw" && hasMapped) continue;
|
|
if (value === undefined || value === null || value === "") continue;
|
|
|
|
const path = prefix ? `${prefix}.${key}` : key;
|
|
if (
|
|
typeof value === "object" &&
|
|
!Array.isArray(value) &&
|
|
!(value instanceof Date)
|
|
) {
|
|
rows.push(...flattenObject(value, path, depth + 1));
|
|
} else {
|
|
rows.push({ label: persianFieldPath(path), value: asString(value) });
|
|
}
|
|
}
|
|
|
|
return rows;
|
|
}
|
|
|
|
function dedupeFields(fields: InsurerFileReportField[]): InsurerFileReportField[] {
|
|
const seen = new Set<string>();
|
|
const out: InsurerFileReportField[] = [];
|
|
|
|
for (const field of fields) {
|
|
const value = asString(field.value);
|
|
if (!value) continue;
|
|
const key = `${field.label}::${value}`;
|
|
if (seen.has(key)) continue;
|
|
seen.add(key);
|
|
out.push({ label: field.label, value });
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
function filterEmptySections(
|
|
sections: Array<InsurerFileReportSection | undefined>,
|
|
): InsurerFileReportSection[] {
|
|
return sections.filter(
|
|
(section): section is InsurerFileReportSection => !!section && section.fields.length > 0,
|
|
);
|
|
}
|
|
|
|
function buildSection(
|
|
title: string,
|
|
fields: InsurerFileReportField[],
|
|
withPlaceholder = true,
|
|
): InsurerFileReportSection {
|
|
const deduped = dedupeFields(fields);
|
|
return {
|
|
title,
|
|
fields:
|
|
deduped.length || !withPlaceholder
|
|
? deduped
|
|
: [{ label: PR.data, value: PR.empty }],
|
|
};
|
|
}
|
|
|
|
function expertNameFromSnapshot(snapshot?: {
|
|
firstName?: string;
|
|
lastName?: string;
|
|
}): string | undefined {
|
|
if (!snapshot) return undefined;
|
|
const name = [snapshot.firstName, snapshot.lastName].filter(Boolean).join(" ");
|
|
return name || undefined;
|
|
}
|
|
|
|
function collectExpertNames(
|
|
blame?: Record<string, unknown> | null,
|
|
claim?: Record<string, unknown> | null,
|
|
): string | undefined {
|
|
const names = new Set<string>();
|
|
|
|
const blameDecision = (
|
|
blame?.expert as Record<string, unknown> | undefined
|
|
)?.decision as Record<string, unknown> | undefined;
|
|
const blameExpert = expertNameFromSnapshot(
|
|
blameDecision?.expertProfileSnapshot as
|
|
| { firstName?: string; lastName?: string }
|
|
| undefined,
|
|
);
|
|
if (blameExpert) names.add(blameExpert);
|
|
|
|
const evaluation = claim?.evaluation as Record<string, unknown> | undefined;
|
|
for (const key of ["damageExpertReplyFinal", "damageExpertReply"] as const) {
|
|
const reply = evaluation?.[key] as Record<string, unknown> | undefined;
|
|
if (!reply) continue;
|
|
const actor = (reply.actorDetail as { actorName?: string } | undefined)
|
|
?.actorName;
|
|
if (actor) names.add(actor);
|
|
const snap = expertNameFromSnapshot(
|
|
reply.expertProfileSnapshot as
|
|
| { firstName?: string; lastName?: string }
|
|
| undefined,
|
|
);
|
|
if (snap) names.add(snap);
|
|
}
|
|
|
|
return names.size ? [...names].join(", ") : undefined;
|
|
}
|
|
|
|
function inquiryRoleData(
|
|
inquiries: Record<string, unknown> | undefined,
|
|
key: string,
|
|
role?: string,
|
|
): Record<string, unknown> | undefined {
|
|
const block = inquiries?.[key] as Record<string, unknown> | undefined;
|
|
const data = block?.data as Record<string, unknown> | undefined;
|
|
if (!data) return undefined;
|
|
if (role && data[role] && typeof data[role] === "object") {
|
|
return data[role] as Record<string, unknown>;
|
|
}
|
|
return data;
|
|
}
|
|
|
|
function pickInquiryReportPayload(
|
|
inquiry?: Record<string, unknown>,
|
|
): Record<string, unknown> | undefined {
|
|
if (!inquiry) return undefined;
|
|
const mapped = inquiry.mapped;
|
|
if (mapped && typeof mapped === "object" && !Array.isArray(mapped)) {
|
|
return mapped as Record<string, unknown>;
|
|
}
|
|
const { raw: _raw, ...rest } = inquiry;
|
|
return Object.keys(rest).length ? rest : inquiry;
|
|
}
|
|
|
|
function resolveReportBlameContext(
|
|
blame?: Record<string, unknown> | null,
|
|
claim?: Record<string, unknown> | null,
|
|
): Record<string, unknown> | null {
|
|
if (blame) return blame;
|
|
const snapshot = claim?.snapshot as Record<string, unknown> | undefined;
|
|
if (!snapshot) return null;
|
|
return {
|
|
type:
|
|
(claim?.blameFileContext as Record<string, unknown> | undefined)
|
|
?.blameRequestType ??
|
|
(snapshot.accident as Record<string, unknown> | undefined)?.type,
|
|
blameStatus: (claim?.blameFileContext as Record<string, unknown> | undefined)
|
|
?.blameStatus,
|
|
parties: snapshot.parties,
|
|
};
|
|
}
|
|
|
|
function getPartyRole(party: ReportParty | null | undefined): string | undefined {
|
|
const role = party?.role;
|
|
return typeof role === "string" ? role : undefined;
|
|
}
|
|
|
|
function partyKindLabel(
|
|
party: ReportParty | null | undefined,
|
|
damagedParty: ReportParty | null,
|
|
guiltyParty: ReportParty | null,
|
|
): string | undefined {
|
|
if (sameParty(party, damagedParty)) return "damaged";
|
|
if (sameParty(party, guiltyParty)) return "guilty";
|
|
return undefined;
|
|
}
|
|
|
|
function partyRoleLabel(role: string | undefined): string | undefined {
|
|
if (!role) return undefined;
|
|
if (role === PartyRole.FIRST) return "طرف اول";
|
|
if (role === PartyRole.SECOND) return "طرف دوم";
|
|
return role;
|
|
}
|
|
|
|
function statementBoolean(value: unknown): string | undefined {
|
|
if (typeof value !== "boolean") return undefined;
|
|
return persianStatus(value);
|
|
}
|
|
|
|
function sameParty(
|
|
first: ReportParty | null | undefined,
|
|
second: ReportParty | null | undefined,
|
|
): boolean {
|
|
if (!first || !second) return false;
|
|
const firstUserId = first.person?.userId != null ? String(first.person.userId) : "";
|
|
const secondUserId =
|
|
second.person?.userId != null ? String(second.person.userId) : "";
|
|
if (firstUserId && secondUserId) return firstUserId === secondUserId;
|
|
return getPartyRole(first) === getPartyRole(second);
|
|
}
|
|
|
|
function resolveEvaluationReply(
|
|
claim?: Record<string, unknown> | null,
|
|
): Record<string, unknown> | undefined {
|
|
const evaluation = claim?.evaluation as Record<string, unknown> | undefined;
|
|
return (
|
|
(evaluation?.damageExpertReplyFinal as Record<string, unknown> | undefined) ??
|
|
(evaluation?.damageExpertReply as Record<string, unknown> | undefined)
|
|
);
|
|
}
|
|
|
|
function insuranceValueFromCandidates(
|
|
...values: unknown[]
|
|
): string | undefined {
|
|
for (const value of values) {
|
|
if (Array.isArray(value)) {
|
|
const listValue = normalizeListValue(value);
|
|
if (listValue) return listValue;
|
|
continue;
|
|
}
|
|
const str = asString(value);
|
|
if (str) return str;
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
function buildThirdPartyInsuranceFields(
|
|
party: ReportParty | null | undefined,
|
|
blame?: Record<string, unknown> | null,
|
|
claim?: Record<string, unknown> | null,
|
|
): InsurerFileReportField[] {
|
|
const role = getPartyRole(party);
|
|
const direct = (party?.insurance ?? {}) as Record<string, unknown>;
|
|
const blameInquiry = pickInquiryReportPayload(
|
|
inquiryRoleData(blame?.inquiries as Record<string, unknown> | undefined, "thirdParty", role),
|
|
);
|
|
const claimInquiry = pickInquiryReportPayload(
|
|
inquiryRoleData(claim?.inquiries as Record<string, unknown> | undefined, "thirdParty", role),
|
|
);
|
|
|
|
return [
|
|
{
|
|
label: PR.policyNumber,
|
|
value: insuranceValueFromCandidates(
|
|
direct.policyNumber,
|
|
blameInquiry?.policyNumber,
|
|
blameInquiry?.PolicyNumber,
|
|
claimInquiry?.policyNumber,
|
|
claimInquiry?.PolicyNumber,
|
|
blameInquiry?.ThirdPolicyCode,
|
|
claimInquiry?.ThirdPolicyCode,
|
|
),
|
|
},
|
|
{
|
|
label: PR.insuranceCompany,
|
|
value: insuranceValueFromCandidates(
|
|
direct.company,
|
|
direct.insurerCompany,
|
|
blameInquiry?.company,
|
|
blameInquiry?.CompanyName,
|
|
claimInquiry?.company,
|
|
claimInquiry?.CompanyName,
|
|
),
|
|
},
|
|
{
|
|
label: PR.policyStartDate,
|
|
value: insuranceValueFromCandidates(
|
|
direct.startDate,
|
|
blameInquiry?.startDate,
|
|
blameInquiry?.PolicyStartDate,
|
|
blameInquiry?.SatrtDate,
|
|
claimInquiry?.startDate,
|
|
claimInquiry?.PolicyStartDate,
|
|
claimInquiry?.SatrtDate,
|
|
),
|
|
},
|
|
{
|
|
label: PR.policyEndDate,
|
|
value: insuranceValueFromCandidates(
|
|
direct.endDate,
|
|
blameInquiry?.endDate,
|
|
blameInquiry?.PolicyEndDate,
|
|
blameInquiry?.EndDate,
|
|
claimInquiry?.endDate,
|
|
claimInquiry?.PolicyEndDate,
|
|
claimInquiry?.EndDate,
|
|
),
|
|
},
|
|
{
|
|
label: PR.financialCeiling,
|
|
value: insuranceValueFromCandidates(
|
|
direct.financialCeiling,
|
|
blameInquiry?.financialCeiling,
|
|
blameInquiry?.FinancialCvrCptl,
|
|
blameInquiry?.FnCvrCptl,
|
|
claimInquiry?.financialCeiling,
|
|
claimInquiry?.FinancialCvrCptl,
|
|
claimInquiry?.FnCvrCptl,
|
|
),
|
|
},
|
|
{
|
|
label: PR.coverages,
|
|
value: insuranceValueFromCandidates(
|
|
direct.coverages,
|
|
blameInquiry?.coverages,
|
|
claimInquiry?.coverages,
|
|
),
|
|
},
|
|
];
|
|
}
|
|
|
|
function buildCarBodyInsuranceFields(
|
|
party: ReportParty | null | undefined,
|
|
blame?: Record<string, unknown> | null,
|
|
claim?: Record<string, unknown> | null,
|
|
): InsurerFileReportField[] {
|
|
const role = getPartyRole(party);
|
|
const direct = (party?.insurance?.carBodyInsurance ??
|
|
party?.insurance?.carBody ??
|
|
{}) as Record<string, unknown>;
|
|
const blameInquiry = pickInquiryReportPayload(
|
|
inquiryRoleData(blame?.inquiries as Record<string, unknown> | undefined, "carBody", role),
|
|
);
|
|
const claimInquiry = pickInquiryReportPayload(
|
|
inquiryRoleData(claim?.inquiries as Record<string, unknown> | undefined, "carBody", role),
|
|
);
|
|
const legacy = (blame?.carBodyInsuranceDetail ?? {}) as Record<string, unknown>;
|
|
|
|
return [
|
|
{
|
|
label: PR.policyNumber,
|
|
value: insuranceValueFromCandidates(
|
|
direct.policyNumber,
|
|
legacy.policyNumber,
|
|
blameInquiry?.policyNumber,
|
|
blameInquiry?.PolicyNumber,
|
|
claimInquiry?.policyNumber,
|
|
claimInquiry?.PolicyNumber,
|
|
),
|
|
},
|
|
{
|
|
label: PR.insuranceCompany,
|
|
value: insuranceValueFromCandidates(
|
|
direct.insurerCompany,
|
|
direct.company,
|
|
legacy.insurerCompany,
|
|
blameInquiry?.company,
|
|
blameInquiry?.CompanyName,
|
|
claimInquiry?.company,
|
|
claimInquiry?.CompanyName,
|
|
),
|
|
},
|
|
{
|
|
label: PR.policyStartDate,
|
|
value: insuranceValueFromCandidates(
|
|
direct.startDate,
|
|
legacy.startDate,
|
|
blameInquiry?.startDate,
|
|
blameInquiry?.PolicyStartDate,
|
|
claimInquiry?.startDate,
|
|
claimInquiry?.PolicyStartDate,
|
|
),
|
|
},
|
|
{
|
|
label: PR.policyEndDate,
|
|
value: insuranceValueFromCandidates(
|
|
direct.endDate,
|
|
legacy.endDate,
|
|
blameInquiry?.endDate,
|
|
blameInquiry?.PolicyEndDate,
|
|
claimInquiry?.endDate,
|
|
claimInquiry?.PolicyEndDate,
|
|
),
|
|
},
|
|
{
|
|
label: PR.coverages,
|
|
value: insuranceValueFromCandidates(
|
|
direct.coverages,
|
|
legacy.coverages,
|
|
blameInquiry?.coverages,
|
|
claimInquiry?.coverages,
|
|
),
|
|
},
|
|
];
|
|
}
|
|
|
|
function buildPartyOwnerSection(
|
|
title: string,
|
|
party: ReportParty | null | undefined,
|
|
options?: {
|
|
claim?: Record<string, unknown> | null;
|
|
useClaimOwnerFallback?: boolean;
|
|
includeSheba?: boolean;
|
|
},
|
|
): InsurerFileReportSection | undefined {
|
|
const person = party?.person as ReportRecord | undefined;
|
|
const claim = options?.claim;
|
|
const money = claim?.money as
|
|
| { sheba?: string; nationalCodeOfInsurer?: string }
|
|
| undefined;
|
|
const claimOwner = claim?.owner as { fullName?: string } | undefined;
|
|
|
|
if (!person && !options?.useClaimOwnerFallback) return undefined;
|
|
|
|
return buildSection(title, [
|
|
{
|
|
label: PR.name,
|
|
value: options?.useClaimOwnerFallback
|
|
? firstDefined(person?.fullName, claimOwner?.fullName)
|
|
: firstDefined(person?.fullName),
|
|
},
|
|
{ label: PR.phone, value: asString(person?.phoneNumber) },
|
|
{
|
|
label: PR.nationalCode,
|
|
value: options?.useClaimOwnerFallback
|
|
? firstDefined(person?.nationalCodeOfInsurer, money?.nationalCodeOfInsurer)
|
|
: firstDefined(person?.nationalCodeOfInsurer, person?.nationalCode),
|
|
},
|
|
{
|
|
label: PR.birthDate,
|
|
value: formatBirthDate(
|
|
person?.insurerBirthday ?? person?.birthday ?? person?.driverBirthday,
|
|
),
|
|
},
|
|
{
|
|
label: PR.sheba,
|
|
value: options?.includeSheba ? money?.sheba : undefined,
|
|
},
|
|
]);
|
|
}
|
|
|
|
function licenseFieldsFromInquiry(
|
|
inquiry?: Record<string, unknown>,
|
|
): { licenseType?: string; licenseDate?: string } {
|
|
if (!inquiry) return {};
|
|
return {
|
|
licenseType: firstDefined(
|
|
inquiry.LicenseType,
|
|
inquiry.licenseType,
|
|
inquiry.Type,
|
|
inquiry.type,
|
|
inquiry.LicenseCategory,
|
|
inquiry.licenseCategory,
|
|
),
|
|
licenseDate: firstDefined(
|
|
inquiry.IssueDate,
|
|
inquiry.issueDate,
|
|
inquiry.LicenseIssueDate,
|
|
inquiry.licenseIssueDate,
|
|
inquiry.ExpireDate,
|
|
inquiry.expireDate,
|
|
),
|
|
};
|
|
}
|
|
|
|
function buildDriverSection(
|
|
damagedParty: ReportParty | null,
|
|
blame?: Record<string, unknown> | null,
|
|
): InsurerFileReportSection | undefined {
|
|
const person = damagedParty?.person as ReportRecord | undefined;
|
|
if (!person || person.driverIsInsurer !== false) return undefined;
|
|
|
|
const role = damagedParty?.role ?? PartyRole.FIRST;
|
|
const licenseInquiry = inquiryRoleData(
|
|
blame?.inquiries as Record<string, unknown> | undefined,
|
|
"drivingLicence",
|
|
String(role),
|
|
);
|
|
const { licenseType, licenseDate } = licenseFieldsFromInquiry(licenseInquiry);
|
|
|
|
return buildSection(PR.driverSection, [
|
|
{ label: PR.name, value: asString(person.fullName) },
|
|
{
|
|
label: PR.licenseType,
|
|
value: licenseType ?? (person.driverLicense ? PR.driverLicense : undefined),
|
|
},
|
|
{
|
|
label: PR.licenseDate,
|
|
value: licenseDate ?? asString(person.driverLicense),
|
|
},
|
|
{ label: PR.phone, value: asString(person.phoneNumber) },
|
|
{ label: PR.nationalCode, value: asString(person.nationalCodeOfDriver) },
|
|
{ label: PR.birthDate, value: formatBirthDate(person.driverBirthday) },
|
|
{ label: PR.licenseNumber, value: asString(person.driverLicense) },
|
|
]);
|
|
}
|
|
|
|
function buildPartyVehicleSection(
|
|
title: string,
|
|
party: ReportParty | null | undefined,
|
|
claimVehicle?: Record<string, unknown>,
|
|
): InsurerFileReportSection | undefined {
|
|
if (!party && !claimVehicle) return undefined;
|
|
|
|
return buildSection(title, [
|
|
...flattenObject(claimVehicle, "claim.vehicle"),
|
|
...flattenObject(party?.vehicle, "party.vehicle"),
|
|
]);
|
|
}
|
|
|
|
function buildPartyStatementSection(
|
|
title: string,
|
|
party: ReportParty | null | undefined,
|
|
damagedParty: ReportParty | null,
|
|
guiltyParty: ReportParty | null,
|
|
): InsurerFileReportSection | undefined {
|
|
if (!party) return undefined;
|
|
const statement = (party.statement ?? {}) as ReportRecord;
|
|
const kind = partyKindLabel(party, damagedParty, guiltyParty);
|
|
|
|
return buildSection(title, [
|
|
{
|
|
label: PR.partyRole,
|
|
value: partyRoleLabel(getPartyRole(party)),
|
|
},
|
|
{
|
|
label: PR.name,
|
|
value: firstDefined(party.person?.fullName),
|
|
},
|
|
{
|
|
label: PR.admitsGuilt,
|
|
value:
|
|
kind === "damaged"
|
|
? undefined
|
|
: statementBoolean(statement.admitsGuilt),
|
|
},
|
|
{
|
|
label: PR.claimsDamage,
|
|
value:
|
|
kind === "guilty"
|
|
? undefined
|
|
: statementBoolean(statement.claimsDamage),
|
|
},
|
|
{
|
|
label: PR.acceptsExpertOpinion,
|
|
value: statementBoolean(statement.acceptsExpertOpinion),
|
|
},
|
|
{
|
|
label: PR.partyDescription,
|
|
value: asString(statement.description),
|
|
},
|
|
]);
|
|
}
|
|
|
|
function buildCaseTimelineSection(
|
|
overview?: Record<string, unknown> | null,
|
|
claim?: Record<string, unknown> | null,
|
|
): InsurerFileReportSection {
|
|
const evaluationReply = resolveEvaluationReply(claim);
|
|
return buildSection(PR.timelineSection, [
|
|
{
|
|
label: PR.fileRegisteredAt,
|
|
value: formatDateTime(overview?.createdAt),
|
|
},
|
|
{
|
|
label: PR.evaluationRegisteredAt,
|
|
value: formatDateTime(evaluationReply?.submittedAt),
|
|
},
|
|
]);
|
|
}
|
|
|
|
function buildFanavaranCodesSection(
|
|
claim?: Record<string, unknown> | null,
|
|
): InsurerFileReportSection | undefined {
|
|
if (!claim) return undefined;
|
|
const sync = (claim.fanavaranSync as Record<string, unknown> | undefined) ?? {};
|
|
const baseClaim = (sync.baseClaim as Record<string, unknown> | undefined) ?? {};
|
|
const damageCase = (sync.damageCase as Record<string, unknown> | undefined) ?? {};
|
|
const expertise = (sync.expertise as Record<string, unknown> | undefined) ?? {};
|
|
|
|
return buildSection(PR.fanavaranSection, [
|
|
{
|
|
label: PR.fanavaranClaimNo,
|
|
value: firstDefined(claim.claimNo, baseClaim.claimNo),
|
|
},
|
|
{
|
|
label: PR.fanavaranClaimId,
|
|
value: firstDefined(claim.claimId, baseClaim.claimId),
|
|
},
|
|
{
|
|
label: PR.fanavaranDamageCaseId,
|
|
value: firstDefined(claim.dmgCaseId, damageCase.dmgCaseId, expertise.dmgCaseId),
|
|
},
|
|
{
|
|
label: PR.fanavaranExpertiseId,
|
|
value: firstDefined(claim.expertiseId, expertise.expertiseId),
|
|
},
|
|
{
|
|
label: PR.fanavaranPolicyId,
|
|
value: firstDefined(baseClaim.policyId),
|
|
},
|
|
{
|
|
label: PR.fanavaranDriverId,
|
|
value: firstDefined(baseClaim.driverId),
|
|
},
|
|
{
|
|
label: PR.fanavaranVehicleKindId,
|
|
value: firstDefined(baseClaim.vehicleKindId),
|
|
},
|
|
{
|
|
label: PR.fanavaranInsuranceCorpId,
|
|
value: firstDefined(baseClaim.insuranceCorpId),
|
|
},
|
|
]);
|
|
}
|
|
|
|
function buildEvaluationSection(
|
|
claim?: Record<string, unknown> | null,
|
|
): InsurerFileReportSection | undefined {
|
|
if (!claim) return undefined;
|
|
const reply = resolveEvaluationReply(claim);
|
|
const actorDetail = reply?.actorDetail as { actorName?: string } | undefined;
|
|
const snapshotName = expertNameFromSnapshot(
|
|
reply?.expertProfileSnapshot as
|
|
| { firstName?: string; lastName?: string }
|
|
| undefined,
|
|
);
|
|
|
|
return buildSection(PR.evaluationSection, [
|
|
{
|
|
label: PR.evaluationResult,
|
|
value: persianStatus(claim.claimStatus),
|
|
},
|
|
{
|
|
label: PR.evaluationExpert,
|
|
value: actorDetail?.actorName ?? snapshotName,
|
|
},
|
|
{
|
|
label: PR.evaluationSubmittedAt,
|
|
value: formatDateTime(reply?.submittedAt),
|
|
},
|
|
{
|
|
label: PR.evaluationResponse,
|
|
value: asString(reply?.description),
|
|
},
|
|
]);
|
|
}
|
|
|
|
function buildAccidentReportSection(
|
|
blame?: Record<string, unknown> | null,
|
|
claim?: Record<string, unknown> | null,
|
|
damagedParty?: ReportParty | null,
|
|
): InsurerFileReportSection {
|
|
const statement = damagedParty?.statement as ReportRecord | undefined;
|
|
const location = damagedParty?.location;
|
|
const snapshotAccident = (
|
|
claim?.snapshot as { accident?: Record<string, unknown> } | undefined
|
|
)?.accident;
|
|
const blameDecision = (
|
|
blame?.expert as Record<string, unknown> | undefined
|
|
)?.decision as Record<string, unknown> | undefined;
|
|
const decisionFields = blameDecision?.fields as Record<string, unknown> | undefined;
|
|
|
|
return buildSection(PR.accidentSection, [
|
|
{
|
|
label: PR.accidentDate,
|
|
value:
|
|
asString(statement?.accidentDate) ??
|
|
asString(snapshotAccident?.date) ??
|
|
asString(blame?.createdAtFormatted) ??
|
|
formatDateTime(blame?.createdAt),
|
|
},
|
|
{
|
|
label: PR.accidentTime,
|
|
value: asString(statement?.accidentTime) ?? asString(snapshotAccident?.time),
|
|
},
|
|
{
|
|
label: PR.experts,
|
|
value: collectExpertNames(blame, claim),
|
|
},
|
|
{
|
|
label: PR.location,
|
|
value:
|
|
location?.lat != null && location?.lon != null
|
|
? `${location.lat}، ${location.lon}`
|
|
: undefined,
|
|
},
|
|
{
|
|
label: PR.weather,
|
|
value:
|
|
asString(statement?.weatherCondition) ??
|
|
asString(snapshotAccident?.weatherCondition),
|
|
},
|
|
{
|
|
label: PR.road,
|
|
value:
|
|
asString(statement?.roadCondition) ??
|
|
asString(snapshotAccident?.roadCondition),
|
|
},
|
|
{
|
|
label: PR.light,
|
|
value:
|
|
asString(statement?.lightCondition) ??
|
|
asString(snapshotAccident?.lightCondition),
|
|
},
|
|
{
|
|
label: PR.blameStatus,
|
|
value: persianStatus(blame?.blameStatus),
|
|
},
|
|
{
|
|
label: PR.claimStatus,
|
|
value: persianStatus(claim?.claimStatus),
|
|
},
|
|
{ label: PR.expertDecision, value: asString(blameDecision?.description) },
|
|
{
|
|
label: PR.accidentWay,
|
|
value: asString(
|
|
(decisionFields?.accidentWay as { label?: string } | undefined)?.label ??
|
|
(
|
|
snapshotAccident?.classification as {
|
|
accidentWay?: { label?: string };
|
|
}
|
|
)?.accidentWay?.label,
|
|
),
|
|
},
|
|
{
|
|
label: PR.accidentReason,
|
|
value: asString(
|
|
(decisionFields?.accidentReason as { label?: string } | undefined)?.label ??
|
|
(
|
|
snapshotAccident?.classification as {
|
|
accidentReason?: { label?: string };
|
|
}
|
|
)?.accidentReason?.label,
|
|
),
|
|
},
|
|
{
|
|
label: PR.accidentType,
|
|
value: asString(
|
|
(decisionFields?.accidentType as { label?: string } | undefined)?.label ??
|
|
(
|
|
snapshotAccident?.classification as {
|
|
accidentType?: { label?: string };
|
|
}
|
|
)?.accidentType?.label,
|
|
),
|
|
},
|
|
{
|
|
label: PR.partyDescription,
|
|
value: asString(statement?.description),
|
|
},
|
|
]);
|
|
}
|
|
|
|
export function buildInsurerFileReport(file: {
|
|
overview?: Record<string, unknown>;
|
|
blame?: Record<string, unknown>;
|
|
claim?: Record<string, unknown>;
|
|
}): InsurerFileReportViewModel {
|
|
const overview = file.overview ?? {};
|
|
const claim = file.claim ?? null;
|
|
const blame = file.blame ?? null;
|
|
const blameContext = resolveReportBlameContext(blame, claim);
|
|
const damagedParty = blameContext
|
|
? (resolveDamagedPartyRow(blameContext as any) as ReportParty | null)
|
|
: null;
|
|
const guiltyParty = blameContext
|
|
? (resolveClaimOwnerParty(blameContext as any) as ReportParty | null)
|
|
: null;
|
|
const isCarBody =
|
|
String((blameContext?.type as string | undefined) ?? "") === "CAR_BODY";
|
|
const includeGuiltySections = !!guiltyParty && !(isCarBody && sameParty(damagedParty, guiltyParty));
|
|
|
|
const claimVehicle = claim?.vehicle as Record<string, unknown> | undefined;
|
|
|
|
const sections = filterEmptySections([
|
|
buildCaseTimelineSection(overview, claim),
|
|
buildPartyOwnerSection(PR.ownerSection, damagedParty, {
|
|
claim,
|
|
useClaimOwnerFallback: true,
|
|
includeSheba: true,
|
|
}),
|
|
includeGuiltySections
|
|
? buildPartyOwnerSection(PR.guiltyOwnerSection, guiltyParty)
|
|
: undefined,
|
|
buildDriverSection(damagedParty, blameContext),
|
|
buildSection(
|
|
PR.damagedThirdPartyInsuranceSection,
|
|
buildThirdPartyInsuranceFields(damagedParty, blameContext, claim),
|
|
),
|
|
buildSection(
|
|
PR.damagedCarBodyInsuranceSection,
|
|
buildCarBodyInsuranceFields(damagedParty, blameContext, claim),
|
|
),
|
|
includeGuiltySections
|
|
? buildSection(
|
|
PR.guiltyThirdPartyInsuranceSection,
|
|
buildThirdPartyInsuranceFields(guiltyParty, blameContext, claim),
|
|
)
|
|
: undefined,
|
|
includeGuiltySections
|
|
? buildSection(
|
|
PR.guiltyCarBodyInsuranceSection,
|
|
buildCarBodyInsuranceFields(guiltyParty, blameContext, claim),
|
|
)
|
|
: undefined,
|
|
buildPartyVehicleSection(PR.damagedVehicleSection, damagedParty, claimVehicle),
|
|
includeGuiltySections
|
|
? buildPartyVehicleSection(PR.guiltyVehicleSection, guiltyParty)
|
|
: undefined,
|
|
buildPartyStatementSection(
|
|
PR.damagedStatementSection,
|
|
damagedParty,
|
|
damagedParty,
|
|
guiltyParty,
|
|
),
|
|
includeGuiltySections
|
|
? buildPartyStatementSection(
|
|
PR.guiltyStatementSection,
|
|
guiltyParty,
|
|
damagedParty,
|
|
guiltyParty,
|
|
)
|
|
: undefined,
|
|
buildFanavaranCodesSection(claim),
|
|
buildEvaluationSection(claim),
|
|
buildAccidentReportSection(blameContext, claim, damagedParty),
|
|
]);
|
|
|
|
return {
|
|
title: PR.reportTitle,
|
|
publicId: asString(overview.publicId) ?? PR.empty,
|
|
requestNo: asString(overview.requestNo),
|
|
sections,
|
|
};
|
|
}
|