feat(pl24): enrich P5 decode for non-VAG OEMs (SEV-2)
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled

parseVehicleResponse only read VAG/BMW vinfoBasic labels, so Jaguar/Land Rover/Toyota/MAN/Suzuki dropped engine+transmission and Mitsubishi's model showed an internal chassis code.

- engine/transmission: read brand labels (JLR Motor Tipi/Vites Kutusu, Toyota ENGINE 1/ATM,MTM + parens engine code, MAN Yedek motor/Şanzıman, Suzuki Motor No./Şanzıman numarası). - Mitsubishi model: prefer Araç/description (L200), not the internal Model code (KB4T 2500DIESEL...). - value normalize: unescape literal \- + collapse whitespace (was prNr-only) → fixes 'XJ 2010 \- 2019', '2005\-07', 'COROLLA   (TUP)'. VAG/BMW/Mercedes/Fiat unchanged. 40/40 pl24 specs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-05 10:48:39 +03:00
parent 01ed1a4480
commit 0d64e22c8d
2 changed files with 190 additions and 10 deletions

View File

@@ -28,6 +28,8 @@ const p = svc as unknown as {
series: string | null;
bodyType: string | null;
engineCode: string | null;
engineType: string | null;
transmission: string | null;
catalogInfo?: { mainGroupsPath?: string };
};
};
@@ -178,3 +180,148 @@ describe("PL24Service.parseVehicleResponse — p5daimler (year from 'Teslimat ta
expect(r.model).toBe("C 180 KOMPRESSOR Sedan");
});
});
// SEV-2: non-VAG P5 OEMs label engine/transmission differently and emit "\-" escapes /
// padded model strings. The shared parser must read those labels + normalize the values.
describe("PL24Service.parseVehicleResponse — p5jlr (Jaguar/LandRover engine+trans, \\- cleanup)", () => {
const jlrData = {
resultStatus: "VEHICLE_IDENTIFIED",
description: "XJ 2010 - 2019 (X351)",
link: {
path: "/p5jlr/extern/groups/vin_main?lang=tr&serviceName=jaguar_parts&vin=SAJAA10MXDPV41100",
},
segments: {
vinfoBasic: {
records: [
{ values: { description: "Sasi numarasi", value: "SAJAA10MXDPV41100" } },
{ values: { description: "Model", value: "XJ 2010 \\- 2019 (X351)" } },
{ values: { description: "Üretim tarihi", value: "18 Eyl 2012" } },
{ values: { description: "Motor Tipi", value: "2.0L 16V TIVCT T/C 240PS Petrol" } },
{ values: { description: "Vites Kutusu", value: "8 Speed Auto Trans ZF 8HP70 2WD" } },
],
},
},
};
it("unescapes \\- in the model and reads the JLR engine/transmission labels", () => {
const r = p.parseVehicleResponse("SAJAA10MXDPV41100", jlrData as never, "jaguar_parts");
expect(r.model).toBe("XJ 2010 - 2019 (X351)");
expect(r.engineType).toBe("2.0L 16V TIVCT T/C 240PS Petrol");
expect(r.transmission).toBe("8 Speed Auto Trans ZF 8HP70 2WD");
expect(r.year).toBe(2012);
});
});
describe("PL24Service.parseVehicleResponse — p5toyota (parens engine code + whitespace collapse)", () => {
const toyotaData = {
resultStatus: "VEHICLE_IDENTIFIED",
description: "COROLLA (TUP)",
link: {
path: "/p5toyota/extern/groups/vin/maingroups?lang=tr&serviceName=toyota_parts&vin=NMTBD3BEX0R033932",
},
segments: {
vinfoBasic: {
records: [
{ values: { description: "Sasi numarasi", value: "NMTBD3BEX0R033932" } },
{ values: { description: "Model", value: "COROLLA (TUP)" } },
{ values: { description: "Üretim tarihi", value: "2023\\-11\\-29" } },
{ values: { description: "ENGINE 1", value: "1800CC 16\\-VALVE DOHC EFI (2ZRFXE)" } },
{
values: { description: "ATM,MTM", value: "CONTINUOUSLY VARIABLE TRANSMISSION (CVT)" },
},
],
},
},
};
it("collapses padded whitespace and extracts the engine code from parens", () => {
const r = p.parseVehicleResponse("NMTBD3BEX0R033932", toyotaData as never, "toyota_parts");
expect(r.model).toBe("COROLLA (TUP)");
expect(r.engineType).toBe("1800CC 16-VALVE DOHC EFI (2ZRFXE)");
expect(r.engineCode).toBe("2ZRFXE");
expect(r.transmission).toBe("CONTINUOUSLY VARIABLE TRANSMISSION (CVT)");
expect(r.productionDate).toBe("2023-11-29");
expect(r.year).toBe(2023);
});
});
describe("PL24Service.parseVehicleResponse — p5man / p5suzuki (brand engine/transmission labels)", () => {
const manData = {
resultStatus: "VEHICLE_IDENTIFIED",
description: "TGS 44T 8X4 BB C3",
link: {
path: "/p5man/extern/groups/main-vin?lang=tr&serviceName=man_parts&vin=WMA39EZZ8NP178144",
},
segments: {
vinfoBasic: {
records: [
{ values: { description: "Araç tip tanımı", value: "TGS 44T 8X4 BB C3" } },
{ values: { description: "Sasi numarasi", value: "WMA39EZZ8NP178144" } },
{ values: { description: "Yedek motor", value: "D 2676 LF80" } },
{ values: { description: "Şanzıman", value: "939271" } },
],
},
},
};
it("reads MAN 'Yedek motor' + 'Şanzıman'", () => {
const r = p.parseVehicleResponse("WMA39EZZ8NP178144", manData as never, "man_parts");
expect(r.model).toBe("TGS 44T 8X4 BB C3");
expect(r.engineType).toBe("D 2676 LF80");
expect(r.transmission).toBe("939271");
});
const suzukiData = {
resultStatus: "VEHICLE_IDENTIFIED",
description: "VITARA (APK416)",
link: {
path: "/p5suzuki/extern/groups/vin/maingroups?lang=tr&serviceName=suzuki_parts&vin=TSMLYE21S00413589",
},
segments: {
vinfoBasic: {
records: [
{ values: { description: "Sasi numarasi", value: "TSMLYE21S00413589" } },
{ values: { description: "Model", value: "VITARA (APK416)" } },
{ values: { description: "Üretim tarihi", value: "2017\\-09" } },
{ values: { description: "Motor No.", value: "M16A\\-2195122" } },
{ values: { description: "Şanzıman numarası", value: "17F4556996" } },
],
},
},
};
it("reads Suzuki 'Motor No.' + 'Şanzıman numarası' and unescapes the date", () => {
const r = p.parseVehicleResponse("TSMLYE21S00413589", suzukiData as never, "suzuki_parts");
expect(r.model).toBe("VITARA (APK416)");
expect(r.engineType).toBe("M16A-2195122");
expect(r.transmission).toBe("17F4556996");
expect(r.productionDate).toBe("2017-09");
expect(r.year).toBe(2017);
});
});
describe("PL24Service.parseVehicleResponse — p5mitsubishi (model from 'Araç', not the internal code)", () => {
const mmcData = {
resultStatus: "VEHICLE_IDENTIFIED",
description: "L200(EUR/MMTH)",
link: {
path: "/p5mitsubishi/extern/groups/vinMainGroups?lang=tr&serviceName=mmc_parts&vin=MMBJNKB407D031498",
},
segments: {
vinfoBasic: {
records: [
{ values: { description: "Sasi numarasi", value: "MMBJNKB407D031498" } },
{ values: { description: "Araç", value: "L200(EUR/MMTH)" } },
{ values: { description: "Model", value: "KB4T 2500DIESEL/4WD(TRUCK)" } },
{ values: { description: "Üretim tarihi", value: "15.07.2006" } },
],
},
},
};
it("uses 'Araç' (L200), not the internal 'Model' code (KB4T …)", () => {
const r = p.parseVehicleResponse("MMBJNKB407D031498", mmcData as never, "mmc_parts");
expect(r.model).toBe("L200");
expect(r.year).toBe(2006);
});
});

