This commit is contained in:
seb
2026-08-04 01:24:19 +02:00
parent c6cb1bc229
commit 4a9403bfd1
6 changed files with 298 additions and 0 deletions

70
src/util/artikelCsv.js Normal file
View File

@@ -0,0 +1,70 @@
function csvField(value) {
const text = value == null ? '' : String(value);
return /[";\n\r]/.test(text) ? `"${text.replace(/"/g, '""')}"` : text;
}
const COLUMNS = [
'FullHash',
'Id',
'SKU',
'Name',
'Slug',
'Einheit',
'EinheitSymbol',
'Menge',
'Marke',
'Netto',
'MwSt',
'EANs',
'Kategorien',
'KategorienPfad',
];
function formatNumber(value) {
if (value == null || Number.isNaN(Number(value))) return '';
return String(value);
}
/**
* Build artikel.csv from the article export rows returned by `listExport`.
* Rows are sorted by Id (server-side ORDER BY a.id). Semicolon separated with a
* `sep=;` hint; Windows-1252 encoding is applied in the route via toWindows1252.
*/
export function buildArtikelCsv(rows) {
if (!Array.isArray(rows) || rows.length === 0) return '';
const lines = [
'sep=;',
COLUMNS.map(csvField).join(';'),
...rows.map((row) =>
[
csvField((row.fullHashes ?? []).join(', ')),
csvField(row.id),
csvField(row.sku),
csvField(row.name ?? ''),
csvField(row.slug ?? ''),
csvField(row.unitSi ?? ''),
csvField(row.unitSymbol ?? ''),
csvField(row.unitamount),
csvField(row.brandName ?? ''),
formatNumber(row.net),
formatNumber(row.taxRate),
csvField((row.eans ?? []).join(', ')),
csvField((row.categoryIds ?? []).join(', ')),
csvField((row.categoryBreadcrumbs ?? []).join(', ')),
].join(';'),
),
];
return lines.join('\r\n');
}
/** Encode a string as Windows-1252 bytes (single-byte; out-of-range → '?'). */
export function toWindows1252(str) {
const bytes = Buffer.alloc(str.length);
for (let i = 0; i < str.length; i++) {
const code = str.charCodeAt(i);
bytes[i] = code <= 0xff ? code : 0x3f; // '?'
}
return bytes;
}