Files
dashback/src/services/companyImport/categoryImporter.js
2026-10-12 03:10:35 +02:00

149 lines
5.0 KiB
JavaScript

// MARK: - Imports
// #region Imports
import fs from 'fs';
import { cleanValue } from './csvParser.js';
import { computeSkeleHash } from './imageHasher.js';
import { createLogger } from '../../util/logger.js';
// #endregion
const log = createLogger('import');
// MARK: - Category Importer
// #region Category Importer
/**
* Imports category tree and attaches category images.
*
* @param {object} params
* @param {object} params.store - Store singleton
* @param {number} params.googleAuthId
* @param {Array<Record<string, string>>} params.includedCategories
* @param {Array<object>} params.categoryImageTasks
* @param {object} params.progressTracker
* @returns {Promise<{ jtlCatIdToDashCatId: Map<string, number>, hauptDashId: number, createdCatCount: number, catPicCount: number }>}
*/
export async function importCategories({
store,
googleAuthId,
includedCategories,
categoryImageTasks,
progressTracker,
}) {
const treeResult = await store.category.getTreeForCompany(googleAuthId);
const existingTree = treeResult.tree || [];
const rootCategory = existingTree.find((c) => c.parentId == null) || existingTree[0];
if (!rootCategory) {
throw new Error('Company root category not found');
}
const rootCategoryId = rootCategory.id;
const existingMap = new Map();
for (const c of existingTree) {
const key = `${c.parentId ?? 'root'}:${String(c.name || '').trim().toLowerCase()}`;
existingMap.set(key, c.id);
}
const jtlCatIdToDashCatId = new Map();
jtlCatIdToDashCatId.set('0', rootCategoryId);
// Group category images by JTL category ID
const catImagesByJtlId = new Map();
for (const task of categoryImageTasks) {
if (!catImagesByJtlId.has(task.catJtlId)) {
catImagesByJtlId.set(task.catJtlId, []);
}
catImagesByJtlId.get(task.catJtlId).push(task);
}
const pendingCats = [...includedCategories];
let createdCatCount = 0;
let catPicCount = 0;
let safety = 0;
while (pendingCats.length > 0 && safety < 10) {
safety++;
const remaining = [];
for (const c of pendingCats) {
const jtlId = String(c.id ?? c.kKategorie);
const jtlParent = String(c.parentid ?? c.kOberKategorie ?? '0');
let name = cleanValue(c.name ?? c.cName);
if (!name) {
try {
const tr = JSON.parse(c.translations || '[]');
name = tr[0]?.name || tr[0]?.cName || '';
} catch {}
}
if (!name) name = `Kategorie ${jtlId}`;
let dashParentId;
if (jtlParent === '0' || !jtlParent) {
dashParentId = rootCategoryId;
} else if (jtlCatIdToDashCatId.has(jtlParent)) {
dashParentId = jtlCatIdToDashCatId.get(jtlParent);
} else {
remaining.push(c);
continue;
}
const key = `${dashParentId}:${name.toLowerCase()}`;
let dashCatId = existingMap.get(key);
if (!dashCatId) {
try {
const created = await store.category.createUnderParent({
googleAuthId,
parentId: dashParentId,
name,
});
dashCatId = created.id;
existingMap.set(key, dashCatId);
createdCatCount++;
} catch (err) {
log.warn(`Failed to create category "${name}":`, err.message);
}
}
if (dashCatId) {
jtlCatIdToDashCatId.set(jtlId, dashCatId);
// Add category images
const imgs = catImagesByJtlId.get(jtlId) || [];
for (const imgTask of imgs) {
try {
const imgBuf = fs.readFileSync(imgTask.fullPath);
await store.category.addImage(googleAuthId, dashCatId, imgBuf);
catPicCount++;
const skele = await computeSkeleHash(imgBuf);
progressTracker.emitSkeleHash(skele?.base62 || null);
} catch (err) {
log.warn(`Category image error (${imgTask.relPath}):`, err.message);
}
progressTracker.counts.imagesdone++;
}
}
progressTracker.counts.categoriesdone++;
progressTracker.emitProgress();
}
pendingCats.length = 0;
pendingCats.push(...remaining);
}
progressTracker.flushSkeleHashes();
progressTracker.emitProgress(true);
log.info(`Categories resolved: ${jtlCatIdToDashCatId.size}/${includedCategories.length}, created: ${createdCatCount}, images: ${catPicCount}`);
const hauptDashId = jtlCatIdToDashCatId.get('1') || rootCategoryId;
return {
jtlCatIdToDashCatId,
hauptDashId,
createdCatCount,
catPicCount,
};
}
// #endregion