View File

@@ -1014,7 +1014,17 @@ export class PL24Service {
const value = (v.key !== undefined ? v.description : v.value) || "";
if (!label) continue;
const key = label.toLowerCase().replace(/[\s\/]+/g, "_");
if (!(key in vehicleData)) vehicleData[key] = value.replace(/\r?\n/g, " ").trim();
if (!(key in vehicleData)) {
// Normalize like prNr col3: newlines→space, unescape the literal "\-" some P5
// backends emit (JLR "XJ 2010 \- 2019", Toyota/Suzuki dates "2023\-11\-29"/"2005\-07"),
// and collapse runs of whitespace (Toyota "COROLLA (TUP)"). Otherwise the raw
// escape/padding leaks straight to the UI.
vehicleData[key] = value
.replace(/\r?\n/g, " ")
.replace(/\\-/g, "-")
.replace(/\s+/g, " ")
.trim();
}
}
// Helper: look up by multiple possible keys (EN + TR)
@@ -1046,11 +1056,24 @@ export class PL24Service {
.replace("?", "?scope=F&subAggregate=n-r&");
}
// Engine code: vinfoBasic "Motor kodu" / "Engine code"
// Engine code: VAG/BMW "Motor kodu" / "Engine code".
const engineCode = lookup("motor_kodu", "engine_code");
// Non-VAG P5 OEMs label the engine differently and give a description (sometimes with a
// code in parens): JLR "Motor Tipi", Toyota "ENGINE 1", MAN "Yedek motor", Suzuki "Motor No.".
const engineLabel = lookup("motor_tipi", "engine_1", "yedek_motor", "motor_no.");
// Transmission code: vinfoBasic "Şanzıman kodu" / "Transmission code"
const transmissionCode = lookup("şanzıman_kodu", "sanzıman_kodu", "transmission_code");
// Transmission: VAG "Şanzıman kodu"; other OEMs use their own labels — JLR "Vites Kutusu",
// Toyota "ATM,MTM" (key "atm,mtm" — only spaces/slashes are underscored), MAN "Şanzıman",
// Suzuki "Şanzıman numarası".
const transmissionCode = lookup(
"şanzıman_kodu",
"sanzıman_kodu",
"transmission_code",
"vites_kutusu",
"atm,mtm",
"şanzıman",
"şanzıman_numarası",
);
// Build body type from prNr K8* (Kaporta formları); brands without a prNr segment
// (e.g. BMW) carry it in vinfoBasic "Karoseri" ("Limousine").
@@ -1075,10 +1098,16 @@ export class PL24Service {
// Friendly model. p5fiat uses "Model bilgisi" ("Panda POP 1.2 …"); most P5 backends use
// "Model". BMW's "Model" is only the trim ("520i") with the chassis/generation in "Seri"
// ("5 G30") — fold the generation in so parts aren't ambiguous across chassis (E60/F10/G30).
const baseModel =
lookup("model_bilgisi", "model")?.trim() ||
(data.description as string)?.split(" - ")[0]?.trim() ||
"";
// Mitsubishi's "Model" is an internal chassis/engine code ("KB4T 2500DIESEL/4WD(TRUCK)");
// the friendly name is in "Araç" / description ("L200(EUR/MMTH)") — strip the region suffix.
const isMitsubishi = getServiceApiPath(serviceName) === "/p5mitsubishi";
const baseModel = isMitsubishi
? (lookup("araç") || (data.description as string) || lookup("model") || "")
.replace(/\s*\([^)]*\)\s*$/, "")
.trim()
: lookup("model_bilgisi", "model")?.trim() ||
(data.description as string)?.split(" - ")[0]?.trim() ||
"";
// "Seri" is "{line} {chassis} [{variant}]" ("5 G30", "X3 G01", "5 E60 MUE"). The trim already
// implies the line ("520i"→5, "X3 sDrive20i"→X3), so drop the leading line token and append
// only the chassis(+variant) → "520i G30", "X3 sDrive20i G01" (avoids a redundant "X3 X3").
@@ -1108,8 +1137,12 @@ export class PL24Service {
0,
series: lookup("seri", "satis_tipi", "sales_type"),
bodyType,
engineCode: engineCode || (engineDesc ? engineDesc.split("/")[0]?.trim() : null),
engineType: engineDesc,
engineCode:
engineCode ||
engineLabel?.match(/\(([A-Za-z0-9]{3,})\)/)?.[1] || // Toyota "1800CC … (2ZRFXE)"
(engineDesc ? engineDesc.split("/")[0]?.trim() : null) ||
null,
engineType: engineDesc || engineLabel,
engineVolume: null,
transmission: transmissionCode || transmissionDesc,
driveType,