u
This commit is contained in:
@@ -995,6 +995,24 @@
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: '#/components/schemas/ManifestLine'
|
||||
* shipping:
|
||||
* $ref: '#/components/schemas/Shipping'
|
||||
* nullable: true
|
||||
* shippings:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: '#/components/schemas/Shipping'
|
||||
* payment:
|
||||
* $ref: '#/components/schemas/Payment'
|
||||
* nullable: true
|
||||
* payments:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: '#/components/schemas/Payment'
|
||||
* paid:
|
||||
* type: boolean
|
||||
* delivered:
|
||||
* type: boolean
|
||||
* ArticleImage:
|
||||
* type: object
|
||||
* required: [picId, sort, full, thumb, hash, thumbHash]
|
||||
|
||||
@@ -43,7 +43,7 @@ export function register(app, basePath = '') {
|
||||
* description: |
|
||||
* Lists sales orders (`manifest` rows with `type = 'order'`)
|
||||
* for the authenticated user's selected company.
|
||||
* Results are paged, include line items, and provide total unpaged count.
|
||||
* Results are paged, include line items, shipping, payment, and provide total unpaged count.
|
||||
* security:
|
||||
* - cookieAuth: []
|
||||
* parameters:
|
||||
|
||||
@@ -1,26 +1,69 @@
|
||||
import { getStore } from './store.js';
|
||||
|
||||
async function enrichSingleOrder(store, googleAuthId, order) {
|
||||
if (!order) return order;
|
||||
const [shippings, payments] = await Promise.all([
|
||||
store.shipping.listByManifestId(googleAuthId, order.id).catch(() => []),
|
||||
store.payment.listByManifestId(googleAuthId, order.id).catch(() => []),
|
||||
]);
|
||||
|
||||
const isPaid = Array.isArray(payments) && payments.length > 0;
|
||||
const isDelivered = Array.isArray(shippings) && shippings.some((s) => s.state === 'shipped' || s.state === 'delivered');
|
||||
|
||||
return {
|
||||
...order,
|
||||
shipping: shippings[0] ?? null,
|
||||
shippings: shippings ?? [],
|
||||
payment: payments[0] ?? null,
|
||||
payments: payments ?? [],
|
||||
paid: isPaid,
|
||||
delivered: isDelivered,
|
||||
};
|
||||
}
|
||||
|
||||
async function enrichOrderResult(store, googleAuthId, result) {
|
||||
if (!result) return result;
|
||||
if (Array.isArray(result)) {
|
||||
return Promise.all(result.map((o) => enrichSingleOrder(store, googleAuthId, o)));
|
||||
}
|
||||
if (Array.isArray(result.orders)) {
|
||||
const enrichedOrders = await Promise.all(
|
||||
result.orders.map((o) => enrichSingleOrder(store, googleAuthId, o))
|
||||
);
|
||||
return {
|
||||
...result,
|
||||
orders: enrichedOrders,
|
||||
};
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function list(googleAuthId, opts) {
|
||||
const store = await getStore();
|
||||
return store.order.list(googleAuthId, opts);
|
||||
const result = await store.order.list(googleAuthId, opts);
|
||||
return enrichOrderResult(store, googleAuthId, result);
|
||||
}
|
||||
|
||||
export async function getById(googleAuthId, id) {
|
||||
const store = await getStore();
|
||||
return store.order.getById(googleAuthId, id);
|
||||
const order = await store.order.getById(googleAuthId, id);
|
||||
return enrichSingleOrder(store, googleAuthId, order);
|
||||
}
|
||||
|
||||
export async function create(googleAuthId, data) {
|
||||
const store = await getStore();
|
||||
return store.order.create(googleAuthId, data);
|
||||
const order = await store.order.create(googleAuthId, data);
|
||||
return enrichSingleOrder(store, googleAuthId, order);
|
||||
}
|
||||
|
||||
export async function update(googleAuthId, id, data) {
|
||||
const store = await getStore();
|
||||
return store.order.update(googleAuthId, id, data);
|
||||
const order = await store.order.update(googleAuthId, id, data);
|
||||
return enrichSingleOrder(store, googleAuthId, order);
|
||||
}
|
||||
|
||||
export async function remove(googleAuthId, id) {
|
||||
const store = await getStore();
|
||||
return store.order.delete(googleAuthId, id);
|
||||
}
|
||||
|
||||
|
||||
@@ -179,11 +179,19 @@ export async function executeCompanyImport({
|
||||
|
||||
// MARK: - Phase 7: Orders / Receipts
|
||||
// #region Phase 7: Orders / Receipts
|
||||
const allArtIdToSku = new Map();
|
||||
for (const a of artRecords) {
|
||||
const kArt = cleanValue(a.id ?? a.kArtikel);
|
||||
const sku = cleanValue(a.sku ?? a.cArtNr);
|
||||
if (kArt && sku) allArtIdToSku.set(kArt, sku);
|
||||
}
|
||||
|
||||
const ordersResult = await importOrders({
|
||||
store,
|
||||
googleAuthId,
|
||||
manRecords,
|
||||
itemRecords,
|
||||
jtlArtIdToSku: allArtIdToSku,
|
||||
progressTracker,
|
||||
});
|
||||
// #endregion
|
||||
@@ -213,6 +221,8 @@ export async function executeCompanyImport({
|
||||
ordersTotal: totalReceiptsCount,
|
||||
ordersImported: ordersResult.ordersImported,
|
||||
ordersSkipped: ordersResult.ordersSkipped,
|
||||
paymentsImported: ordersResult.paymentsImported,
|
||||
shippingsImported: ordersResult.shippingsImported,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -36,8 +36,8 @@ export async function importArticles({
|
||||
let articlesUpdated = 0;
|
||||
|
||||
for (const a of includedArticles) {
|
||||
const jtlArtId = String(a.kArtikel);
|
||||
const sku = cleanValue(a.cArtNr);
|
||||
const jtlArtId = String(a.id ?? a.kArtikel);
|
||||
const sku = cleanValue(a.sku ?? a.cArtNr);
|
||||
if (!sku) {
|
||||
progressTracker.counts.articlesdone++;
|
||||
continue;
|
||||
@@ -45,15 +45,15 @@ export async function importArticles({
|
||||
|
||||
jtlArtIdToSku.set(jtlArtId, sku);
|
||||
|
||||
let name = cleanValue(a.cName);
|
||||
let desc = cleanValue(a.descriptions);
|
||||
let name = cleanValue(a.name ?? a.cName);
|
||||
let desc = cleanValue(a.desc ?? a.descriptions);
|
||||
|
||||
if (!name && desc) {
|
||||
try {
|
||||
const parsed = JSON.parse(desc);
|
||||
const item = Array.isArray(parsed) ? parsed[0] : parsed;
|
||||
if (item?.cName) name = String(item.cName).trim();
|
||||
if (item?.cBeschreibung) desc = String(item.cBeschreibung).trim();
|
||||
if (item?.cName || item?.name) name = String(item.cName || item.name).trim();
|
||||
if (item?.cBeschreibung || item?.desc) desc = String(item.cBeschreibung || item.desc).trim();
|
||||
} catch {}
|
||||
}
|
||||
if (!name) name = `Artikel ${sku}`;
|
||||
@@ -66,7 +66,7 @@ export async function importArticles({
|
||||
if (rawCatStr.startsWith('[') || rawCatStr.startsWith('{')) {
|
||||
try {
|
||||
const raw = JSON.parse(rawCatStr);
|
||||
parsedIds = (Array.isArray(raw) ? raw : [raw]).map((it) => String(it.kKategorie || it));
|
||||
parsedIds = (Array.isArray(raw) ? raw : [raw]).map((it) => String(it.id ?? it.kKategorie ?? it));
|
||||
} catch {
|
||||
parsedIds = rawCatStr.split(/[\r\n,;]+/).map((s) => s.trim()).filter(Boolean);
|
||||
}
|
||||
@@ -81,12 +81,13 @@ export async function importArticles({
|
||||
}
|
||||
const primaryCatId = assignedDashCatIds[0] || hauptDashId;
|
||||
|
||||
const trackStock = a.cLagerAktiv === 'Y' ? 1 : 0;
|
||||
const netPrice = parseGermanFloat(a.fVKNetto, 0);
|
||||
const taxRate = a.kSteuerklasse === '3' ? 7 : 19;
|
||||
const barcode = cleanValue(a.cBarcode);
|
||||
const rawTrack = cleanValue(a.track_strock ?? a.track_stock ?? a.cLagerAktiv);
|
||||
const trackStock = (rawTrack === 'Y' || rawTrack === '1' || rawTrack === 'true') ? 1 : 0;
|
||||
const netPrice = parseGermanFloat(a.net ?? a.fVKNetto, 0);
|
||||
const taxRate = a.taxrate != null && String(a.taxrate).trim() !== '' ? parseGermanFloat(a.taxrate, 19) : (a.kSteuerklasse === '3' ? 7 : 19);
|
||||
const barcode = cleanValue(a.ean ?? a.cBarcode);
|
||||
|
||||
const hersteller = cleanValue(a.Hersteller);
|
||||
const hersteller = cleanValue(a.brand ?? a.Hersteller);
|
||||
const brandId = hersteller ? (brandNameToId.get(hersteller.toLowerCase()) || null) : null;
|
||||
|
||||
let existingArticle = null;
|
||||
|
||||
@@ -20,15 +20,15 @@ export function filterCatalogRecords(catRecords, artRecords) {
|
||||
const excludedJtlIds = new Set(['72']);
|
||||
|
||||
for (const c of catRecords) {
|
||||
let name = (c.cName || '').trim();
|
||||
let name = (c.name || c.cName || '').trim();
|
||||
if (!name) {
|
||||
try {
|
||||
const tr = JSON.parse(c.translations || '[]');
|
||||
name = tr[0]?.cName || '';
|
||||
name = tr[0]?.cName || tr[0]?.name || '';
|
||||
} catch {}
|
||||
}
|
||||
if (name.toLowerCase().includes('papierkorb') || name.toLowerCase().includes('trash')) {
|
||||
excludedJtlIds.add(String(c.kKategorie));
|
||||
excludedJtlIds.add(String(c.id ?? c.kKategorie));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,8 +36,8 @@ export function filterCatalogRecords(catRecords, artRecords) {
|
||||
while (addedExcluded) {
|
||||
addedExcluded = false;
|
||||
for (const c of catRecords) {
|
||||
const id = String(c.kKategorie);
|
||||
const parent = String(c.kOberKategorie);
|
||||
const id = String(c.id ?? c.kKategorie);
|
||||
const parent = String(c.parentid ?? c.kOberKategorie);
|
||||
if (!excludedJtlIds.has(id) && excludedJtlIds.has(parent)) {
|
||||
excludedJtlIds.add(id);
|
||||
addedExcluded = true;
|
||||
@@ -45,16 +45,21 @@ export function filterCatalogRecords(catRecords, artRecords) {
|
||||
}
|
||||
}
|
||||
|
||||
const includedCategories = catRecords.filter((c) => !excludedJtlIds.has(String(c.kKategorie)));
|
||||
const includedCategories = catRecords.filter((c) => !excludedJtlIds.has(String(c.id ?? c.kKategorie)));
|
||||
|
||||
const includedArticles = artRecords.filter((a) => {
|
||||
const rawActive = cleanValue(a.active ?? a.cAktiv);
|
||||
if (rawActive === '0' || rawActive === 'N' || rawActive === 'false') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const catStr = String(a.categories || '').trim();
|
||||
if (!catStr) return true;
|
||||
let catIds = [];
|
||||
if (catStr.startsWith('[') || catStr.startsWith('{')) {
|
||||
try {
|
||||
const raw = JSON.parse(catStr);
|
||||
catIds = (Array.isArray(raw) ? raw : [raw]).map((it) => String(it.kKategorie || it));
|
||||
catIds = (Array.isArray(raw) ? raw : [raw]).map((it) => String(it.id ?? it.kKategorie ?? it));
|
||||
} catch {
|
||||
catIds = catStr.split(/[\r\n,;]+/).map((s) => s.trim()).filter(Boolean);
|
||||
}
|
||||
@@ -94,7 +99,7 @@ export function prepareImportTasks({ importDir, includedCategories, includedArti
|
||||
for (const rel of picRel.split(/[\r\n,;]+/).map((s) => s.trim()).filter(Boolean)) {
|
||||
const full = path.resolve(importDir, rel);
|
||||
if (fs.existsSync(full)) {
|
||||
categoryImageTasks.push({ catJtlId: String(c.kKategorie), relPath: rel, fullPath: full });
|
||||
categoryImageTasks.push({ catJtlId: String(c.id ?? c.kKategorie), relPath: rel, fullPath: full });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -103,11 +108,11 @@ export function prepareImportTasks({ importDir, includedCategories, includedArti
|
||||
// Brand images (unique manufacturers, 1 primary logo)
|
||||
const brandMap = new Map(); // brandName -> array of { relPath, fullPath }
|
||||
for (const a of includedArticles) {
|
||||
const brand = cleanValue(a.Hersteller);
|
||||
const brand = cleanValue(a.brand ?? a.Hersteller);
|
||||
if (!brand) continue;
|
||||
if (!brandMap.has(brand)) {
|
||||
brandMap.set(brand, []);
|
||||
const rawPics = String(a.manufacturerPictureFiles || '').trim();
|
||||
const rawPics = String(a.brandPictureFiles ?? a.manufacturerPictureFiles ?? '').trim();
|
||||
if (rawPics) {
|
||||
for (const rel of rawPics.split(/[\r\n,;]+/).map((s) => s.trim()).filter(Boolean)) {
|
||||
const full = path.resolve(importDir, rel);
|
||||
@@ -129,7 +134,7 @@ export function prepareImportTasks({ importDir, includedCategories, includedArti
|
||||
// Article images
|
||||
const articleImageTasks = [];
|
||||
for (const a of includedArticles) {
|
||||
const sku = cleanValue(a.cArtNr);
|
||||
const sku = cleanValue(a.sku ?? a.cArtNr);
|
||||
if (!sku) continue;
|
||||
const rawPics = String(a.pictureFiles || '').trim();
|
||||
if (rawPics) {
|
||||
|
||||
@@ -64,14 +64,14 @@ export async function importCategories({
|
||||
safety++;
|
||||
const remaining = [];
|
||||
for (const c of pendingCats) {
|
||||
const jtlId = String(c.kKategorie);
|
||||
const jtlParent = String(c.kOberKategorie || '0');
|
||||
const jtlId = String(c.id ?? c.kKategorie);
|
||||
const jtlParent = String(c.parentid ?? c.kOberKategorie ?? '0');
|
||||
|
||||
let name = cleanValue(c.cName);
|
||||
let name = cleanValue(c.name ?? c.cName);
|
||||
if (!name) {
|
||||
try {
|
||||
const tr = JSON.parse(c.translations || '[]');
|
||||
name = tr[0]?.cName || '';
|
||||
name = tr[0]?.name || tr[0]?.cName || '';
|
||||
} catch {}
|
||||
}
|
||||
if (!name) name = `Kategorie ${jtlId}`;
|
||||
|
||||
@@ -10,33 +10,37 @@ const log = createLogger('import');
|
||||
// #region Order / Manifest Importer
|
||||
|
||||
/**
|
||||
* Imports order manifests and line items, resolving customer entities.
|
||||
* Imports order manifests and line items, resolving customer entities, payments, and shippings.
|
||||
*
|
||||
* @param {object} params
|
||||
* @param {object} params.store - Store singleton
|
||||
* @param {number} params.googleAuthId
|
||||
* @param {Array<Record<string, string>>} params.manRecords
|
||||
* @param {Array<Record<string, string>>} params.itemRecords
|
||||
* @param {Map<string, string>} [params.jtlArtIdToSku]
|
||||
* @param {object} params.progressTracker
|
||||
* @returns {Promise<{ ordersImported: number, ordersSkipped: number }>}
|
||||
* @returns {Promise<{ ordersImported: number, ordersSkipped: number, paymentsImported: number, shippingsImported: number }>}
|
||||
*/
|
||||
export async function importOrders({
|
||||
store,
|
||||
googleAuthId,
|
||||
manRecords,
|
||||
itemRecords,
|
||||
jtlArtIdToSku = new Map(),
|
||||
progressTracker,
|
||||
}) {
|
||||
let ordersImported = 0;
|
||||
let ordersSkipped = 0;
|
||||
let paymentsImported = 0;
|
||||
let shippingsImported = 0;
|
||||
|
||||
if (manRecords.length === 0 || itemRecords.length === 0) {
|
||||
return { ordersImported, ordersSkipped };
|
||||
return { ordersImported, ordersSkipped, paymentsImported, shippingsImported };
|
||||
}
|
||||
|
||||
const itemsByAuftrag = new Map();
|
||||
for (const it of itemRecords) {
|
||||
const kAuftrag = String(it.kAuftrag || '').trim();
|
||||
const kAuftrag = String(it.manifestid ?? it.kAuftrag ?? '').trim();
|
||||
if (!kAuftrag) continue;
|
||||
if (!itemsByAuftrag.has(kAuftrag)) {
|
||||
itemsByAuftrag.set(kAuftrag, []);
|
||||
@@ -88,8 +92,38 @@ export async function importOrders({
|
||||
}
|
||||
};
|
||||
|
||||
const accountMap = new Map();
|
||||
try {
|
||||
const accounts = await store.account.list(googleAuthId);
|
||||
for (const acc of accounts) {
|
||||
if (acc.name) {
|
||||
accountMap.set(acc.name.trim().toLowerCase(), acc.id);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
log.warn('Could not list accounts:', err.message);
|
||||
}
|
||||
|
||||
const resolveAccountId = async (accountName) => {
|
||||
const cleanAccName = (accountName || 'Bar').trim();
|
||||
const key = cleanAccName.toLowerCase();
|
||||
if (accountMap.has(key)) return accountMap.get(key);
|
||||
|
||||
try {
|
||||
const created = await store.account.create(googleAuthId, {
|
||||
type: cleanAccName,
|
||||
name: cleanAccName,
|
||||
});
|
||||
accountMap.set(key, created.id);
|
||||
return created.id;
|
||||
} catch (err) {
|
||||
log.warn(`Could not create account "${cleanAccName}":`, err.message);
|
||||
return accountMap.values().next().value || null;
|
||||
}
|
||||
};
|
||||
|
||||
for (const m of manRecords) {
|
||||
const orderNumber = cleanValue(m.cAuftragsNr);
|
||||
const orderNumber = cleanValue(m.nr ?? m.cAuftragsNr);
|
||||
if (!orderNumber || existingOrderNumbers.has(orderNumber)) {
|
||||
ordersSkipped++;
|
||||
progressTracker.counts.receiptsdone++;
|
||||
@@ -97,7 +131,8 @@ export async function importOrders({
|
||||
continue;
|
||||
}
|
||||
|
||||
const rawItems = itemsByAuftrag.get(String(m.kAuftrag || '').trim()) || [];
|
||||
const manifestKey = String(m.id ?? m.kAuftrag ?? '').trim();
|
||||
const rawItems = itemsByAuftrag.get(manifestKey) || [];
|
||||
if (rawItems.length === 0) {
|
||||
ordersSkipped++;
|
||||
progressTracker.counts.receiptsdone++;
|
||||
@@ -105,15 +140,18 @@ export async function importOrders({
|
||||
continue;
|
||||
}
|
||||
|
||||
rawItems.sort((a, b) => (Number(a.sort ?? a.nSort) || 0) - (Number(b.sort ?? b.nSort) || 0));
|
||||
|
||||
const lines = rawItems.map((it) => {
|
||||
const sku = cleanValue(it.cArtNr) || null;
|
||||
const rawName = cleanValue(it.cName || it.cNameStandard);
|
||||
const articleId = cleanValue(it.articleid ?? it.kArtikel);
|
||||
const sku = cleanValue(it.sku ?? it.cArtNr) || (articleId && jtlArtIdToSku?.get?.(articleId)) || null;
|
||||
const rawName = cleanValue(it.name ?? it.cName ?? it.cNameStandard);
|
||||
const name = rawName || (sku ? `Artikel ${sku}` : 'Artikel');
|
||||
const amount = parseGermanFloat(it.fAnzahl, 1);
|
||||
const net = parseGermanFloat(it.fVKNetto, 0);
|
||||
const taxrate = parseGermanFloat(it.fMwSt, 19);
|
||||
const rebate = parseGermanFloat(it.fRabatt, 0);
|
||||
const comment = cleanValue(it.cHinweis) || null;
|
||||
const amount = parseGermanFloat(it.amount ?? it.fAnzahl, 1);
|
||||
const net = parseGermanFloat(it.net ?? it.fVKNetto, 0);
|
||||
const taxrate = parseGermanFloat(it.taxrate ?? it.fMwSt, 19);
|
||||
const rebate = parseGermanFloat(it.rebate ?? it.fRabatt, 0);
|
||||
const comment = cleanValue(it.comment ?? it.cHinweis) || null;
|
||||
|
||||
return {
|
||||
sku,
|
||||
@@ -126,18 +164,19 @@ export async function importOrders({
|
||||
};
|
||||
});
|
||||
|
||||
const custName = cleanValue(m.cName) || 'Laufkunde';
|
||||
const custFirstName = cleanValue(m.cVorname);
|
||||
const custName = cleanValue(m.name ?? m.cName) || 'Laufkunde';
|
||||
const custFirstName = cleanValue(m.firstName ?? m.cVorname);
|
||||
const entityId = await resolveEntityId(custName, custFirstName);
|
||||
|
||||
const dateStr = cleanValue(m.dErstellt);
|
||||
const dateStr = cleanValue(m.created_at ?? m.dErstellt);
|
||||
const parsedDate = dateStr ? new Date(dateStr) : new Date();
|
||||
const validDate = !Number.isNaN(parsedDate.getTime()) ? parsedDate.toISOString() : new Date().toISOString();
|
||||
|
||||
let createdOrder = null;
|
||||
try {
|
||||
await store.order.create(googleAuthId, {
|
||||
createdOrder = await store.order.create(googleAuthId, {
|
||||
number: orderNumber,
|
||||
externalNumber: cleanValue(m.cExterneAuftragsnummer) || null,
|
||||
externalNumber: cleanValue(m.extnr ?? m.cExterneAuftragsnummer) || null,
|
||||
date: validDate,
|
||||
createdAt: validDate,
|
||||
entityId: entityId || undefined,
|
||||
@@ -149,16 +188,67 @@ export async function importOrders({
|
||||
log.warn(`Failed to create order ${orderNumber}:`, err.message);
|
||||
}
|
||||
|
||||
if (createdOrder) {
|
||||
// Handle Payment
|
||||
const isPayed = String(m.payed ?? '').trim() === '1';
|
||||
const paymentType = cleanValue(m.paymentType);
|
||||
if (isPayed && paymentType) {
|
||||
const orderTotal = Math.round(
|
||||
lines.reduce(
|
||||
(sum, l) => sum + (l.amount * l.net * (1 - (l.rebate || 0) / 100) * (1 + (l.taxrate || 0) / 100)),
|
||||
0
|
||||
) * 100
|
||||
) / 100;
|
||||
|
||||
if (orderTotal > 0) {
|
||||
try {
|
||||
const accountId = await resolveAccountId(paymentType);
|
||||
if (accountId) {
|
||||
await store.payment.create(googleAuthId, {
|
||||
accountId,
|
||||
manifestId: createdOrder.id,
|
||||
type: paymentType,
|
||||
amount: orderTotal,
|
||||
});
|
||||
paymentsImported++;
|
||||
}
|
||||
} catch (err) {
|
||||
log.warn(`Failed to create payment for order ${orderNumber}:`, err.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle Delivery / Shipping
|
||||
const shippingType = cleanValue(m.shippingType);
|
||||
if (shippingType) {
|
||||
const isShipped = String(m.shipped ?? '').trim() === '1';
|
||||
const shippingState = isShipped ? 'shipped' : 'open';
|
||||
|
||||
try {
|
||||
await store.shipping.create(googleAuthId, {
|
||||
manifestId: createdOrder.id,
|
||||
type: shippingType,
|
||||
state: shippingState,
|
||||
});
|
||||
shippingsImported++;
|
||||
} catch (err) {
|
||||
log.warn(`Failed to create shipping for order ${orderNumber}:`, err.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
progressTracker.counts.receiptsdone++;
|
||||
progressTracker.emitProgress();
|
||||
}
|
||||
|
||||
progressTracker.emitProgress(true);
|
||||
log.info(`Orders complete: ${ordersImported} created, ${ordersSkipped} skipped`);
|
||||
log.info(`Orders complete: ${ordersImported} created, ${ordersSkipped} skipped, ${paymentsImported} payments, ${shippingsImported} shippings`);
|
||||
|
||||
return {
|
||||
ordersImported,
|
||||
ordersSkipped,
|
||||
paymentsImported,
|
||||
shippingsImported,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -72,12 +72,12 @@ export async function importPfand({
|
||||
// 1. Identify parent articles and map their Pfand values so child variants can inherit
|
||||
const parentPfandMap = new Map();
|
||||
for (const a of includedArticles) {
|
||||
const sku = cleanValue(a.cArtNr);
|
||||
const isParent = a.nIstVater === '1' || (sku && parentSkus.has(sku));
|
||||
const sku = cleanValue(a.sku ?? a.cArtNr);
|
||||
const isParent = a.is_parent === '1' || a.nIstVater === '1' || (sku && parentSkus.has(sku));
|
||||
if (isParent) {
|
||||
const val = extractPfandValue(a.attributes);
|
||||
if (val != null) {
|
||||
parentPfandMap.set(String(a.kArtikel), val);
|
||||
parentPfandMap.set(String(a.id ?? a.kArtikel), val);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -85,19 +85,20 @@ export async function importPfand({
|
||||
// 2. Discover target articles (purchasable standalone or variant items) and their Pfand values
|
||||
const articleTargets = [];
|
||||
for (const a of includedArticles) {
|
||||
const sku = cleanValue(a.cArtNr);
|
||||
const sku = cleanValue(a.sku ?? a.cArtNr);
|
||||
if (!sku) continue;
|
||||
|
||||
const isParent = a.nIstVater === '1' || parentSkus.has(sku);
|
||||
const isParent = a.is_parent === '1' || a.nIstVater === '1' || parentSkus.has(sku);
|
||||
if (isParent) {
|
||||
// Parent articles cannot have options in Dash
|
||||
continue;
|
||||
}
|
||||
|
||||
let pfandVal = extractPfandValue(a.attributes);
|
||||
if (pfandVal == null && a.kVaterArtikel && a.kVaterArtikel !== '0') {
|
||||
if (parentPfandMap.has(String(a.kVaterArtikel))) {
|
||||
pfandVal = parentPfandMap.get(String(a.kVaterArtikel));
|
||||
const parentId = cleanValue(a.parentid ?? a.kVaterArtikel);
|
||||
if (pfandVal == null && parentId && parentId !== '0') {
|
||||
if (parentPfandMap.has(parentId)) {
|
||||
pfandVal = parentPfandMap.get(parentId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -60,15 +60,15 @@ export async function importVariants({
|
||||
// Group children by parent SKU
|
||||
const parentChildrenMap = new Map();
|
||||
for (const child of includedArticles) {
|
||||
const parentJtlId = String(child.kVaterArtikel || '').trim();
|
||||
const parentJtlId = String(child.parentid ?? child.kVaterArtikel ?? '').trim();
|
||||
if (!parentJtlId || parentJtlId === '0') continue;
|
||||
|
||||
const parentSku = jtlArtIdToSku.get(parentJtlId);
|
||||
const childSku = cleanValue(child.cArtNr);
|
||||
const childSku = cleanValue(child.sku ?? child.cArtNr);
|
||||
if (!parentSku || !childSku || parentSku === childSku) continue;
|
||||
|
||||
if (!parentChildrenMap.has(parentSku)) {
|
||||
const parentRow = includedArticles.find((a) => String(a.kArtikel) === parentJtlId) || null;
|
||||
const parentRow = includedArticles.find((a) => String(a.id ?? a.kArtikel) === parentJtlId) || null;
|
||||
parentChildrenMap.set(parentSku, { parentRow, children: [] });
|
||||
}
|
||||
parentChildrenMap.get(parentSku).children.push(child);
|
||||
@@ -76,7 +76,7 @@ export async function importVariants({
|
||||
|
||||
let variantsLinked = 0;
|
||||
for (const [parentSku, { parentRow, children }] of parentChildrenMap.entries()) {
|
||||
const rawAttrName = parentRow ? cleanValue(parentRow.kEigenschaftKombi) : '';
|
||||
const rawAttrName = parentRow ? cleanValue(parentRow.variation ?? parentRow.kEigenschaftKombi) : '';
|
||||
const targetAttrId = await resolveAttributeId(rawAttrName || 'Variante');
|
||||
|
||||
if (targetAttrId) {
|
||||
@@ -92,8 +92,8 @@ export async function importVariants({
|
||||
|
||||
const usedValues = new Set();
|
||||
for (const child of children) {
|
||||
const childSku = cleanValue(child.cArtNr);
|
||||
let val = cleanValue(child.kEigenschaftKombi) || 'Standard';
|
||||
const childSku = cleanValue(child.sku ?? child.cArtNr);
|
||||
let val = cleanValue(child.variation ?? child.kEigenschaftKombi) || 'Standard';
|
||||
|
||||
if (usedValues.has(val.toLowerCase())) {
|
||||
val = `${val} (${childSku})`;
|
||||
|
||||
Reference in New Issue
Block a user