diff --git a/src/services/companyImport/categoryImporter.js b/src/services/companyImport/categoryImporter.js index cc6e1ae..8f88d68 100644 --- a/src/services/companyImport/categoryImporter.js +++ b/src/services/companyImport/categoryImporter.js @@ -55,15 +55,48 @@ export async function importCategories({ catImagesByJtlId.get(task.catJtlId).push(task); } - const pendingCats = [...includedCategories]; + function parseSortValue(val) { + if (val == null || val === '') return 0; + const n = Number(val); + return Number.isFinite(n) ? n : 0; + } + + const pendingCats = includedCategories.map((c, idx) => ({ ...c, _csvIndex: idx })); let createdCatCount = 0; let catPicCount = 0; let safety = 0; + const importedMetaByCatId = new Map(); + const touchedParentIds = new Set(); + while (pendingCats.length > 0 && safety < 10) { safety++; + const readyCats = []; const remaining = []; + for (const c of pendingCats) { + const jtlParent = String(c.parentid ?? c.kOberKategorie ?? '0'); + if (jtlParent === '0' || !jtlParent || jtlCatIdToDashCatId.has(jtlParent)) { + readyCats.push(c); + } else { + remaining.push(c); + } + } + + // Group by parent and order siblings numerically by CSV sort column, maintaining CSV order on ties + readyCats.sort((a, b) => { + const parentA = String(a.parentid ?? a.kOberKategorie ?? '0'); + const parentB = String(b.parentid ?? b.kOberKategorie ?? '0'); + if (parentA !== parentB) { + return parentA.localeCompare(parentB); + } + const sortA = parseSortValue(a.sort ?? a.nSort); + const sortB = parseSortValue(b.sort ?? b.nSort); + if (sortA !== sortB) return sortA - sortB; + return a._csvIndex - b._csvIndex; + }); + + for (const c of readyCats) { const jtlId = String(c.id ?? c.kKategorie); const jtlParent = String(c.parentid ?? c.kOberKategorie ?? '0'); @@ -106,6 +139,11 @@ export async function importCategories({ if (dashCatId) { jtlCatIdToDashCatId.set(jtlId, dashCatId); + importedMetaByCatId.set(dashCatId, { + sort: parseSortValue(c.sort ?? c.nSort), + csvIndex: c._csvIndex, + }); + touchedParentIds.add(dashParentId); // Add category images const imgs = catImagesByJtlId.get(jtlId) || []; @@ -131,6 +169,43 @@ export async function importCategories({ pendingCats.push(...remaining); } + // Explicit sibling reordering pass using store.category.order + if (touchedParentIds.size > 0 && typeof store.category?.order === 'function') { + try { + const treeAfter = await store.category.getTreeForCompany(googleAuthId); + const allNodes = treeAfter.tree || []; + + for (const parentId of touchedParentIds) { + const siblings = allNodes.filter((node) => node.parentId === parentId); + if (siblings.length <= 1) continue; + + siblings.sort((a, b) => { + const metaA = importedMetaByCatId.get(a.id); + const metaB = importedMetaByCatId.get(b.id); + const sortA = metaA ? metaA.sort : Infinity; + const sortB = metaB ? metaB.sort : Infinity; + if (sortA !== sortB) return sortA - sortB; + const idxA = metaA ? metaA.csvIndex : (a.sort ?? 0); + const idxB = metaB ? metaB.csvIndex : (b.sort ?? 0); + return idxA - idxB; + }); + + const categoryIds = siblings.map((s) => s.id); + try { + await store.category.order({ + googleAuthId, + parentId, + categoryIds, + }); + } catch (orderErr) { + log.warn(`Failed to apply category order for parent ${parentId}:`, orderErr.message); + } + } + } catch (err) { + log.warn('Failed to fetch tree for category reordering:', err.message); + } + } + progressTracker.flushSkeleHashes(); progressTracker.emitProgress(true); log.info(`Categories resolved: ${jtlCatIdToDashCatId.size}/${includedCategories.length}, created: ${createdCatCount}, images: ${catPicCount}`);