forked from Yara724/api
fanavaran stages matching data is double checked and more controlled +
This commit is contained in:
@@ -262,6 +262,159 @@ export function pickFanavaranVehicleFromInquiry(
|
||||
return asObjectRecord(inquired);
|
||||
}
|
||||
|
||||
export type FanavaranDamagedVehicleIdentity = {
|
||||
vin?: string | null;
|
||||
chassis?: string | null;
|
||||
plaque?: FanavaranPlaqueParts | null;
|
||||
policyCINumber?: string | null;
|
||||
vehicleKindId?: number | null;
|
||||
};
|
||||
|
||||
export function asVehicleInquiryRows(
|
||||
inquired: unknown,
|
||||
): Record<string, unknown>[] {
|
||||
if (Array.isArray(inquired)) {
|
||||
return inquired
|
||||
.map((row) => asObjectRecord(row))
|
||||
.filter((row): row is Record<string, unknown> => row != null);
|
||||
}
|
||||
const one = asObjectRecord(inquired);
|
||||
return one ? [one] : [];
|
||||
}
|
||||
|
||||
export function normalizePolicyCINumber(value: unknown): string {
|
||||
return toEnglishDigits(value).replace(/\s+/g, "").trim();
|
||||
}
|
||||
|
||||
function vehicleHasPlaqueParts(vehicle: Record<string, unknown>): boolean {
|
||||
return Boolean(
|
||||
asPlaqueText(vehicle.PlaqueLeftNo ?? vehicle.plaqueLeftNo) &&
|
||||
(parseFanavaranId(
|
||||
vehicle.PlaqueMiddleCodeId ?? vehicle.plaqueMiddleCodeId,
|
||||
) != null ||
|
||||
String(vehicle.PlaqueLetter ?? vehicle.plaqueLetter ?? "").trim()) &&
|
||||
asPlaqueText(vehicle.PlaqueRightNo ?? vehicle.plaqueRightNo) &&
|
||||
asPlaqueText(vehicle.PlaqueSerial ?? vehicle.plaqueSerial),
|
||||
);
|
||||
}
|
||||
|
||||
function vinEquals(left: unknown, right: unknown): boolean {
|
||||
const a = normalizeVin(left);
|
||||
const b = normalizeVin(right);
|
||||
return Boolean(a && b && a === b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Same physical car: VIN and plaque must not contradict the damaged-party
|
||||
* inquiry. A row that only "has PlaqueKindId" is not enough.
|
||||
*/
|
||||
export function fanavaranVehicleMatchesDamagedIdentity(
|
||||
vehicle: Record<string, unknown> | null,
|
||||
identity: FanavaranDamagedVehicleIdentity,
|
||||
): boolean {
|
||||
if (!vehicle) return false;
|
||||
|
||||
const identityVin = normalizeVin(identity.vin);
|
||||
const identityChassis = normalizeVin(identity.chassis);
|
||||
const vehicleVin = pickVehicleVin(vehicle);
|
||||
const vehicleChassis = normalizeVin(
|
||||
vehicle.ChassisNo ?? vehicle.chassisNo ?? vehicle.ShsNum,
|
||||
);
|
||||
|
||||
const vinMatched = Boolean(
|
||||
identityVin &&
|
||||
(vinEquals(vehicleVin, identityVin) ||
|
||||
vinEquals(vehicleChassis, identityVin)),
|
||||
);
|
||||
const chassisMatched = Boolean(
|
||||
identityChassis &&
|
||||
(vinEquals(vehicleVin, identityChassis) ||
|
||||
vinEquals(vehicleChassis, identityChassis)),
|
||||
);
|
||||
|
||||
if (identityVin && vehicleVin && !vinMatched && !chassisMatched) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const plaqueMatched = Boolean(
|
||||
identity.plaque && plaqueMatchesVehicle(identity.plaque, vehicle),
|
||||
);
|
||||
if (identity.plaque && vehicleHasPlaqueParts(vehicle) && !plaqueMatched) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return vinMatched || chassisMatched || plaqueMatched;
|
||||
}
|
||||
|
||||
export function fanavaranPayloadMatchesDamagedIdentity(
|
||||
payload: Record<string, unknown> | null,
|
||||
identity: FanavaranDamagedVehicleIdentity,
|
||||
): boolean {
|
||||
if (!payload) return false;
|
||||
|
||||
const payloadVin = normalizeVin(payload.VIN ?? payload.vin);
|
||||
const identityVin = normalizeVin(identity.vin);
|
||||
if (payloadVin && identityVin && payloadVin !== identityVin) return false;
|
||||
|
||||
const payloadChassis = normalizeVin(
|
||||
payload.ChassisNo ?? payload.chassisNo,
|
||||
);
|
||||
const identityChassis = normalizeVin(identity.chassis);
|
||||
if (payloadChassis && identityChassis && payloadChassis !== identityChassis) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const payloadCI = normalizePolicyCINumber(payload.PolicyCINumber);
|
||||
const identityCI = normalizePolicyCINumber(identity.policyCINumber);
|
||||
if (payloadCI && identityCI && payloadCI !== identityCI) return false;
|
||||
|
||||
if (identity.plaque && vehicleHasPlaqueParts(payload)) {
|
||||
if (!plaqueMatchesVehicle(identity.plaque, payload)) return false;
|
||||
}
|
||||
|
||||
const vinMatched = Boolean(payloadVin && identityVin && payloadVin === identityVin);
|
||||
const chassisMatched = Boolean(
|
||||
payloadChassis && identityChassis && payloadChassis === identityChassis,
|
||||
);
|
||||
const ciMatched = Boolean(payloadCI && identityCI && payloadCI === identityCI);
|
||||
const plaqueMatched = Boolean(
|
||||
identity.plaque && plaqueMatchesVehicle(identity.plaque, payload),
|
||||
);
|
||||
return vinMatched || chassisMatched || ciMatched || plaqueMatched;
|
||||
}
|
||||
|
||||
export function canReuseCachedFanavaranVehicleIds(source: unknown): boolean {
|
||||
return source === "vehicle-inquiry" || source === "vin-inquiry" || source === "vehicle-get";
|
||||
}
|
||||
|
||||
/**
|
||||
* Pick the VIN-inquiry / vehicle-GET row that is the damaged car.
|
||||
* Newest VersionNo wins among matches; PlaqueKindId is a tie-break only.
|
||||
*/
|
||||
export function selectFanavaranVehicleForDamageCase(
|
||||
inquired: unknown,
|
||||
identity: FanavaranDamagedVehicleIdentity,
|
||||
): Record<string, unknown> | null {
|
||||
const matches = asVehicleInquiryRows(inquired).filter((row) =>
|
||||
fanavaranVehicleMatchesDamagedIdentity(row, identity),
|
||||
);
|
||||
if (matches.length === 0) return null;
|
||||
|
||||
return matches.reduce((current, candidate) => {
|
||||
const currentVersion = parseFanavaranId(current.VersionNo) ?? 0;
|
||||
const candidateVersion = parseFanavaranId(candidate.VersionNo) ?? 0;
|
||||
if (candidateVersion !== currentVersion) {
|
||||
return candidateVersion > currentVersion ? candidate : current;
|
||||
}
|
||||
const currentHasKind =
|
||||
pickFanavaranPlaqueLookupFields(current).kindId != null;
|
||||
const candidateHasKind =
|
||||
pickFanavaranPlaqueLookupFields(candidate).kindId != null;
|
||||
if (candidateHasKind && !currentHasKind) return candidate;
|
||||
return current;
|
||||
});
|
||||
}
|
||||
|
||||
export function pickVinFromPartyVehicle(party: {
|
||||
vehicle?: { vin?: unknown; inquiry?: unknown };
|
||||
} | null | undefined): string | null {
|
||||
|
||||
Reference in New Issue
Block a user