fix(pl24): Volvo alanlarında iç kodu değil okunur değeri kullan
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled

Volvo vinfoBasic aynı özniteliği iki kez veriyor: "Şanzıman kodu" = "B"
ile "Şanzıman" = "6-PSHIFT 2WD / MPS6", "Satis tipi" = "42" ile "Türü"
= "S80". VAG için doğru olan kod-önce sırası (VAG'ın "Şanzıman kodu"
zaten anlamlı: "MQ200") bu yüzden Volvo'da şanzımanı tek harf, seriyi
çıplak sayı yapıyordu.

`lookupDescriptive()` eklendi: listedeki ilk *açıklayıcı* değeri döner
(2 karakterden uzun ve salt rakam değil), hiçbiri açıklayıcı değilse
ilk mevcut değere düşer — yani yalnız kod üreten backend'ler aynen
eskisi gibi davranır. Şanzıman ve seri bu yardımcıya geçti.

Ayrıca eksik İngilizce etiketler eklendi: `transmission` (İngilizce
yanıtta şanzıman yine tek harfe düşüyordu) ve `body_style`.

Ölçüm (gerçek keşif yakalamaları, YV1AS84ABD1168166 S80):
- şanzıman "B" → "6-PSHIFT 2WD / MPS6"
- seri "42" → "S80"
- kaporta İngilizce yanıtta null → "Sedan"
- PSA İngilizce yanıtta şanzıman null → "BVM5"
PSA Türkçe, Subaru ve diğer markalarda çıktı değişmedi.

6 yeni test + iki dilde gerçek Volvo fixture'ı. api 617 test geçiyor.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-20 08:45:16 +03:00
parent 0928559ea4
commit 72f8c2a3e4
4 changed files with 502 additions and 3 deletions

View File

@@ -0,0 +1,198 @@
{
"link": {
"wid": "mainGroupTable",
"path": "/p5volvo/extern/groups/vin/mainGroup?lang=en&model=308&modelYear=1620&partnerGroup=46&serviceName=volvo_parts&upds=2026-08-24--11-02&vin=YV1AS84ABD1168166"
},
"segments": {
"vinfoBasic": {
"records": [
{
"values": {
"description": "Vehicle Identification No.",
"value": "YV1AS84ABD1168166"
}
},
{
"values": {
"description": "Year",
"value": "2013"
}
},
{
"values": {
"description": "Model",
"value": "S80 (07\\-)"
}
},
{
"values": {
"description": "Km/h or mph",
"value": "K"
}
},
{
"values": {
"description": "Factory code",
"value": "21"
}
},
{
"values": {
"description": "Steering gear prod no",
"value": "31360538"
}
},
{
"values": {
"description": "Structure week",
"value": "201236"
}
},
{
"values": {
"description": "Type",
"value": "S80"
}
},
{
"values": {
"description": "Chassis",
"value": "168166"
}
},
{
"values": {
"description": "Partner group",
"value": "Europe"
}
},
{
"values": {
"description": "Upholstery/interior code",
"value": "210100"
}
},
{
"values": {
"description": "Upholstery/interior",
"value": "LEATHER/ANTHR/QRTZCEIL/NV"
}
},
{
"values": {
"description": "Exterior color",
"value": "61400"
}
},
{
"values": {
"description": "Exterior color",
"value": "WHITE SOLID ICE WHITE"
}
},
{
"values": {
"description": "Body style code",
"value": "0"
}
},
{
"values": {
"description": "Body style",
"value": "Sedan"
}
},
{
"values": {
"description": "Special vehicle code",
"value": " "
}
},
{
"values": {
"description": "Special vehicles",
"value": " "
}
},
{
"values": {
"description": "Sales type",
"value": "42"
}
},
{
"values": {
"description": "Sales type",
"value": "SALES VERSION 42"
}
},
{
"values": {
"description": "Market code",
"value": "49"
}
},
{
"values": {
"description": "Market",
"value": "TR"
}
},
{
"values": {
"description": "Engine Code",
"value": "84"
}
},
{
"values": {
"description": "Engine",
"value": "D4162T"
}
},
{
"values": {
"description": "Engine part no",
"value": "6906309"
}
},
{
"values": {
"description": "Engine serial no",
"value": "00000000000004138845 / 0ELD61 2208122233587"
}
},
{
"values": {
"description": "Transmission Code",
"value": "B"
}
},
{
"values": {
"description": "Transmission",
"value": "6\\-PSHIFT 2WD / MPS6"
}
},
{
"values": {
"description": "Transmission part no",
"value": "1285041"
}
},
{
"values": {
"description": "Transmission serial no",
"value": "00AWBB1 170812170654"
}
},
{
"values": {
"description": "Chassis code",
"value": "35659B7A6276"
}
}
]
}
}
}

View File

