fanavaran car body and third_party lookup codings applied

This commit is contained in:
2026-09-06 10:45:40 +03:30
parent 5f714e1456
commit 02edd0b2ca
5 changed files with 160 additions and 2 deletions

View File

@@ -291,9 +291,10 @@ export class FanavaranLookupService {
async customerById( async customerById(
clientKey: FanavaranClientKey, clientKey: FanavaranClientKey,
customerId: number, customerId: number,
options?: { contractIdOverride?: string },
): Promise<unknown> { ): Promise<unknown> {
const url = `${FANAVARAN_LOOKUP_BASE_URL}/common/customers/${customerId}`; const url = `${FANAVARAN_LOOKUP_BASE_URL}/common/customers/${customerId}`;
return this.fetchFromFanavaran(clientKey, url); return this.fetchFromFanavaran(clientKey, url, options);
} }
async inquiryByUniqueIdentifier( async inquiryByUniqueIdentifier(

View File

@@ -1,9 +1,12 @@
import { import {
filterPoliciesByLine, filterPoliciesByLine,
insuranceLineLabel,
parseLastCarPolicyInput, parseLastCarPolicyInput,
pickVehicleId, pickVehicleId,
plaqueLetterFromMiddleCode,
plaqueMatchesVehicle, plaqueMatchesVehicle,
selectLastAmongCarMatches, selectLastAmongCarMatches,
toAppPlaque,
vehicleMatchesCar, vehicleMatchesCar,
} from "./fanavaran-last-car-policy"; } from "./fanavaran-last-car-policy";
@@ -40,6 +43,28 @@ describe("fanavaran last car policy", () => {
}); });
}); });
it("maps insurance line ids to CAR_BODY and THIRD_PARTY", () => {
expect(insuranceLineLabel(4)).toBe("CAR_BODY");
expect(insuranceLineLabel(5)).toBe("THIRD_PARTY");
});
it("converts PlaqueMiddleCodeId 5 to د and builds app plaque parts", () => {
expect(plaqueLetterFromMiddleCode(5)).toBe("د");
expect(
toAppPlaque({
PlaqueLeftNo: "29",
PlaqueMiddleCodeId: 5,
PlaqueRightNo: "782",
PlaqueSerial: "60",
}),
).toEqual({
leftTwoDigits: "29",
serialLetter: "د",
threeDigits: "782",
rightTwoDigits: "60",
});
});
it("keeps only the requested insurance line", () => { it("keeps only the requested insurance line", () => {
const rows = filterPoliciesByLine( const rows = filterPoliciesByLine(
[ [

View File

@@ -94,6 +94,52 @@ export function insuranceLineIdForProduct(
: FANAVARAN_THIRD_PARTY_LINE_ID; : FANAVARAN_THIRD_PARTY_LINE_ID;
} }
export function insuranceLineLabel(
insuranceLineId: number,
): "CAR_BODY" | "THIRD_PARTY" | null {
if (insuranceLineId === FANAVARAN_CAR_BODY_LINE_ID) return "CAR_BODY";
if (insuranceLineId === FANAVARAN_THIRD_PARTY_LINE_ID) return "THIRD_PARTY";
return null;
}
export function plaqueLetterFromMiddleCode(code: unknown): string | null {
const id = parseFanavaranId(code);
if (id === null) return null;
const found = Object.entries(FANAVARAN_PLATE_LETTER_CODE).find(
([, value]) => value === id,
);
return found?.[0] ?? null;
}
export type AppPlaque = {
leftTwoDigits: string;
serialLetter: string;
threeDigits: string;
rightTwoDigits: string;
};
export function toAppPlaque(
vehicle: Record<string, unknown> | null,
): AppPlaque | null {
if (!vehicle) return null;
const leftTwoDigits = normalizePlaquePart(
vehicle.PlaqueLeftNo ?? vehicle.plaqueLeftNo,
);
const threeDigits = normalizePlaquePart(
vehicle.PlaqueRightNo ?? vehicle.plaqueRightNo,
);
const rightTwoDigits = normalizePlaquePart(
vehicle.PlaqueSerial ?? vehicle.plaqueSerial,
);
const serialLetter = plaqueLetterFromMiddleCode(
vehicle.PlaqueMiddleCodeId ?? vehicle.plaqueMiddleCodeId,
);
if (!leftTwoDigits || !serialLetter || !threeDigits || !rightTwoDigits) {
return null;
}
return { leftTwoDigits, serialLetter, threeDigits, rightTwoDigits };
}
export function toEnglishDigits(value: unknown): string { export function toEnglishDigits(value: unknown): string {
return String(value ?? "") return String(value ?? "")
.split("") .split("")

View File

@@ -58,6 +58,7 @@ describe("LookupsService.findLastProcessedCarPolicy", () => {
}), }),
), ),
getRemoteLookup: jest.fn().mockResolvedValue([]), getRemoteLookup: jest.fn().mockResolvedValue([]),
customerById: jest.fn().mockResolvedValue(null),
}; };
const service = createService(lookup); const service = createService(lookup);
@@ -68,10 +69,15 @@ describe("LookupsService.findLastProcessedCarPolicy", () => {
expect(result.policyId).toBe(20); expect(result.policyId).toBe(20);
expect(result.product).toBe("third-party"); expect(result.product).toBe("third-party");
expect(result.insuranceLine).toBe("THIRD_PARTY");
expect(result.insuranceLineId).toBe(5); expect(result.insuranceLineId).toBe(5);
expect(result.vehicle).toEqual({ expect(result.vehicle).toEqual({
Id: 500, Id: 500,
VIN: "IRNKAEK4150012345", VIN: "IRNKAEK4150012345",
color: null,
vehicleKind: null,
used: null,
plaque: null,
}); });
expect(lookup.thirdPartyPolicyById).toHaveBeenCalled(); expect(lookup.thirdPartyPolicyById).toHaveBeenCalled();
expect(lookup.myPolicies.mock.calls[0][2]).toBe(5); expect(lookup.myPolicies.mock.calls[0][2]).toBe(5);
@@ -103,6 +109,7 @@ describe("LookupsService.findLastProcessedCarPolicy", () => {
PlaqueSerial: "67", PlaqueSerial: "67",
}), }),
getRemoteLookup: jest.fn().mockResolvedValue([]), getRemoteLookup: jest.fn().mockResolvedValue([]),
customerById: jest.fn().mockResolvedValue(null),
}; };
const service = createService(lookup); const service = createService(lookup);
@@ -116,6 +123,13 @@ describe("LookupsService.findLastProcessedCarPolicy", () => {
expect(result.policyId).toBe(44); expect(result.policyId).toBe(44);
expect(result.product).toBe("car-body"); expect(result.product).toBe("car-body");
expect(result.insuranceLine).toBe("CAR_BODY");
expect(result.vehicle?.plaque).toEqual({
leftTwoDigits: "12",
serialLetter: "ب",
threeDigits: "345",
rightTwoDigits: "67",
});
expect(lookup.bodyPolicyById).toHaveBeenCalled(); expect(lookup.bodyPolicyById).toHaveBeenCalled();
expect(lookup.thirdPartyPolicyById).not.toHaveBeenCalled(); expect(lookup.thirdPartyPolicyById).not.toHaveBeenCalled();
expect(lookup.inquiryByVin).not.toHaveBeenCalled(); expect(lookup.inquiryByVin).not.toHaveBeenCalled();
@@ -147,6 +161,7 @@ describe("LookupsService.findLastProcessedCarPolicy", () => {
ChassisNo: "RDE07024", ChassisNo: "RDE07024",
}), }),
getRemoteLookup: jest.fn().mockResolvedValue([]), getRemoteLookup: jest.fn().mockResolvedValue([]),
customerById: jest.fn().mockResolvedValue(null),
}; };
const service = createService(lookup); const service = createService(lookup);

