157 lines
4.4 KiB
JavaScript
157 lines
4.4 KiB
JavaScript
import { randomUUID } from 'crypto';
|
||
import fs from 'fs';
|
||
import path from 'path';
|
||
import { DatabaseSync } from 'node:sqlite';
|
||
import { fileURLToPath } from 'url';
|
||
import XLSX from 'xlsx';
|
||
import dotenv from 'dotenv';
|
||
|
||
dotenv.config();
|
||
|
||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||
const root = path.join(__dirname, '..');
|
||
const xlsxPath = path.join(root, 'Anlagenliste ITT.xlsx');
|
||
const dbPath = process.env.SQLITE_PATH || path.join(root, 'data', 'crm.db');
|
||
|
||
/** Zeile 9 (1-basiert): Spaltenbeschriftung / Feldnamen */
|
||
const ZEILE_BESCHREIBUNG = 9;
|
||
/** Zeile 7: Gruppierung / übergeordnete Rubrik pro Spalte (z. B. „Kunde“) */
|
||
const ZEILE_GRUPPE = 7;
|
||
/** Nur Spalten 1–99 importieren; Spalten 100–180 entfallen */
|
||
const MAX_SPALTEN = 99;
|
||
|
||
if (!fs.existsSync(xlsxPath)) {
|
||
console.error('Datei nicht gefunden:', xlsxPath);
|
||
process.exit(1);
|
||
}
|
||
|
||
function dedupeHeaders(raw) {
|
||
const count = {};
|
||
return raw.map((h) => {
|
||
const base = String(h ?? '').trim() || 'Spalte';
|
||
count[base] = (count[base] || 0) + 1;
|
||
return count[base] === 1 ? base : `${base}_${count[base]}`;
|
||
});
|
||
}
|
||
|
||
function padRow(arr, width) {
|
||
const a = Array.isArray(arr) ? [...arr] : [];
|
||
while (a.length < width) a.push('');
|
||
return a.slice(0, width);
|
||
}
|
||
|
||
const wb = XLSX.readFile(xlsxPath);
|
||
const sheet =
|
||
wb.Sheets.Anlagen ||
|
||
wb.Sheets[wb.SheetNames.find((n) => /anlagen/i.test(n))] ||
|
||
wb.Sheets[wb.SheetNames[0]];
|
||
|
||
const aoa = XLSX.utils.sheet_to_json(sheet, { header: 1, defval: '' });
|
||
const beschreibIdx = ZEILE_BESCHREIBUNG - 1;
|
||
const gruppeIdx = ZEILE_GRUPPE - 1;
|
||
|
||
let width = 0;
|
||
for (const r of [beschreibIdx, gruppeIdx]) {
|
||
if (aoa[r] && aoa[r].length > width) width = aoa[r].length;
|
||
}
|
||
if (sheet['!ref']) {
|
||
const range = XLSX.utils.decode_range(sheet['!ref']);
|
||
width = Math.max(width, range.e.c + 1);
|
||
}
|
||
|
||
width = Math.min(width, MAX_SPALTEN);
|
||
|
||
const rawRow9 = padRow(aoa[beschreibIdx] || [], width).map((h) =>
|
||
String(h ?? '').trim(),
|
||
);
|
||
const rawRow7 = padRow(aoa[gruppeIdx] || [], width).map((h) =>
|
||
String(h ?? '').trim(),
|
||
);
|
||
const headers = dedupeHeaders(rawRow9);
|
||
|
||
fs.mkdirSync(path.dirname(dbPath), { recursive: true });
|
||
const db = new DatabaseSync(dbPath);
|
||
db.exec('PRAGMA foreign_keys = ON');
|
||
|
||
const cols = db.prepare('PRAGMA table_info(machines)').all();
|
||
if (!cols.some((c) => c.name === 'extras')) {
|
||
db.exec('ALTER TABLE machines ADD COLUMN extras TEXT');
|
||
}
|
||
|
||
const replaceAll = process.argv.includes('--replace');
|
||
if (replaceAll) {
|
||
db.exec('DELETE FROM events');
|
||
db.exec('DELETE FROM tickets');
|
||
db.exec('DELETE FROM machines');
|
||
console.log('Bestehende Maschinen/Tickets/Events gelöscht.');
|
||
}
|
||
|
||
const insertMachine = db.prepare(
|
||
`INSERT INTO machines (id, name, typ, seriennummer, standort, extras, updated_at)
|
||
VALUES (?, ?, ?, ?, ?, ?, datetime('now'))`,
|
||
);
|
||
|
||
let imported = 0;
|
||
let skipped = 0;
|
||
|
||
db.exec('BEGIN');
|
||
try {
|
||
for (let i = beschreibIdx + 1; i < aoa.length; i++) {
|
||
const row = aoa[i];
|
||
if (!row || !row.length) continue;
|
||
const padded = padRow(row, width);
|
||
const sn = String(padded[0] ?? '').trim();
|
||
if (!/^ITT#/i.test(sn)) continue;
|
||
|
||
const dup = db
|
||
.prepare('SELECT id FROM machines WHERE seriennummer = ?')
|
||
.get(sn);
|
||
if (dup) {
|
||
skipped += 1;
|
||
continue;
|
||
}
|
||
|
||
const rowObj = {};
|
||
headers.forEach((h, j) => {
|
||
const v = padded[j];
|
||
rowObj[h] =
|
||
v === '' || v === undefined || v === null ? '' : String(v).trim();
|
||
});
|
||
|
||
const typ = rowObj.Typ || '—';
|
||
const standort =
|
||
[rowObj.Stadt, rowObj.Land].filter(Boolean).join(', ') || '—';
|
||
|
||
const werteAlsListe = padded.map((v) =>
|
||
v === '' || v === undefined || v === null ? '' : String(v).trim(),
|
||
);
|
||
|
||
const extrasObj = {
|
||
_beschriftungZeile9: rawRow9,
|
||
_gruppeZeile7: rawRow7,
|
||
_werteAlsListe: werteAlsListe,
|
||
...rowObj,
|
||
};
|
||
|
||
const extrasJson = JSON.stringify(extrasObj);
|
||
const id = randomUUID();
|
||
|
||
insertMachine.run(id, sn, typ, sn, standort, extrasJson);
|
||
imported += 1;
|
||
}
|
||
db.exec('COMMIT');
|
||
} catch (e) {
|
||
db.exec('ROLLBACK');
|
||
throw e;
|
||
}
|
||
|
||
db.close();
|
||
console.log(
|
||
`Anlagenliste: ${imported} Maschinen importiert, ${skipped} übersprungen (Seriennr. schon vorhanden).`,
|
||
);
|
||
if (!replaceAll && skipped > 0 && imported === 0) {
|
||
console.log(
|
||
'Hinweis: Für Neuimport: npm run import:anlagen -- --replace',
|
||
);
|
||
}
|