@@ -0,0 +1,198 @@
{
"link": {
"wid": "mainGroupTable",
"path": "/p5volvo/extern/groups/vin/mainGroup?lang=tr&model=308&modelYear=1620&partnerGroup=46&serviceName=volvo_parts&upds=2026-08-24--11-02&vin=YV1AS84ABD1168166"
},
"segments": {
"vinfoBasic": {
"records": [
{
"values": {
"description": "Sasi numarasi",
"value": "YV1AS84ABD1168166"
}
},
{
"values": {
"description": "Model yili",
"value": "2013"
}
},
{
"values": {
"description": "Model",
"value": "S80 (07\\-)"
}
},
{
"values": {
"description": "Hız Birimi",
"value": "K"
}
},
{
"values": {
"description": "Fabrika Kodu",
"value": "21"
}
},
{
"values": {
"description": "Direksiyon Kutusu Üretim No",
"value": "31360538"
}
},
{
"values": {
"description": "Üretim Haftası",
"value": "201236"
}
},
{
"values": {
"description": "Türü",
"value": "S80"
}
},
{
"values": {
"description": "Şasi",
"value": "168166"
}
},
{
"values": {
"description": "Ortak grubu",
"value": "Europe"
}
},
{
"values": {
"description": "Döşeme/iç mekan kodu",
"value": "210100"
}
},
{
"values": {
"description": "Döşeme",
"value": "LEATHER/ANTHR/QRTZCEIL/NV"
}
},
{
"values": {
"description": "Dis rengi",
"value": "61400"
}
},
{
"values": {
"description": "Dis rengi",
"value": "WHITE SOLID ICE WHITE"
}
},
{
"values": {
"description": "Karoseri Tipi Kodu",
"value": "0"
}
},
{
"values": {
"description": "Kaporta Stili",
"value": "Sedan"
}
},
{
"values": {
"description": "Özel Araç Kodu",
"value": " "
}
},
{
"values": {
"description": "Özel araçlar",
"value": " "
}
},
{
"values": {
"description": "Satis tipi",
"value": "42"
}
},
{
"values": {
"description": "Satis tipi",
"value": "SALES VERSION 42"
}
},
{
"values": {
"description": "Piyasa Kodu",
"value": "49"
}
},
{
"values": {
"description": "Market",
"value": "TR"
}
},
{
"values": {
"description": "Motor kodu",
"value": "84"
}
},
{
"values": {
"description": "Motor",
"value": "D4162T"
}
},
{
"values": {
"description": "Motor Parça No",
"value": "6906309"
}
},
{
"values": {
"description": "Motor Seri Numarası",
"value": "00000000000004138845 / 0ELD61 2208122233587"
}
},
{
"values": {
"description": "Şanzıman kodu",
"value": "B"
}
},
{
"values": {
"description": "Şanzıman",
"value": "6\\-PSHIFT 2WD / MPS6"
}
},
{
"values": {
"description": "Şanzıman Parça No",
"value": "1285041"
}
},
{
"values": {
"description": "Şanzıman Seri No",
"value": "00AWBB1 170812170654"
}
},
{
"values": {
"description": "Şasi Kodu",
"value": "35659B7A6276"
}
}
]
}
}
}

View File

@@ -0,0 +1,68 @@
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { describe, expect, it } from "vitest";
import { PL24Service } from "./pl24.service";
/**
* Volvo P5 vinfoBasic regression lock (plv2.md, finding p5core-09).
*
* Volvo emits BOTH a bare internal code and a readable description for the same
* attribute — "Şanzıman kodu" = "B" next to "Şanzıman" = "6-PSHIFT 2WD / MPS6",
* and "Satis tipi" = "42" next to "Türü" = "S80". The key order the parser needs
* for VAG (whose "Şanzıman kodu" IS the useful value, e.g. "MQ200") therefore
* rendered a single letter as the gearbox and a bare number as the series.
*
* Fixtures are the real 2026-09-16 discovery captures for VIN
* YV1AS84ABD1168166 (S80), trimmed to the segments the parser reads, in both
* the Turkish and the English label locale.
*/
const fixture = (name: string) =>
JSON.parse(readFileSync(join(__dirname, "__fixtures__", `${name}.json`), "utf-8"));
// parseVehicleResponse only touches the response and the service name.
const parse = (data: unknown, serviceName = "volvo_parts") => {
const svc = Object.create(PL24Service.prototype) as unknown as {
parseVehicleResponse: (v: string, d: unknown, s: string) => Record<string, unknown>;
isDaimlerService: (s: string) => boolean;
};
svc.isDaimlerService = () => false;
return svc.parseVehicleResponse("YV1AS84ABD1168166", data, serviceName);
};
describe("Volvo P5 vinfoBasic — kod yerine açıklama", () => {
it("Türkçe etiketlerde şanzımanı okunur değerinden alır", () => {
const out = parse(fixture("p5_volvo_tr"));
expect(out.transmission).toBe("6-PSHIFT 2WD / MPS6");
// Tek harflik "Şanzıman kodu" artık kazanmıyor.
expect(out.transmission).not.toBe("B");
});
it("İngilizce etiketlerde de aynı sonucu verir", () => {
const out = parse(fixture("p5_volvo_en"));
expect(out.transmission).toBe("6-PSHIFT 2WD / MPS6");
});
it("seri, çıplak satış kodu yerine gerçek tipi döner", () => {
expect(parse(fixture("p5_volvo_tr")).series).toBe("S80");
expect(parse(fixture("p5_volvo_en")).series).toBe("S80");
expect(parse(fixture("p5_volvo_tr")).series).not.toBe("42");
});
it("kaporta stili iki dilde de çözülür ve '0' kodu sızmaz", () => {
expect(parse(fixture("p5_volvo_tr")).bodyType).toBe("Sedan");
expect(parse(fixture("p5_volvo_en")).bodyType).toBe("Sedan");
});
it("motor alanları bozulmadan kalır", () => {
const out = parse(fixture("p5_volvo_tr"));
expect(out.engineCode).toBe("84");
expect(out.engineType).toBe("D4162T");
});
it("model ve yıl korunur", () => {
const out = parse(fixture("p5_volvo_tr"));
expect(out.model).toBe("S80 (07-)");
expect(out.year).toBe(2013);
});
});

