feat(vinpin): Renault/Dacia decode akışı (Rpartstore + Dialogys)
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled

VINPIN köprüsüne Fiat'ın yanına Renault/Dacia desteği eklendi.

- Marka yönlendirme: selectVinpinBrandFlow(vin) WMI'den akışı seçer
  (Renault/Dacia → Rpartstore/Dialogys, geri kalan → mevcut Fiat ePER,
  Fiat davranışı byte-identical). isVinpinBrandAllowed allowlist'i
  fiat + renault + dacia'ya genişletildi.
- Renault akışı durum-toleranslı: koltuk son katalogu (Rpartstore)
  sunucu-taraflı hatırlayıp açık resume ediyor; ensureRpartstore grid /
  submenu / açık-pencere / yükleniyor durumlarını tanıyıp kendini
  toparlıyor. Rpartstore ana akış, Dialogys best-effort fallback.
- Ortak ensureBrandGrid artık resume olmuş yabancı katalog penceresini
  (Rpartstore/Dialogys/ePER) kapatıp grid'e dönüyor → Renault↔Fiat
  ardışık decode'ları (tek koltuk) artık kırılmıyor.
- parseRenaultHeader/isUsableRenaultParse: OCR başlığından model+marka
  (RENAULT/DACIA) + yıl; bilinen-token allowlist ile contiguous model
  koşusu. Matcher + processor kararlaştırılan markaya göre PL24
  catalog_vehicle eşliyor (Fiat varsayılan korunur).
- Testler: Renault parser + marka-yönlendirme/allowlist birim testleri.

Flag-gated (VINPIN_ENABLED). Canlı doğrulama: 5/5 Renault/Dacia VIN
(Kadjar, Clio II, Clio IV, Dacia Duster, Latitude) doğru decode; Fiat
Egea spot-check korunuyor.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-14 08:44:00 +03:00
parent 24716d03a9
commit 77c93af9a0
7 changed files with 681 additions and 35 deletions

View File

@@ -53,7 +53,9 @@ export async function processVinpinDecode(
return { status: "not_found", catalogVehicleId: null };
}
// Match the decoded model to an existing PL24 Fiat catalog_vehicle.
// Match the decoded model to an existing PL24 catalog_vehicle of the SAME
// brand. Fiat → 'fiat'; Renault/Dacia → the decoded brand (RENAULT/DACIA).
const brandName = decoded.brand ?? "Fiat";
const rows = await db
.select({
id: catalogVehicles.id,
@@ -62,7 +64,10 @@ export async function processVinpinDecode(
})
.from(catalogVehicles)
.where(
and(sql`lower(${catalogVehicles.brandName}) = 'fiat'`, eq(catalogVehicles.source, "pl24")),
and(
sql`lower(${catalogVehicles.brandName}) = ${brandName.toLowerCase()}`,
eq(catalogVehicles.source, "pl24"),
),
);
const catalogVehicleId = pickBestCatalogMatch(
@@ -74,7 +79,7 @@ export async function processVinpinDecode(
.update(vinpinDecodes)
.set({
status: "decoded",
brandName: "Fiat",
brandName,
model: decoded.model,
sincom: decoded.sincom,
trim: decoded.trim,