View File

@@ -19,11 +19,13 @@ import {
asObjectRecord, asObjectRecord,
filterPoliciesByLine, filterPoliciesByLine,
insuranceLineIdForProduct, insuranceLineIdForProduct,
insuranceLineLabel,
parseFanavaranId, parseFanavaranId,
parseLastCarPolicyInput, parseLastCarPolicyInput,
pickVehicleId, pickVehicleId,
selectLastAmongCarMatches, selectLastAmongCarMatches,
sortPoliciesNewestEndDateFirst, sortPoliciesNewestEndDateFirst,
toAppPlaque,
vehicleMatchesCar, vehicleMatchesCar,
type FanavaranCarPolicyProduct, type FanavaranCarPolicyProduct,
type HydratedFanavaranCarPolicy, type HydratedFanavaranCarPolicy,
@@ -203,9 +205,11 @@ export class LookupsService {
}, },
): Promise<{ ): Promise<{
product: FanavaranCarPolicyProduct; product: FanavaranCarPolicyProduct;
insuranceLine: "CAR_BODY" | "THIRD_PARTY" | null;
insuranceLineId: number; insuranceLineId: number;
policyId: number; policyId: number;
policy: any; policy: any;
customer: Record<string, unknown> | null;
vehicle: Record<string, unknown> | null; vehicle: Record<string, unknown> | null;
}> { }> {
const parsed = parseLastCarPolicyInput(query); const parsed = parseLastCarPolicyInput(query);
@@ -355,10 +359,77 @@ export class LookupsService {
return { return {
product, product,
insuranceLine: insuranceLineLabel(insuranceLineId),
insuranceLineId, insuranceLineId,
policyId: selected.policyId, policyId: selected.policyId,
policy: await this.mapPolicyDetails(selected.policy), policy: await this.mapPolicyDetails(selected.policy),
vehicle: selected.vehicle, customer: await this.fetchCustomer(
clientKey,
parseFanavaranId(selected.policy.CustomerId),
requestOptions,
),
vehicle: await this.enrichVehicle(selected.vehicle),
};
}
private async fetchCustomer(
clientKey: ReturnType<LookupsService["activeClientKey"]>,
customerId: number | null,
requestOptions: { contractIdOverride?: string },
): Promise<Record<string, unknown> | null> {
if (customerId === null) return null;
try {
return asObjectRecord(
await this.fanavaranLookupService.customerById(
clientKey,
customerId,
requestOptions,
),
);
} catch (error) {
this.logger.warn(
`Customer GET failed for CustomerId=${customerId}: ${
error instanceof Error ? error.message : error
}`,
);
return null;
}
}
private async lookupRowById(
lookupName: string,
id: number | null,
): Promise<Record<string, unknown> | null> {
if (id === null) return null;
try {
const rows = await this.getClientRemoteLookup(lookupName);
if (!Array.isArray(rows)) return null;
const match = rows.find(
(row) => parseFanavaranId((row as { Id?: unknown })?.Id) === id,
);
return asObjectRecord(match);
} catch (error) {
this.logger.warn(
`Failed to resolve ${lookupName} id=${id}: ${
error instanceof Error ? error.message : error
}`,
);
return null;
}
}
private async enrichVehicle(
vehicle: Record<string, unknown> | null,
): Promise<Record<string, unknown> | null> {
if (!vehicle) return null;
const vehicleKindId = parseFanavaranId(vehicle.VehicleKindId);
const usedId = parseFanavaranId(vehicle.UsedId);
return {
...vehicle,
color: null,
vehicleKind: await this.lookupRowById("vehicle-kinds", vehicleKindId),
used: await this.lookupRowById("vehicle-use-types", usedId),
plaque: toAppPlaque(vehicle),
}; };
} }