View File

@@ -1184,6 +1184,31 @@ export class PL24Service {
return null;
};
/**
* Like `lookup`, but prefers a human-readable value over a bare internal
* code when the record carries both.
*
* Volvo's vinfoBasic has BOTH "Şanzıman kodu" ("B") and "Şanzıman"
* ("6-PSHIFT 2WD / MPS6"), and BOTH "Satis tipi" ("42") and a second
* "Satis tipi" ("SALES VERSION 42"). The code-first key order that VAG needs
* (its "Şanzıman kodu" IS the useful value, e.g. "MQ200") therefore rendered
* a single letter as the Volvo gearbox and a bare number as its series.
*
* Falls back to the first present value, so a backend that only ever emits
* codes behaves exactly as before.
*/
const lookupDescriptive = (...keys: string[]): string | null => {
let firstPresent: string | null = null;
for (const k of keys) {
const v = vehicleData[k]?.trim();
if (!v) continue;
if (firstPresent === null) firstPresent = v;
// Two characters or fewer, or digits only → an index, not a description.
if (v.length > 2 && !/^\d+$/.test(v)) return v;
}
return firstPresent;
};
// Extract prNr records for richer vehicle attributes
const prNrRecords = segments.prNr?.records || [];
const prNrByCode: Record<string, string> = {};
@@ -1226,13 +1251,17 @@ export class PL24Service {
// normalizeLabel folds ı→i and strips diacritics, so "Şanzıman kodu" and
// "ŞANZIMAN KODU" both arrive as "sanziman_kodu". PSA uses "AKTARMA
// SİSTEMLERİ" ("5 MEKANİK VİTES KUTUSU"), Subaru "Mission".
const transmissionCode = lookup(
const transmissionCode = lookupDescriptive(
"sanziman_kodu",
"transmission_code",
"vites_kutusu",
"atm,mtm",
"aktarma_sistemleri",
"sanziman",
// Volvo in the English locale: "Transmission code" ("B") plus the readable
// "Transmission" ("6-PSHIFT 2WD / MPS6"). Without the plain key the
// English response falls back to the single-letter code.
"transmission",
"sanziman_numarasi",
"mission",
);
@@ -1242,7 +1271,10 @@ export class PL24Service {
const bodyType =
Object.entries(prNrByCode).find(([code]) => code.startsWith("K8"))?.[1] ||
// PSA "GÖVDE TİPİ" ("4 KAPILI SEDAN"), Volvo "Kaporta Stili" ("Sedan").
lookup("karoseri", "body", "body_type", "govde_tipi", "kaporta_stili") ||
// Volvo: TR "Kaporta Stili" / EN "Body style" ("Sedan"). Its
// "Karoseri Tipi Kodu" / "Body style code" is a bare "0" — never matched
// here because the lookup is exact-key, and that is deliberate.
lookup("karoseri", "body", "body_type", "govde_tipi", "kaporta_stili", "body_style") ||
null;
// Engine description from prNr D3* (Motor nitelikleri)
@@ -1301,7 +1333,10 @@ export class PL24Service {
damToModelYear(lookup("dam")) ||
extractModelYear(vin) ||
0,
series: lookup("seri", "satis_tipi", "sales_type", "turu"),
// Volvo's "Satis tipi" is the bare sales code ("42") with the readable
// form ("SALES VERSION 42") in a duplicate row, and its "Türü" is the real
// series ("S80") — so prefer whichever of these is actually descriptive.
series: lookupDescriptive("seri", "satis_tipi", "sales_type", "turu", "type"),
bodyType,
engineCode:
engineCode ||