fix(pl24): enrich BMW decode — chassis/generation, body, drive from vinfoBasic
BMW (p5bmw) decode was weak: model was just the trim ("520i") with no
chassis/generation, body_type empty, because BMW has NO prNr segment and keeps
that data in distinct vinfoBasic labels the shared parser ignored. Live-verified
fields: "Seri"="5 G30" (chassis), "Karoseri"="Limousine" (body), "Tahrik"="RWD".
- model: fold the generation ("Seri"/"Model tanimi") into the model when present
and not already included → "520i 5 G30" (disambiguates E60/F10/G30 for parts).
- bodyType: fall back to vinfoBasic "Karoseri" when there's no prNr K8*.
- series: read "Seri"; driveType: read "Tahrik".
Year already comes from "Üretim tarihi" (P5 year fallback). VW/Audi (prNr) and
Mercedes ("Piyasa adı", no seri/karoseri) verified unaffected.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -25,6 +25,9 @@ const p = svc as unknown as {
|
||||
model: string;
|
||||
year: number;
|
||||
productionDate: string | null;
|
||||
series: string | null;
|
||||
bodyType: string | null;
|
||||
engineCode: string | null;
|
||||
catalogInfo?: { mainGroupsPath?: string };
|
||||
};
|
||||
};
|
||||
@@ -115,3 +118,39 @@ describe("PL24Service.parseVehicleResponse — p5vwag shape still works (regress
|
||||
expect(r.catalogInfo?.mainGroupsPath).toContain("/p5vwag/");
|
||||
});
|
||||
});
|
||||
|
||||
describe("PL24Service.parseVehicleResponse — p5bmw (no prNr; chassis in 'Seri')", () => {
|
||||
// Real /p5bmw shape (captured live 2026-06-04): no prNr segment; the plain "Model" is only
|
||||
// the trim ("520i"), with chassis/generation in "Seri" ("5 G30") and body in "Karoseri".
|
||||
const bmwData = {
|
||||
resultStatus: "VEHICLE_IDENTIFIED",
|
||||
description: "5' G30- Limousine- CARBONSCHWARZ METALLIC (416)",
|
||||
link: { path: "/p5bmw/extern/groups/vin/maingroups?vin=WBAJA" },
|
||||
segments: {
|
||||
vinfoBasic: {
|
||||
records: [
|
||||
{ values: { description: "Model tanimi", value: "5' G30" } },
|
||||
{ values: { description: "Üretim tarihi", value: "27.09.2019" } },
|
||||
{ values: { description: "Tahrik", value: "RWD" } },
|
||||
{ values: { description: "Model", value: "520i" } },
|
||||
{ values: { description: "Seri", value: "5 G30" } },
|
||||
{ values: { description: "Karoseri", value: "Limousine" } },
|
||||
{ values: { description: "Motor kodu", value: "B48B16M0" } },
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
it("folds the chassis/generation ('Seri') into the model so it isn't ambiguous", () => {
|
||||
const r = p.parseVehicleResponse("WBAJA3100LCD26756", bmwData as never, "bmw_parts");
|
||||
expect(r.model).toBe("520i 5 G30");
|
||||
expect(r.series).toBe("5 G30");
|
||||
});
|
||||
|
||||
it("derives year from production date, body from 'Karoseri', engine from 'Motor kodu'", () => {
|
||||
const r = p.parseVehicleResponse("WBAJA3100LCD26756", bmwData as never, "bmw_parts");
|
||||
expect(r.year).toBe(2019);
|
||||
expect(r.bodyType).toBe("Limousine");
|
||||
expect(r.engineCode).toBe("B48B16M0");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1052,9 +1052,12 @@ export class PL24Service {
|
||||
// Transmission code: vinfoBasic "Şanzıman kodu" / "Transmission code"
|
||||
const transmissionCode = lookup("şanzıman_kodu", "sanzıman_kodu", "transmission_code");
|
||||
|
||||
// Build body type from prNr K8* (Kaporta formları)
|
||||
// Build body type from prNr K8* (Kaporta formları); brands without a prNr segment
|
||||
// (e.g. BMW) carry it in vinfoBasic "Karoseri" ("Limousine").
|
||||
const bodyType =
|
||||
Object.entries(prNrByCode).find(([code]) => code.startsWith("K8"))?.[1] || null;
|
||||
Object.entries(prNrByCode).find(([code]) => code.startsWith("K8"))?.[1] ||
|
||||
lookup("karoseri", "body", "body_type") ||
|
||||
null;
|
||||
|
||||
// Engine description from prNr D3* (Motor nitelikleri)
|
||||
const engineDesc =
|
||||
@@ -1064,19 +1067,27 @@ export class PL24Service {
|
||||
const transmissionDesc =
|
||||
Object.entries(prNrByCode).find(([code]) => code.startsWith("G0"))?.[1] || null;
|
||||
|
||||
// Drive type from prNr 1X* (Tahrik türü)
|
||||
// Drive type from prNr 1X* (Tahrik türü); BMW carries it in vinfoBasic "Tahrik" (RWD/xDrive).
|
||||
const driveType =
|
||||
Object.entries(prNrByCode).find(([code]) => code.startsWith("1X"))?.[1] ||
|
||||
lookup("aks_tahrigi_tanimi", "axle_drive");
|
||||
lookup("tahrik", "aks_tahrigi_tanimi", "axle_drive");
|
||||
|
||||
// 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() ||
|
||||
"";
|
||||
const generation = lookup("seri", "model_tanimi");
|
||||
const model =
|
||||
generation && baseModel && !baseModel.toLowerCase().includes(generation.toLowerCase())
|
||||
? `${baseModel} ${generation}`
|
||||
: baseModel;
|
||||
|
||||
return {
|
||||
brand: SERVICE_TO_BRAND[serviceName] || serviceName.replace("_parts", ""),
|
||||
// p5fiat carries the friendly model in "Model bilgisi" ("Panda POP 1.2 8V 69CV 5M E6");
|
||||
// its plain "Model" is a numeric code. Other P5 backends only have "model".
|
||||
model:
|
||||
lookup("model_bilgisi", "model")?.trim() ||
|
||||
(data.description as string)?.split(" - ")[0]?.trim() ||
|
||||
"",
|
||||
model,
|
||||
// p5fiat has no plain model-year field; derive it from "MY" ("… = 2014 YILI") or the
|
||||
// production date ("05/09/2014"). Other P5 backends use a plain model_yili/year number.
|
||||
year:
|
||||
@@ -1087,7 +1098,7 @@ export class PL24Service {
|
||||
) ||
|
||||
extractModelYear(vin) ||
|
||||
0,
|
||||
series: lookup("satis_tipi", "sales_type"),
|
||||
series: lookup("seri", "satis_tipi", "sales_type"),
|
||||
bodyType,
|
||||
engineCode: engineCode || (engineDesc ? engineDesc.split("/")[0]?.trim() : null),
|
||||
engineType: engineDesc,
|
||||
|
||||
Reference in New Issue
Block a user