diff --git a/apps/api/src/jobs/prefetch-worker.service.ts b/apps/api/src/jobs/prefetch-worker.service.ts index dbaf17d..d66fa52 100644 --- a/apps/api/src/jobs/prefetch-worker.service.ts +++ b/apps/api/src/jobs/prefetch-worker.service.ts @@ -247,7 +247,7 @@ export class PrefetchWorkerService implements OnModuleInit, OnModuleDestroy { await this.queueCategoryJob(child, vehicleId, source, 1); queued++; } - } else if (this.isLeafLinkPath(cat.linkPath, cat.source)) { + } else if (this.isLeafLinkPath(cat.linkPath, cat.source, cat.hasSubgroups)) { // Leaf — check if parts already fetched const [partCheck] = await this.db .select({ id: parts.id }) @@ -488,14 +488,20 @@ export class PrefetchWorkerService implements OnModuleInit, OnModuleDestroy { // ==================== Helpers ==================== private async queueCategoryJob( - cat: { id: string; linkPath: string | null; source: string; unavailable: boolean }, + cat: { + id: string; + linkPath: string | null; + source: string; + unavailable: boolean; + hasSubgroups?: boolean | null; + }, vehicleId: string, source: string, depth: number, ): Promise { if (cat.unavailable) return; - if (this.isLeafLinkPath(cat.linkPath, cat.source)) { + if (this.isLeafLinkPath(cat.linkPath, cat.source, cat.hasSubgroups)) { // Leaf — check if already has parts const [partCheck] = await this.db .select({ id: parts.id }) @@ -543,12 +549,25 @@ export class PrefetchWorkerService implements OnModuleInit, OnModuleDestroy { } } - private isLeafLinkPath(linkPath: string | null, source: string): boolean { + private isLeafLinkPath( + linkPath: string | null, + source: string, + hasSubgroups?: boolean | null, + ): boolean { if (!linkPath) return false; // EMEX: Vehicle.aspx group nodes are parents to drill; Unit.aspx (hierarchical // tree) and QuickDetails.aspx (legacy flat) leaves carry parts. if (source === "emex") return !linkPath.includes("Vehicle.aspx"); - if (source === "parts-catalogs") return linkPath.startsWith("pcat:"); // pcat: prefix = leaf + // parts-catalogs: EVERY node has a pcat: linkPath, so the old "pcat: prefix = + // leaf" rule mis-flagged every mid-group folder as a leaf — the worker fetched + // its "parts" (a 1-level drill that just revealed sub-groups) and never + // recursed, leaving deep pcat trees a single level shallow (no parts → empty + // part counts, dead cross-tree search). Use the captured hasSubgroups flag + // instead: a node is a parts leaf only when it is NOT a known parent group, so + // folders queue a children job and the recursion drills to full depth. Unknown + // flag (null, rare pre-migration rows) → treated as leaf, preserving the old + // 1-level behaviour for those. + if (source === "parts-catalogs") return hasSubgroups !== true; // PL24 leaf indicators return ( linkPath.includes("/bom/") ||