diff --git a/client/src/components/CSVImportDialog.js b/client/src/components/CSVImportDialog.js
index 177bffd..76becc4 100644
--- a/client/src/components/CSVImportDialog.js
+++ b/client/src/components/CSVImportDialog.js
@@ -1,9 +1,5 @@
import React, { Component } from 'react';
import {
- Dialog,
- DialogTitle,
- DialogContent,
- DialogActions,
Button,
Typography,
Box,
@@ -14,6 +10,7 @@ import {
Tabs,
Tab,
Divider,
+ Paper,
} from '@mui/material';
import {
CloudUpload as UploadIcon,
@@ -30,7 +27,7 @@ const IMPORT_TYPES = {
DATEV_LINKS: 'DATEV_LINKS',
};
-class CSVImportDialog extends Component {
+class CSVImportPanel extends Component {
constructor(props) {
super(props);
this.state = {
@@ -276,10 +273,6 @@ class CSVImportDialog extends Component {
datevCsvData: null,
datevHeaders: null,
});
-
- if (this.props.onClose) {
- this.props.onClose();
- }
};
renderUploadPanel = ({ isBanking }) => {
@@ -341,86 +334,6 @@ class CSVImportDialog extends Component {
- {!isBanking && (
-
-
-
-
- Hinweise zum DATEV Beleglink-Upload
-
-
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer posuere, neque at efficitur
- blandit, sapien libero finibus nunc, a facilisis lacus arcu sed urna. Suspendisse potenti.
- Phasellus tincidunt, lorem in dictum lacinia, sem tortor ultrices risus, vitae porta odio
- mauris non neque. Sed vitae nibh dapibus, viverra velit nec, aliquet odio.
-
-
- Cras lacinia, massa a sagittis placerat, enim dolor fermentum lectus, in pulvinar mi risus ut
- ipsum. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis
- egestas. Mauris mattis lorem sit amet risus mattis volutpat. Proin sit amet hendrerit lectus.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- DATEV Beleglinks Beispiel – Platzhalter Illustration
-
-
-
-
- Beispiel-Screenshot (SVG Platzhalter mit Shapes). Ersetzen Sie dieses Bild später durch die endgültige Anleitungsgrafik.
-
-
- )}
-
{currentFile && (
@@ -461,7 +374,6 @@ class CSVImportDialog extends Component {
};
render() {
- const { open } = this.props;
const {
activeTab,
importing,
@@ -476,25 +388,22 @@ class CSVImportDialog extends Component {
const hasData = isBanking ? csvData : datevCsvData;
return (
-
+
+ {imported && (
+
+
+
+ )}
+
+
);
}
}
-export default CSVImportDialog;
\ No newline at end of file
+export default CSVImportPanel;
\ No newline at end of file
diff --git a/client/src/components/TableManagement.js b/client/src/components/TableManagement.js
index 0d1d2ba..a936e91 100644
--- a/client/src/components/TableManagement.js
+++ b/client/src/components/TableManagement.js
@@ -15,7 +15,7 @@ import {
import KreditorTable from './admin/KreditorTable';
import KontoTable from './admin/KontoTable';
import BUTable from './admin/BUTable';
-import CSVImportDialog from './CSVImportDialog';
+import CSVImportPanel from './CSVImportDialog';
class TableManagement extends Component {
constructor(props) {
@@ -90,9 +90,7 @@ class TableManagement extends Component {
Hier können Sie CSV-Dateien von Ihrer Bank importieren. Die Daten werden in die Datenbank gespeichert und können dann Banking-Konten zugeordnet werden.
- {}} // Always open in this tab
+
diff --git a/src/routes/data/csvImport.js b/src/routes/data/csvImport.js
index be8f4ca..00965f4 100644
--- a/src/routes/data/csvImport.js
+++ b/src/routes/data/csvImport.js
@@ -370,4 +370,203 @@ router.get('/csv-import-batches', authenticateToken, async (req, res) => {
}
});
+// Import DATEV Beleglinks to database
+router.post('/import-datev-beleglinks', authenticateToken, async (req, res) => {
+ try {
+ const { executeQuery } = require('../../config/database');
+ const { beleglinks, filename, batchId, headers } = req.body;
+
+ if (!beleglinks || !Array.isArray(beleglinks)) {
+ return res.status(400).json({ error: 'Beleglinks array is required' });
+ }
+
+ // Expected DATEV CSV headers from the example
+ const expectedHeaders = [
+ 'Belegart', 'Geschäftspartner-Name', 'Geschäftspartner-Konto', 'Rechnungsbetrag', 'WKZ',
+ 'Rechnungs-Nr.', 'Interne Re.-Nr.', 'Rechnungsdatum', 'BU', 'Konto', 'Konto-Bezeichnung',
+ 'Ware/Leistung', 'Zahlungszuordnung', 'Kontoumsatzzuordnung', 'Gebucht', 'Festgeschrieben',
+ 'Kopie', 'Eingangsdatum', 'Bezahlt', 'BezahltAm', 'Geschäftspartner-Ort', 'Skonto-Betrag 1',
+ 'Fällig mit Skonto 1', 'Skonto 1 in %', 'Skonto-Betrag 2', 'Fällig mit Skonto 2',
+ 'Skonto 2 in %', 'Fällig ohne Skonto', 'Steuer in %', 'USt-IdNr.', 'Kunden-Nr.',
+ 'KOST 1', 'KOST 2', 'KOST-Menge', 'Kurs', 'Nachricht', 'Freier Text', 'IBAN', 'BIC',
+ 'Bankkonto-Nr.', 'BLZ', 'Notiz', 'Land', 'Personalnummer', 'Nachname', 'Vorname',
+ 'Belegkategorie', 'Bezeichnung', 'Abrechnungsmonat', 'Gültig bis', 'Prüfungsrelevant',
+ 'Ablageort', 'Belegtyp', 'Herkunft', 'Leistungsdatum', 'Buchungstext', 'Beleg-ID',
+ 'Zahlungsbedingung', 'Geheftet', 'Gegenkonto', 'keine Überweisung/Lastschrift erstellen',
+ 'Aufgeteilt', 'Bereitgestellt', 'Freigegeben', 'FreigegebenAm', 'Erweiterte Belegdaten fehlen',
+ 'Periode fehlt', 'Rechnungsdaten beim Import fehlen'
+ ];
+
+ if (beleglinks.length === 0) {
+ return res.status(400).json({ error: 'No beleglink data found' });
+ }
+
+ const importBatchId = batchId || 'datev_import_' + Date.now();
+ let successCount = 0;
+ let errorCount = 0;
+ let updateCount = 0;
+ let insertCount = 0;
+ let skippedCount = 0;
+ const errors = [];
+
+ for (let i = 0; i < beleglinks.length; i++) {
+ const beleglink = beleglinks[i];
+
+ try {
+ // Skip empty rows or rows without Beleg-ID
+ const belegId = beleglink['Beleg-ID'];
+ if (!belegId || belegId.trim() === '') {
+ console.log(`Skipping row ${i + 1}: No Beleg-ID found`);
+ skippedCount++;
+ continue;
+ }
+
+ const validationErrors = [];
+
+ // Parse amount if available
+ let numericAmount = null;
+ if (beleglink['Rechnungsbetrag']) {
+ const amountStr = beleglink['Rechnungsbetrag'].toString().replace(/[^\d,.-]/g, '');
+ const normalizedAmount = amountStr.replace(',', '.');
+ numericAmount = parseFloat(normalizedAmount) || null;
+ }
+
+ // Parse date if available
+ let parsedDate = null;
+ if (beleglink['Rechnungsdatum']) {
+ const dateStr = beleglink['Rechnungsdatum'].trim();
+ const dateParts = dateStr.split(/[.\/\-]/);
+ if (dateParts.length === 3) {
+ const day = parseInt(dateParts[0], 10);
+ const month = parseInt(dateParts[1], 10) - 1;
+ let year = parseInt(dateParts[2], 10);
+
+ if (year < 100) {
+ year += (year < 50) ? 2000 : 1900;
+ }
+
+ parsedDate = new Date(year, month, day);
+
+ if (isNaN(parsedDate.getTime())) {
+ parsedDate = null;
+ }
+ }
+ }
+
+ // First, check if a record with this datevlink already exists
+ const checkExistingDatevLink = `
+ SELECT kUmsatzBeleg FROM eazybusiness.dbo.tUmsatzBeleg WHERE datevlink = @datevlink
+ `;
+
+ const existingDatevLink = await executeQuery(checkExistingDatevLink, { datevlink: belegId });
+
+ if (existingDatevLink.recordset.length > 0) {
+ // Record with this datevlink already exists - skip
+ console.log(`Datevlink already exists, skipping: ${belegId}`);
+ skippedCount++;
+ continue;
+ }
+
+ // Extract key from filename in 'Herkunft' column
+ // Examples: "Rechnung146.pdf" -> key 146 for tRechnung
+ // "UmsatzBeleg192.pdf" -> key 192 for tUmsatzBeleg
+ const herkunft = beleglink['Herkunft'];
+ if (!herkunft || herkunft.trim() === '') {
+ console.log(`Skipping row ${i + 1}: No filename in Herkunft column`);
+ skippedCount++;
+ continue;
+ }
+
+ // Extract the key from filename patterns
+ let matchFound = false;
+
+ // Pattern: UmsatzBeleg{key}.pdf -> match with tUmsatzBeleg.kUmsatzBeleg
+ const umsatzBelegMatch = herkunft.match(/UmsatzBeleg(\d+)\.pdf/i);
+ if (umsatzBelegMatch) {
+ const kUmsatzBeleg = parseInt(umsatzBelegMatch[1], 10);
+
+ const updateQuery = `
+ UPDATE eazybusiness.dbo.tUmsatzBeleg
+ SET datevlink = @datevlink
+ WHERE kUmsatzBeleg = @kUmsatzBeleg AND (datevlink IS NULL OR datevlink = '')
+ `;
+
+ const updateResult = await executeQuery(updateQuery, {
+ datevlink: belegId,
+ kUmsatzBeleg: kUmsatzBeleg
+ });
+
+ if (updateResult.rowsAffected && updateResult.rowsAffected[0] > 0) {
+ updateCount++;
+ console.log(`Added datevlink ${belegId} to tUmsatzBeleg.kUmsatzBeleg: ${kUmsatzBeleg}`);
+ matchFound = true;
+ } else {
+ console.log(`Skipping row ${i + 1}: UmsatzBeleg ${kUmsatzBeleg} nicht gefunden oder datevlink bereits gesetzt`);
+ skippedCount++;
+ }
+ }
+
+ // Pattern: Rechnung{key}.pdf -> match with tPdfObjekt.kPdfObjekt
+ const rechnungMatch = herkunft.match(/Rechnung(\d+)\.pdf/i);
+ if (!matchFound && rechnungMatch) {
+ const kPdfObjekt = parseInt(rechnungMatch[1], 10);
+
+ const updateQuery = `
+ UPDATE eazybusiness.dbo.tPdfObjekt
+ SET datevlink = @datevlink
+ WHERE kPdfObjekt = @kPdfObjekt AND (datevlink IS NULL OR datevlink = '')
+ `;
+
+ const updateResult = await executeQuery(updateQuery, {
+ datevlink: belegId,
+ kPdfObjekt: kPdfObjekt
+ });
+
+ if (updateResult.rowsAffected && updateResult.rowsAffected[0] > 0) {
+ updateCount++;
+ console.log(`Added datevlink ${belegId} to tPdfObjekt.kPdfObjekt: ${kPdfObjekt}`);
+ matchFound = true;
+ } else {
+ console.log(`Skipping row ${i + 1}: PdfObjekt ${kPdfObjekt} nicht gefunden oder datevlink bereits gesetzt`);
+ skippedCount++;
+ }
+ }
+
+ if (!matchFound) {
+ console.log(`Skipping row ${i + 1}: Unbekanntes Dateiformat '${herkunft}' (erwartet: UmsatzBeleg{key}.pdf oder Rechnung{key}.pdf)`);
+ skippedCount++;
+ continue;
+ }
+
+ successCount++;
+ } catch (error) {
+ console.error('Error processing beleglink ' + (i + 1) + ':', error);
+ errors.push({
+ row: i + 1,
+ error: error.message,
+ beleglink: beleglink
+ });
+ errorCount++;
+ }
+ }
+
+ res.json({
+ success: true,
+ batchId: importBatchId,
+ imported: updateCount, // Number of datevlinks actually added/updated
+ processed: successCount,
+ updated: updateCount,
+ inserted: insertCount,
+ skipped: skippedCount, // Records skipped (existing datevlinks)
+ errors: errorCount, // Only actual errors, not skipped records
+ details: errors.length > 0 ? errors : undefined,
+ message: `${updateCount} datevlinks hinzugefügt, ${skippedCount} bereits vorhanden, ${errorCount} Fehler`
+ });
+
+ } catch (error) {
+ console.error('Error importing DATEV beleglinks:', error);
+ res.status(500).json({ error: 'Failed to import DATEV beleglinks' });
+ }
+});
+
module.exports = router;
\ No newline at end of file