Files
vda-to-edifact-converter/js/watcher-bridge.js
zed 67bd64b688 Separaten Output-Ordner für INVRPT → VDA 4913 Konvertierungen hinzufügen
Der eingehende Watcher nutzt denselben Input-Ordner für alle Dateitypen,
schreibt INVRPT-Konvertierungen nun aber in einen konfigurierbaren
separaten Output-Ordner. Bleibt das Feld leer, wird der Standard-
Output-Ordner als Fallback verwendet (abwärtskompatibel).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 12:22:45 +01:00

351 lines
14 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Watcher Bridge Renderer-side logic for folder watcher
* Communicates with Electron main process via window.electronAPI
*
* Flow:
* 1. Main process detects new file via chokidar
* 2. Main sends file content to renderer via 'watcher-file-detected'
* 3. This bridge converts using the EDIBridge converters
* 4. Calls back to main to write output file
* 5. Calls back to main to move source file to archiv/
*/
window.EDIBridge = window.EDIBridge || {};
class WatcherBridge {
constructor() {
this.status = 'stopped'; // 'stopped', 'running', 'paused'
this.log = [];
this.maxLogEntries = 200;
this.onStatusChange = null;
this.onLogEntry = null;
this.config = null;
this._setupListeners();
}
_setupListeners() {
if (!window.electronAPI) return;
window.electronAPI.onFileDetected((data) => {
this._processDetectedFile(data);
});
window.electronAPI.onWatcherError((data) => {
this._addLog('error', data.filePath || '', data.error || 'Unbekannter Fehler');
});
window.electronAPI.onWatcherReady((data) => {
this._addLog('info', '', `Watcher bereit. Überwache: ${data.inputDir}`);
});
}
async _processDetectedFile(data) {
const { filePath, fileName, content, outputDir, invrptOutputDir } = data;
this._addLog('info', fileName, 'Datei erkannt, starte Konvertierung...');
let konvertierungsmodus = 'Unbekannt';
let quellformat = null;
let zielformat = null;
let convertedContent = null;
let pError = null;
let outFileName = null;
try {
// Try to use a global config if this.config is not set (e.g. from app.js)
if (!this.config && window.app) {
this.config = window.app.config;
}
const result = this._convertContent(content, fileName);
if (result.error) {
this._addLog('error', fileName, result.error);
pError = result.error;
} else {
konvertierungsmodus = result.type;
quellformat = result.format === 'vda' ? 'EDIFACT DELFOR' : 'VDA 4913';
zielformat = result.targetFormat || (result.format === 'vda' ? 'VDA 4905' : (result.format === 'xml' ? 'IFM DELVRY03' : 'Bosch DESADV'));
convertedContent = result.output;
// Build output filename
const baseName = fileName.replace(/\.[^.]+$/, '');
const outExt = result.format === 'vda' ? '.vda' : (result.format === 'xml' ? '.xml' : '.edi');
outFileName = baseName + '_converted' + outExt;
const isInvrpt = result.type && result.type.includes('INVRPT');
const effectiveOutputDir = (isInvrpt && invrptOutputDir) ? invrptOutputDir : outputDir;
const outPath = effectiveOutputDir + '\\' + outFileName;
// Write converted file
const writeResult = await window.electronAPI.writeFile(outPath, result.output);
if (writeResult.error) {
this._addLog('error', fileName, `Schreibfehler: ${writeResult.error}`);
pError = writeResult.error;
} else {
this._addLog('success', fileName, `Konvertiert → ${outFileName} (${result.type})`);
// Move source file to archiv
const archiveResult = await window.electronAPI.moveToArchive(filePath);
if (archiveResult.error) {
this._addLog('error', fileName, `Archiv-Fehler: ${archiveResult.error}`);
} else {
this._addLog('info', fileName, 'Quelldatei nach archiv/ verschoben.');
}
}
}
} catch (e) {
this._addLog('error', fileName, `Fehler: ${e.message}`);
pError = e.message;
}
// Log to database
if (window.app && typeof window.app._logConversion === 'function') {
await window.app._logConversion({
dateiname: fileName,
ausgangsdateiname: outFileName,
quellformat: quellformat,
zielformat: zielformat,
konvertierungsmodus: konvertierungsmodus,
eingang_daten: content,
ausgang_daten: convertedContent,
status: pError ? 'FEHLERHAFT' : 'ERFOLGREICH',
fehlermeldung: pError,
quelle: 'WATCHER' // Auto tag
});
}
}
_convertContent(content, fileName) {
const trimmed = content.trim();
const upper = trimmed.toUpperCase();
// Detect file type and choose converter
if (upper.startsWith('UNA') || upper.startsWith('UNB')) {
// EDIFACT inbound → VDA 4905
return this._convertInbound(content, upper);
} else if (/^[0-9]{3}/.test(trimmed) || /^[57][0-9]{2}/.test(trimmed)) {
// VDA record (starts with 3-digit record type, e.g. 511, 711) → EDIFACT outbound
return this._convertOutbound(content);
} else {
return { error: `Dateiformat nicht erkannt (weder EDIFACT noch VDA). Erste Zeichen: "${trimmed.substring(0, 20)}..."` };
}
}
_convertInbound(content, upper) {
try {
const ConfigParser = window.EDIBridge.ConfigParser;
const DelforParser = window.EDIBridge.DelforParser;
let preferredMode = null;
if (ConfigParser && DelforParser) {
const customerId = DelforParser.getCustomerId(content);
console.log("Watcher detected Inbound Customer ID:", customerId);
if (customerId) {
preferredMode = ConfigParser.lookupCustomerMode(this.config, customerId);
console.log("Watcher mapped Inbound mode for ID", customerId, ":", preferredMode);
}
}
// 1. Prioritize mapped mode
if (preferredMode === 'inbound-ifm') {
const converter = window.EDIBridge.DelforToVDA4905Converter;
if (converter) {
const result = converter.convert(content);
return { output: result, type: 'IFM DELFOR → VDA 4905', format: 'vda' };
}
} else if (preferredMode === 'inbound-bosch') {
const parser = window.EDIBridge.DelforParser;
const generator = window.EDIBridge.VDA4905Generator;
if (parser && generator) {
const parsed = parser.parse(content);
const vda4905 = generator.generate(parsed);
return { output: vda4905, type: 'DELFOR → VDA 4905', format: 'vda' };
}
} else if (preferredMode === 'inbound-invrpt') {
const converter = window.EDIBridge.InvrptToVDA4913;
if (converter) {
const vda4913 = converter.convert(content);
return { output: vda4913, type: 'INVRPT → VDA 4913', format: 'vda' };
}
}
// 2. Fallback to hardcoded logic if no mapping
if (upper.includes('INVRPT')) {
const converter = window.EDIBridge.InvrptToVDA4913;
if (converter) {
const vda4913 = converter.convert(content);
return { output: vda4913, type: 'INVRPT → VDA 4913', format: 'vda' };
}
}
if (upper.includes('DELFOR') && upper.includes('D:04A')) {
const converter = window.EDIBridge.DelforToVDA4905Converter;
if (converter) {
const result = converter.convert(content);
return { output: result, type: 'IFM DELFOR D:04A → VDA 4905', format: 'vda' };
}
}
const parser = window.EDIBridge.DelforParser;
const generator = window.EDIBridge.VDA4905Generator;
if (parser && generator) {
const parsed = parser.parse(content);
const vda4905 = generator.generate(parsed);
return { output: vda4905, type: 'DELFOR → VDA 4905', format: 'vda' };
}
return { error: 'Passendes Inbound-Modul nicht geladen oder Typ unbekannt.' };
} catch (e) {
return { error: `Inbound-Konvertierung fehlgeschlagen: ${e.message}` };
}
}
_convertOutbound(content) {
try {
const VDAParser = window.EDIBridge.VDAParser;
if (!VDAParser) return { error: 'VDA Parser nicht geladen.' };
const vda = VDAParser.parse(content);
if (!vda.interchanges || vda.interchanges.length === 0) {
return { error: 'Keine gültigen VDA 4913 Daten gefunden.' };
}
// Check for customer mapping in config
const ConfigParser = window.EDIBridge.ConfigParser;
const customerId = VDAParser.getCustomerId(content);
console.log("Watcher detected Customer ID:", customerId);
let preferredMode = null;
if (ConfigParser && customerId) {
preferredMode = ConfigParser.lookupCustomerMode(this.config, customerId);
console.log("Watcher mapped mode for ID", customerId, ":", preferredMode);
}
if (preferredMode === 'outbound-ifm') {
const delvry = window.EDIBridge.VDA4913ToDELVRY03;
if (delvry) {
const idocs = delvry.convert(vda, this.config || {});
const output = delvry.toXML(idocs);
return { output, type: 'VDA 4913 → IFM DELVRY03', format: 'xml' };
}
} else if (preferredMode === 'outbound-zf') {
const desadvZf = window.EDIBridge.VDA4913ToDESADVZF;
if (desadvZf) {
const output = desadvZf.convert(content, {}, { linWeights: {}, pacWeights: {}, pacDims: {} }, this.config);
return { output, type: 'VDA 4913 → ZF DESADV', format: 'zf-edi', targetFormat: 'ZF DESADV' };
}
} else if (preferredMode === 'outbound-bosch') {
const desadv = window.EDIBridge.VDA4913ToDESADV;
if (desadv) {
const output = desadv.convert(content, {}, { linWeights: {}, pacWeights: {}, pacDims: {} }, this.config);
return { output, type: 'VDA 4913 → Bosch DESADV', format: 'edi' };
}
}
// Fallback to original logic if no mapping or mapped converter missing
// Try IFM DELVRY03 first
const delvry = window.EDIBridge.VDA4913ToDELVRY03;
if (delvry) {
const idocs = delvry.convert(vda, this.config || {});
const output = delvry.toXML(idocs);
return { output, type: 'VDA 4913 → IFM DELVRY03', format: 'xml' };
}
// Fallback to Bosch DESADV
const desadv = window.EDIBridge.VDA4913ToDESADV;
if (desadv) {
const output = desadv.convert(content, {}, { linWeights: {}, pacWeights: {}, pacDims: {} }, this.config);
return { output, type: 'VDA 4913 → Bosch DESADV', format: 'edi' };
}
return { error: 'Kein Outbound-Converter geladen.' };
} catch (e) {
return { error: `Outbound-Konvertierung fehlgeschlagen: ${e.message}` };
}
}
_addLog(level, fileName, message) {
const entry = {
time: new Date().toLocaleTimeString('de-DE'),
level,
fileName,
message
};
this.log.unshift(entry);
if (this.log.length > this.maxLogEntries) this.log.pop();
if (this.onLogEntry) this.onLogEntry(entry);
}
async start(inputDir, outputDir, invrptOutputDir) {
if (!window.electronAPI) {
return { error: 'Electron API nicht verfügbar. App läuft im Browser-Modus.' };
}
const result = await window.electronAPI.startWatcher({ inputDir, outputDir, invrptOutputDir });
if (result.success) {
this.status = 'running';
this._addLog('info', '', `Watcher gestartet. Überwache: ${inputDir}`);
if (this.onStatusChange) this.onStatusChange(this.status);
} else if (result.error) {
this._addLog('error', '', result.error);
}
return result;
}
async stop() {
if (!window.electronAPI) return;
const result = await window.electronAPI.stopWatcher();
if (result.success) {
this.status = 'stopped';
this._addLog('info', '', 'Watcher gestoppt.');
if (this.onStatusChange) this.onStatusChange(this.status);
}
return result;
}
async pause() {
if (!window.electronAPI) return;
const result = await window.electronAPI.pauseWatcher();
if (result.success) {
this.status = 'paused';
this._addLog('info', '', 'Watcher pausiert.');
if (this.onStatusChange) this.onStatusChange(this.status);
}
return result;
}
async resume() {
if (!window.electronAPI) return;
const result = await window.electronAPI.resumeWatcher();
if (result.success) {
this.status = 'running';
this._addLog('info', '', 'Watcher fortgesetzt.');
if (this.onStatusChange) this.onStatusChange(this.status);
} else if (result.error) {
this._addLog('error', '', result.error);
}
return result;
}
async loadSettings() {
if (!window.electronAPI) return { inputDir: '', outputDir: '', mode: 'auto' };
return await window.electronAPI.loadSettings();
}
async saveSettings(settings) {
if (!window.electronAPI) return;
return await window.electronAPI.saveSettings(settings);
}
async selectFolder() {
if (!window.electronAPI) return null;
return await window.electronAPI.selectFolder();
}
isElectron() {
return !!window.electronAPI;
}
}
window.EDIBridge.WatcherBridge = WatcherBridge;