fix(pl24): correct PSA VIN decode via FI/VIN-indexed flow + cycle-correct model year
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
PSA (Peugeot/Citroën/DS) VIN decode was systemically broken: the catalog
vin-group page returns all families unfiltered, so decode fell back to the first
family/salesType (a manual base variant) — yielding "{Brand} {VIN}" model names,
empty transmission, wrong model year, and manual-only parts trees (automatic
gearbox parts missing). Reported for a 1999 Peugeot 106 automatic shown as a 2029
manual with no automatic parts.
- New self-contained PL24PsaService: consumes PL24's FI flow (vin.action →
hintstoken → FI page → json-vin-main-groups → json-vin-illustrations →
vin-image-board). Reads model/year/transmission from the FI identification
table; builds the VIN-indexed parts tree (correct per actual VIN). Does not
touch Ford/Volvo/Nissan/Opel/Hyundai-Kia/Fiat.
- Orchestrator + categories.service route PSA VIN decode/drill to the new service.
- Cycle-correct extractModelYear in @sase/shared (X→1999, not 2029): resolve the
30-yr VIN year code to the most-recent plausible year (≤ now+1); dedupe 6 copies.
Validated live against 13 already-decoded PSA VINs: 12/13 full trees with real
model/year/transmission; automatics correctly detected (106 BVA, 206 AL4, 3008 BVA8).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -24,6 +24,11 @@ function createService(db: any) {
|
||||
};
|
||||
const pl24FordLegacyService = {
|
||||
fetchCategoriesForPsaVin: vi.fn().mockResolvedValue([]),
|
||||
fetchPsaParts: vi.fn().mockResolvedValue({ success: true, parts: [] }),
|
||||
};
|
||||
const pl24PsaService = {
|
||||
fetchCategoriesForPsaVin: vi.fn().mockResolvedValue([]),
|
||||
fetchVinParts: vi.fn().mockResolvedValue({ success: true, parts: [] }),
|
||||
};
|
||||
const translationsService = {
|
||||
translate: vi
|
||||
@@ -47,6 +52,7 @@ function createService(db: any) {
|
||||
partsCatalogsService as any,
|
||||
storage as any,
|
||||
pl24FordLegacyService as any,
|
||||
pl24PsaService as any,
|
||||
translationsService as any,
|
||||
pcatSourceDb as any,
|
||||
emexSourceDb as any,
|
||||
|
||||
@@ -8,6 +8,7 @@ import { EmexService } from "../integrations/emex/emex.service";
|
||||
import { PartsCatalogsService } from "../integrations/parts-catalogs/parts-catalogs.service";
|
||||
import { PcatGroup } from "../integrations/parts-catalogs/parts-catalogs.types";
|
||||
import { PL24FordLegacyService } from "../integrations/pl24/pl24-ford-legacy.service";
|
||||
import { PL24PsaService } from "../integrations/pl24/pl24-psa.service";
|
||||
import { PL24Service } from "../integrations/pl24/pl24.service";
|
||||
import { RedisService } from "../redis/redis.service";
|
||||
import { StorageService } from "../storage/storage.service";
|
||||
@@ -25,6 +26,7 @@ export class CategoriesService {
|
||||
private partsCatalogsService: PartsCatalogsService,
|
||||
private storage: StorageService,
|
||||
private pl24FordLegacyService: PL24FordLegacyService,
|
||||
private pl24PsaService: PL24PsaService,
|
||||
private translationsService: TranslationsService,
|
||||
private pcatSourceDb: PcatSourceDbService,
|
||||
private emexSourceDb: EmexSourceDbService,
|
||||
@@ -103,10 +105,7 @@ export class CategoriesService {
|
||||
const svcName: string = rawData?.catalogInfo?.serviceName ?? "";
|
||||
if (catPath.startsWith("/psa/") && svcName && vehicle.vin) {
|
||||
try {
|
||||
const scopes = await this.pl24FordLegacyService.fetchCategoriesForPsaVin(
|
||||
svcName,
|
||||
vehicle.vin,
|
||||
);
|
||||
const scopes = await this.pl24PsaService.fetchCategoriesForPsaVin(svcName, vehicle.vin);
|
||||
if (scopes.length > 0) {
|
||||
const insertData = scopes.map((s) => ({
|
||||
vehicleId,
|
||||
@@ -1298,16 +1297,26 @@ export class CategoriesService {
|
||||
const isPsaBoard =
|
||||
category.linkPath.includes("/psa/") &&
|
||||
category.linkPath.includes("image-board.action");
|
||||
// VIN-indexed leaves (vin-image-board.action) → dedicated PSA service.
|
||||
const isPsaVinBoard =
|
||||
category.linkPath.includes("/psa/") &&
|
||||
category.linkPath.includes("vin-image-board.action");
|
||||
if (needImage && isPsaBoard && !pl24Result.schemaImageBuffer) {
|
||||
try {
|
||||
const freshResult = await this.pl24FordLegacyService.fetchPsaParts(
|
||||
category.linkPath,
|
||||
catalogInfo.serviceName,
|
||||
"_all_",
|
||||
"_all_",
|
||||
"_all_",
|
||||
true,
|
||||
);
|
||||
const freshResult = isPsaVinBoard
|
||||
? await this.pl24PsaService.fetchVinParts(
|
||||
category.linkPath,
|
||||
catalogInfo.serviceName,
|
||||
true,
|
||||
)
|
||||
: await this.pl24FordLegacyService.fetchPsaParts(
|
||||
category.linkPath,
|
||||
catalogInfo.serviceName,
|
||||
"_all_",
|
||||
"_all_",
|
||||
"_all_",
|
||||
true,
|
||||
);
|
||||
if (freshResult.schemaImageBuffer) {
|
||||
(pl24Result as any).schemaImageBuffer = freshResult.schemaImageBuffer;
|
||||
(pl24Result as any).schemaImageContentType = freshResult.schemaImageContentType;
|
||||
|
||||
Reference in New Issue
Block a user