u
This commit is contained in:
@@ -91,6 +91,51 @@ export function parseCsvRecords(text) {
|
|||||||
return records;
|
return records;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts or derives a variant attribute value for a child article
|
||||||
|
* based on its name relative to its parent name, or SKU suffixes.
|
||||||
|
*
|
||||||
|
* @param {string} parentName
|
||||||
|
* @param {string} childName
|
||||||
|
* @param {string} childSku
|
||||||
|
* @param {string} parentSku
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export function extractVariantValue(parentName, childName, childSku, parentSku) {
|
||||||
|
let pName = (parentName || '').replace(/[©®™]/g, '').trim();
|
||||||
|
let cName = (childName || '').replace(/[©®™]/g, '').trim();
|
||||||
|
|
||||||
|
if (pName && cName) {
|
||||||
|
// Child name starts with parent name
|
||||||
|
if (cName.toLowerCase().startsWith(pName.toLowerCase())) {
|
||||||
|
let val = cName.slice(pName.length).trim().replace(/^[-–—:/,\s]+/, '').trim();
|
||||||
|
if (val) return val;
|
||||||
|
}
|
||||||
|
// Match common leading tokens
|
||||||
|
const pWords = pName.toLowerCase().split(/\s+/);
|
||||||
|
const cWords = cName.split(/\s+/);
|
||||||
|
if (cWords.length > 1 && pWords[0] === cWords[0].toLowerCase()) {
|
||||||
|
let matchCount = 0;
|
||||||
|
while (matchCount < pWords.length && matchCount < cWords.length && pWords[matchCount] === cWords[matchCount].toLowerCase()) {
|
||||||
|
matchCount++;
|
||||||
|
}
|
||||||
|
if (matchCount > 0) {
|
||||||
|
const remaining = cWords.slice(matchCount).join(' ').trim().replace(/^[-–—:/,\s]+/, '').trim();
|
||||||
|
if (remaining) return remaining;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SKU suffix: "42001505-1" with parent "42001505" -> "1"
|
||||||
|
if (parentSku && childSku && childSku.startsWith(parentSku)) {
|
||||||
|
let suffix = childSku.slice(parentSku.length).trim().replace(/^[-_]+/, '').trim();
|
||||||
|
if (suffix) return suffix;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cName && cName !== pName) return cName;
|
||||||
|
return childSku || 'Standard';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executes full catalog import from preserved files in importDir.
|
* Executes full catalog import from preserved files in importDir.
|
||||||
*
|
*
|
||||||
@@ -502,23 +547,108 @@ export async function executeCompanyImport({
|
|||||||
console.log(`[Import] Linking ${variantChildren.length} child variants to parents...`);
|
console.log(`[Import] Linking ${variantChildren.length} child variants to parents...`);
|
||||||
onProgress('variants', 0, variantChildren.length, 'Linking variants');
|
onProgress('variants', 0, variantChildren.length, 'Linking variants');
|
||||||
|
|
||||||
let variantsLinked = 0;
|
// Ensure a default attribute (e.g. "Variante") exists for variant parents
|
||||||
for (let i = 0; i < variantChildren.length; i++) {
|
let defaultVariantAttrId = null;
|
||||||
const child = variantChildren[i];
|
try {
|
||||||
const childSku = String(child.cArtNr || '').trim();
|
const attrList = await store.attributename.list(googleAuthId);
|
||||||
|
const existingAttr = attrList.find(
|
||||||
|
(a) => (a.name || '').trim().toLowerCase() === 'variante'
|
||||||
|
);
|
||||||
|
if (existingAttr) {
|
||||||
|
defaultVariantAttrId = existingAttr.id;
|
||||||
|
} else {
|
||||||
|
const createdAttr = await store.attributename.create(googleAuthId, {
|
||||||
|
name: 'Variante',
|
||||||
|
exclusive: true,
|
||||||
|
});
|
||||||
|
defaultVariantAttrId = createdAttr.id;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
log.warn('Could not ensure default "Variante" attribute:', err.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Group children by parent SKU
|
||||||
|
const parentChildrenMap = new Map();
|
||||||
|
for (const child of variantChildren) {
|
||||||
const parentJtlId = String(child.kVaterArtikel || '').trim();
|
const parentJtlId = String(child.kVaterArtikel || '').trim();
|
||||||
const parentSku = jtlArtIdToSku.get(parentJtlId);
|
const parentSku = jtlArtIdToSku.get(parentJtlId);
|
||||||
|
const childSku = String(child.cArtNr || '').trim();
|
||||||
|
if (!parentSku || !childSku || parentSku === childSku) continue;
|
||||||
|
|
||||||
if (childSku && parentSku && childSku !== parentSku) {
|
if (!parentChildrenMap.has(parentSku)) {
|
||||||
try {
|
parentChildrenMap.set(parentSku, []);
|
||||||
await store.article.setParentBySku(googleAuthId, childSku, parentSku);
|
}
|
||||||
variantsLinked++;
|
parentChildrenMap.get(parentSku).push(child);
|
||||||
} catch (err) {
|
}
|
||||||
console.warn(`[Import] Failed to link child ${childSku} to parent ${parentSku}:`, err.message);
|
|
||||||
|
let variantsLinked = 0;
|
||||||
|
let processedCount = 0;
|
||||||
|
|
||||||
|
for (const [parentSku, children] of parentChildrenMap.entries()) {
|
||||||
|
let parentDetail = null;
|
||||||
|
try {
|
||||||
|
const res = await store.article.getBySku(googleAuthId, parentSku);
|
||||||
|
parentDetail = res.article;
|
||||||
|
} catch (err) {
|
||||||
|
log.warn(`[Import] Could not load parent ${parentSku}:`, err.message);
|
||||||
|
processedCount += children.length;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure parent has is_parent = 1 and parentattributename_id set
|
||||||
|
let parentAttrId = parentDetail?.parentAttributeNameId;
|
||||||
|
if (!parentDetail?.isParent || !parentAttrId) {
|
||||||
|
if (defaultVariantAttrId) {
|
||||||
|
try {
|
||||||
|
await store.article.toggleParent(googleAuthId, parentSku, {
|
||||||
|
isParent: true,
|
||||||
|
attributeNameId: defaultVariantAttrId,
|
||||||
|
});
|
||||||
|
parentAttrId = defaultVariantAttrId;
|
||||||
|
} catch (err) {
|
||||||
|
log.warn(`[Import] Could not toggle parent for ${parentSku}:`, err.message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (i % 20 === 0 || i === variantChildren.length - 1) {
|
|
||||||
onProgress('variants', i + 1, variantChildren.length, `${childSku} -> ${parentSku}`);
|
const parentName = parentDetail?.name || '';
|
||||||
|
const usedValues = new Set();
|
||||||
|
|
||||||
|
for (const child of children) {
|
||||||
|
processedCount++;
|
||||||
|
const childSku = String(child.cArtNr || '').trim();
|
||||||
|
let childName = '';
|
||||||
|
try {
|
||||||
|
const desc = JSON.parse(child.descriptions || '[]');
|
||||||
|
childName = (Array.isArray(desc) ? desc[0]?.cName : desc?.cName) || '';
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
let val = extractVariantValue(parentName, childName, childSku, parentSku);
|
||||||
|
if (usedValues.has(val.toLowerCase())) {
|
||||||
|
val = `${val} (${childSku})`;
|
||||||
|
}
|
||||||
|
usedValues.add(val.toLowerCase());
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (parentAttrId) {
|
||||||
|
await store.article.attachChild(googleAuthId, parentSku, {
|
||||||
|
sku: childSku,
|
||||||
|
value: val,
|
||||||
|
reparent: true,
|
||||||
|
});
|
||||||
|
variantsLinked++;
|
||||||
|
} else {
|
||||||
|
// Fallback to setParentBySku if toggleParent failed
|
||||||
|
await store.article.setParentBySku(googleAuthId, childSku, parentSku);
|
||||||
|
variantsLinked++;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.warn(`[Import] Failed to attach child ${childSku} to parent ${parentSku}:`, err.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (processedCount % 20 === 0 || processedCount === variantChildren.length) {
|
||||||
|
onProgress('variants', processedCount, variantChildren.length, `${childSku} -> ${parentSku}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.log(`[Import] Linked ${variantsLinked}/${variantChildren.length} variants`);
|
console.log(`[Import] Linked ${variantsLinked}/${variantChildren.length} variants`);
|
||||||
|
|||||||
Reference in New Issue
Block a user