mapping problems fixed

This commit is contained in:
2026-08-01 17:56:58 +03:30
parent ea3db8f025
commit 86f8b829fd
5 changed files with 283 additions and 78 deletions

View File

@@ -809,7 +809,12 @@ export class ClaimRequestManagementService {
throw er;
}
if (er.code === 11000) {
if (
typeof er === "object" &&
er !== null &&
"code" in er &&
er.code === 11000
) {
throw new HttpException(
"Duplicate claim file reference.",
HttpStatus.CONFLICT,
@@ -1413,8 +1418,8 @@ export class ClaimRequestManagementService {
return new ClaimPartUploadDetail([updatedClaim]);
} catch (error) {
this.logger.error(
`Error in selectCarOtherPartDamage: ${error.message}`,
error.stack,
`Error in selectCarOtherPartDamage: ${error instanceof Error ? error.message : String(error)}`,
error instanceof Error ? error.stack : undefined,
);
// Re-throw the specific error from the validation steps or a generic one.
throw error;
@@ -1516,7 +1521,7 @@ export class ClaimRequestManagementService {
}
} catch (fileError) {
this.logger.warn(
`Failed to delete file ${existingDoc.path}: ${fileError.message}`,
`Failed to delete file ${existingDoc.path}: ${fileError instanceof Error ? fileError.message : String(fileError)}`,
);
// Continue even if file deletion fails
}
@@ -1612,8 +1617,8 @@ export class ClaimRequestManagementService {
};
} catch (error) {
this.logger.error(
`Error in uploadRequiredDocument: ${error.message}`,
error.stack,
`Error in uploadRequiredDocument: ${error instanceof Error ? error.message : String(error)}`,
error instanceof Error ? error.stack : undefined,
);
throw error;
}
@@ -1658,8 +1663,8 @@ export class ClaimRequestManagementService {
};
} catch (error) {
this.logger.error(
`Error in getRequiredDocumentsStatus: ${error.message}`,
error.stack,
`Error in getRequiredDocumentsStatus: ${error instanceof Error ? error.message : String(error)}`,
error instanceof Error ? error.stack : undefined,
);
throw error;
}
@@ -1922,8 +1927,8 @@ export class ClaimRequestManagementService {
);
} catch (error) {
this.logger.error(
`[BACKGROUND AI] Failed to process AI requests for part ${partId}, request ${requestId}: ${error.message}`,
error.stack,
`[BACKGROUND AI] Failed to process AI requests for part ${partId}, request ${requestId}: ${error instanceof Error ? error.message : String(error)}`,
error instanceof Error ? error.stack : undefined,
);
// Don't throw - we're in background processing
}
@@ -2013,11 +2018,15 @@ export class ClaimRequestManagementService {
`[SUCCESS] Successfully updated AI report and image for part ${partId}`,
);
} catch (dbError) {
const dbErrorMessage =
dbError instanceof Error ? dbError.message : String(dbError);
this.logger.error(`[ERROR] Database update error for part ${partId}:`);
this.logger.error(`[ERROR] ${dbError.message}`);
this.logger.error(`[ERROR] Stack: ${dbError.stack}`);
this.logger.error(`[ERROR] ${dbErrorMessage}`);
this.logger.error(
`[ERROR] Stack: ${dbError instanceof Error ? dbError.stack : undefined}`,
);
throw new HttpException(
`Database update failed: ${dbError.message}`,
`Database update failed: ${dbErrorMessage}`,
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
@@ -2053,7 +2062,8 @@ export class ClaimRequestManagementService {
);
} catch (error) {
// Extract error source from error message if available
const errorMessage = error.message || String(error);
const errorMessage =
error instanceof Error ? error.message : String(error);
const errorSource = errorMessage.includes("[ERROR]")
? errorMessage.split("[ERROR]")[1]?.split("]")[0] || "UNKNOWN"
: "UNKNOWN";
@@ -2089,7 +2099,7 @@ export class ClaimRequestManagementService {
this.logger.error(`🔴 [FINAL FAILURE] File path: ${newImageDoc.path}`);
this.logger.error(`🔴 [FINAL FAILURE] Error source: ${errorSource}`);
this.logger.error(`🔴 [FINAL FAILURE] Final error: ${errorMessage}`);
if (error.stack) {
if (error instanceof Error && error.stack) {
this.logger.error(`🔴 [FINAL FAILURE] Stack trace: ${error.stack}`);
}
// Optional: Update the claim status to indicate an AI failure.
@@ -2856,7 +2866,7 @@ export class ClaimRequestManagementService {
} catch (error) {
this.logger.error(
`Error during factor upload for claim ${claimId}:`,
error.stack,
error instanceof Error ? error.stack : String(error),
);
if (error instanceof HttpException) {
@@ -3155,7 +3165,12 @@ export class ClaimRequestManagementService {
};
} catch (err) {
this.logger.error(err);
return new HttpException(err.message, err.status);
return new HttpException(
err instanceof HttpException ? err.message : String(err),
err instanceof HttpException
? err.getStatus()
: HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
@@ -3214,7 +3229,12 @@ export class ClaimRequestManagementService {
};
} catch (err) {
this.logger.error(err);
return new HttpException(err.message, err.status);
return new HttpException(
err instanceof HttpException ? err.message : String(err),
err instanceof HttpException
? err.getStatus()
: HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
@@ -3609,6 +3629,53 @@ export class ClaimRequestManagementService {
return `${plate.centerDigits}${plate.centerAlphabet}${plate.leftDigits}`;
}
/** First non-empty string from party vehicle.inquiry mapped/raw (ESG + Tejarat aliases). */
private pickPartyInquiryField(
mapped: Record<string, any> | null | undefined,
raw: Record<string, any> | null | undefined,
keys: string[],
): string | null {
for (const key of keys) {
const value = mapped?.[key] ?? raw?.[key];
if (value == null) continue;
const text = String(value).trim();
if (text) return text;
}
return null;
}
private resolvePlateFromPartyInquiry(
mapped: Record<string, any> | null | undefined,
raw: Record<string, any> | null | undefined,
): {
leftDigits: number;
centerAlphabet: string;
centerDigits: number;
ir: number;
} | null {
const left = this.pickPartyInquiryField(mapped, raw, ["plk1", "Plk1"]);
const middle = this.pickPartyInquiryField(mapped, raw, ["plk2", "Plk2"]);
const right = this.pickPartyInquiryField(mapped, raw, ["plk3", "Plk3"]);
const serial = this.pickPartyInquiryField(mapped, raw, [
"plksrl",
"PlkSrl",
]);
if (!left || !middle || !right || !serial) return null;
const leftDigits = Number(left);
const centerDigits = Number(right);
const ir = Number(serial);
const centerAlphabet = this.plateNormalizer.normalizePlateText(middle);
if (
!Number.isFinite(leftDigits) ||
!Number.isFinite(centerDigits) ||
!Number.isFinite(ir) ||
!centerAlphabet
) {
return null;
}
return { leftDigits, centerAlphabet, centerDigits, ir };
}
private async resolveVehicleKindId(
clientKey: FanavaranClientKey,
carType?: string,
@@ -3781,10 +3848,6 @@ export class ClaimRequestManagementService {
FaultPercent: number;
};
}): Promise<Record<string, unknown>> {
const plate = this.resolveOwnershipPlateForClaim(
input.claimCase,
input.blameCase,
);
const damagedParty = Array.isArray(input.blameCase?.parties)
? input.blameCase.parties.find(
(party: any) =>
@@ -3795,8 +3858,19 @@ export class ClaimRequestManagementService {
const person = damagedParty?.person ?? {};
const vehicle = damagedParty?.vehicle ?? {};
const insurance = damagedParty?.insurance ?? {};
const inquiryMapped = vehicle?.inquiry?.mapped ?? {};
const inquiryRaw = vehicle?.inquiry?.raw ?? {};
// Prefer nested inquiry (ESG/Tejarat) on the damaged party — not flattened insurance.*
const inquiryRawPayload =
vehicle?.inquiry?.raw?.data != null &&
typeof vehicle.inquiry.raw.data === "object" &&
!Array.isArray(vehicle.inquiry.raw.data)
? vehicle.inquiry.raw.data
: (vehicle?.inquiry?.raw ?? {});
const inquiryMapped = vehicle?.inquiry?.mapped ?? inquiryRawPayload ?? {};
const inquiryRaw = inquiryRawPayload ?? {};
const plate =
this.resolveOwnershipPlateForClaim(input.claimCase, input.blameCase) ??
this.resolvePlateFromPartyInquiry(inquiryMapped, inquiryRaw);
this.logger.log(
`[buildFanavaranDamageCasePayload] damagedParty resolved: userId=${damagedParty?.person?.userId ?? "NONE"}, ` +
@@ -3812,22 +3886,44 @@ export class ClaimRequestManagementService {
);
}
const guiltyUserId =
input.blameCase?.expertSubmitReplyFinal?.guiltyUserId ??
input.blameCase?.expertSubmitReply?.guiltyUserId;
const blamedParty =
guiltyUserId && Array.isArray(input.blameCase?.parties)
? input.blameCase.parties.find(
(party: any) =>
String(party?.person?.userId ?? "") === String(guiltyUserId),
)
: null;
// Get PolicyCINumber from damaged party's inquiry (mapped or raw), not blamed party
const policyCINumber =
inquiryMapped.ThirdPolicyCode ??
inquiryRaw.ThirdPolicyCode ??
// Policy document / unique code / coverage dates from damaged party's plate inquiry
const policyNo =
this.pickPartyInquiryField(inquiryMapped, inquiryRaw, [
"PrntCmpDocNo",
"PrntPlcyCmpDocNo",
"printNumber",
"insuranceNumber",
]) ??
insurance.policyNumber ??
null;
const policyCINumber = this.pickPartyInquiryField(
inquiryMapped,
inquiryRaw,
["PlcyUnqCod", "ThirdPolicyCode"],
);
const beginDate =
this.pickPartyInquiryField(inquiryMapped, inquiryRaw, [
"HBgnDte",
"StartDate",
"persianStartDate",
"SatrtDate",
]) ??
insurance.startDate ??
null;
const endDate =
this.pickPartyInquiryField(inquiryMapped, inquiryRaw, [
"HEndDte",
"EndDate",
"persianEndDate",
]) ??
insurance.endDate ??
null;
const builtYearRaw = this.pickPartyInquiryField(inquiryMapped, inquiryRaw, [
"PrdDte",
"ModelField",
"ModelCii",
]);
const builtYear = builtYearRaw ? Number(builtYearRaw) || null : null;
const carType = input.claimCase?.vehicle?.carType as string | undefined;
const vehicleKindId = await this.resolveVehicleKindId(
@@ -3870,24 +3966,23 @@ export class ClaimRequestManagementService {
}
this.logger.log(
`[buildFanavaranDamageCasePayload] Final DriverId=${driverFanavaranId ?? "NULL"} for nationalCodeOfDriver=${person.nationalCodeOfDriver ?? "MISSING"}`,
`[buildFanavaranDamageCasePayload] Final DriverId=${driverFanavaranId ?? "NULL"} for nationalCodeOfDriver=${person.nationalCodeOfDriver ?? "MISSING"}, ` +
`PolicyNo=${policyNo ?? "NULL"}, PolicyCINumber=${policyCINumber ?? "NULL"}`,
);
return {
BeginDate: insurance.startDate ?? null,
BuiltYear:
Number(inquiryMapped.ModelField ?? inquiryMapped.ModelCii) || Number(inquiryMapped.PrdDte) || null,
ChassisNo:
inquiryMapped.ChassisNumberField ??
inquiryMapped.ShsNum ??
inquiryRaw.ChassisNumberField ??
inquiryRaw.ShsNum ??
null,
BeginDate: beginDate,
BuiltYear: builtYear,
ChassisNo: this.pickPartyInquiryField(inquiryMapped, inquiryRaw, [
"ShsNum",
"ChassisNumberField",
"shsNam",
]),
DmgHistoryStatus: input.defaults.DmgHistoryStatus,
Desc: this.formatFanavaranSelectedPartsDesc(input.selectedParts),
DmgCaseTypeId: input.defaults.DmgCaseTypeId,
DriverId: driverFanavaranId ?? null,
EndDate: insurance.endDate ?? null,
EndDate: endDate,
EstimateAmount: FANAVARAN_PROVISIONAL_ESTIMATE_AMOUNT,
FaultPercent: input.defaults.FaultPercent,
InsuranceCorpId: await this.fanavaranLookupService.resolveInsuranceCorpId(input.clientKey),
@@ -3898,12 +3993,11 @@ export class ClaimRequestManagementService {
LicenceIssuDate: person.driverBirthday ?? "1394/10/13",
LicenceNo: person.driverLicense ?? "1124242",
LicenceTypeId: input.defaults.CulpritLicenceTypeId,
MotorNo:
inquiryMapped.MtrNum ??
inquiryMapped.EngineNumberField ??
inquiryRaw.MtrNum ??
inquiryRaw.EngineNumberField ??
null,
MotorNo: this.pickPartyInquiryField(inquiryMapped, inquiryRaw, [
"MtrNum",
"EngineNumberField",
"mtrnum",
]),
OwnerId: null,
PlaqueCityId: null,
PlaqueKindId: plate ? input.defaults.PlaqueKindId : null,
@@ -3911,14 +4005,20 @@ export class ClaimRequestManagementService {
PlaqueMiddleCodeId: this.getFanavaranPlateMiddleCode(
plate?.centerAlphabet,
),
PlaqueNo: this.formatFanavaranPlateNo(plate),
PlaqueNo:
this.pickPartyInquiryField(inquiryMapped, inquiryRaw, ["Plk"]) ??
this.formatFanavaranPlateNo(plate),
PlaqueRightNo: plate ? String(plate.centerDigits) : null,
PlaqueSampleId: plate ? input.defaults.PlaqueSampleId : null,
PlaqueSerial: plate ? String(plate.ir) : null,
PolicyNo: insurance.policyNumber ?? null,
PreviousPolicyEndDate: insurance.endDate ?? "",
PolicyNo: policyNo,
PreviousPolicyEndDate: endDate ?? "",
VehicleKindId: vehicleKindId,
VIN: inquiryMapped.VIN ?? inquiryRaw.VIN ?? null,
VIN: this.pickPartyInquiryField(inquiryMapped, inquiryRaw, [
"VIN",
"vin",
"VinNumberField",
]),
AccidentVehicleUsedId: input.defaults.AccidentVehicleUsedId,
PolicyCINumber: policyCINumber,
};