finanzen hinzugefügt.

This commit is contained in:
cay 2026-03-27 13:25:38 +00:00
parent 95f4d9f271
commit 6927d0be3e
4 changed files with 86 additions and 13 deletions

View File

@ -16,7 +16,8 @@
"dotenv": "^16.3.1", "dotenv": "^16.3.1",
"dns": "^0.2.2", "dns": "^0.2.2",
"pdfkit": "^0.14.0", "pdfkit": "^0.14.0",
"node-cron": "^3.0.3" "node-cron": "^3.0.3",
"multer": "^1.4.5-lts.1"
}, },
"devDependencies": { "devDependencies": {
"nodemon": "^3.0.1" "nodemon": "^3.0.1"

View File

@ -1248,3 +1248,44 @@ body:not(.admin-body) > * {
.expiry-urgent { background:#fee2e2; color:var(--error); } .expiry-urgent { background:#fee2e2; color:var(--error); }
.expiry-warning { background:#fffbeb; color:var(--warning); } .expiry-warning { background:#fffbeb; color:var(--warning); }
.expiry-normal { background:#f0fdf4; color:var(--success); } .expiry-normal { background:#f0fdf4; color:var(--success); }
/* ---- CSV File Upload ---- */
.file-upload-wrap { position: relative; }
.file-upload-wrap input[type="file"] {
position: absolute; width: 1px; height: 1px; opacity: 0;
}
.file-upload-label {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 16px;
border: 1.5px dashed var(--border);
border-radius: 8px;
cursor: pointer;
font-size: 0.9rem;
color: var(--text-muted);
transition: all 0.2s;
background: var(--bg);
}
.file-upload-label:hover {
border-color: var(--primary);
color: var(--primary);
background: #f0f0ff;
}
.file-upload-label::before { content: '📁'; font-size: 1.1rem; }
.import-divider {
display: flex;
align-items: center;
gap: 12px;
margin: 16px 0;
color: var(--text-muted);
font-size: 0.85rem;
}
.import-divider::before,
.import-divider::after {
content: '';
flex: 1;
height: 1px;
background: var(--border);
}

View File

@ -1,4 +1,6 @@
const express = require('express'); const express = require('express');
const multer = require('multer');
const upload = multer({ storage: multer.memoryStorage(), limits: { fileSize: 1024*1024 } });
const router = express.Router(); const router = express.Router();
const db = require('../config/database'); const db = require('../config/database');
const { requireAdmin } = require('../middleware/auth'); const { requireAdmin } = require('../middleware/auth');
@ -175,11 +177,17 @@ router.post('/chargebacks/:id/resolve', requireAdmin, async (req, res) => {
}); });
// CSV Import Rückläufer // CSV Import Rückläufer
router.post('/chargebacks/import', requireAdmin, async (req, res) => { router.post('/chargebacks/import', requireAdmin, upload.single('csv_file'), async (req, res) => {
const { csv_data } = req.body; // Datei hat Vorrang, sonst Textfeld
if (!csv_data || !csv_data.trim()) return res.redirect('/admin/finance?error=Keine+Daten+eingegeben'); let rawData = '';
if (req.file && req.file.buffer.length > 0) {
rawData = req.file.buffer.toString('utf-8').replace(/\r/g, '');
} else if (req.body.csv_data && req.body.csv_data.trim()) {
rawData = req.body.csv_data.trim();
}
if (!rawData) return res.redirect('/admin/finance?error=Keine+Daten+eingegeben');
try { try {
const lines = csv_data.trim().split('\n').filter(l => l.trim()); const lines = rawData.trim().split('\n').filter(l => l.trim());
let imported = 0; let imported = 0;
for (const line of lines) { for (const line of lines) {
const cols = line.split(';').map(c => c.trim().replace(/"/g, '')); const cols = line.split(';').map(c => c.trim().replace(/"/g, ''));

View File

@ -365,16 +365,34 @@
<h3>Rückläufer CSV Import</h3> <h3>Rückläufer CSV Import</h3>
<button onclick="toggleModal('importChargebackModal')" class="modal-close">✕</button> <button onclick="toggleModal('importChargebackModal')" class="modal-close">✕</button>
</div> </div>
<form method="POST" action="/admin/finance/chargebacks/import"> <form method="POST" action="/admin/finance/chargebacks/import"
enctype="multipart/form-data">
<p style="font-size:0.8rem;color:var(--text-muted);margin-bottom:16px">
Format pro Zeile: <code>IBAN;Betrag;Datum;Grund</code><br>
Beispiel: <code>DE89370400440532013000;29,95;2026-04-05;Rücklastschrift</code>
</p>
<!-- Option 1: Datei -->
<div class="form-group"> <div class="form-group">
<label>CSV-Daten einfügen</label> <label>📁 CSV-Datei hochladen</label>
<p style="font-size:0.8rem;color:var(--text-muted);margin-bottom:8px"> <div class="file-upload-wrap">
Format pro Zeile: <code>IBAN;Betrag;Datum;Grund</code><br> <input type="file" name="csv_file" id="csvFile" accept=".csv,.txt"
Beispiel: <code>DE89370400440532013000;29,95;2026-04-05;Rücklastschrift</code> onchange="showFileName(this)">
</p> <label for="csvFile" class="file-upload-label">
<textarea name="csv_data" class="form-control" rows="6" <span id="fileNameDisplay">Datei auswählen...</span>
placeholder="IBAN;Betrag;Datum;Grund"></textarea> </label>
</div>
</div> </div>
<div class="import-divider"><span>oder</span></div>
<!-- Option 2: Textfeld -->
<div class="form-group">
<label>📋 Daten einfügen</label>
<textarea name="csv_data" class="form-control" rows="5"
placeholder="IBAN;Betrag;Datum;Grund&#10;DE89370400440532013000;29,95;2026-04-05;Rücklastschrift"></textarea>
</div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" onclick="toggleModal('importChargebackModal')" class="btn btn-outline">Abbrechen</button> <button type="button" onclick="toggleModal('importChargebackModal')" class="btn btn-outline">Abbrechen</button>
<button type="submit" class="btn btn-primary">📥 Importieren</button> <button type="submit" class="btn btn-primary">📥 Importieren</button>
@ -480,6 +498,11 @@ function showTab(name) {
function toggleModal(id) { function toggleModal(id) {
document.getElementById(id).classList.toggle('hidden'); document.getElementById(id).classList.toggle('hidden');
} }
function showFileName(input) {
const display = document.getElementById('fileNameDisplay');
display.textContent = input.files.length > 0 ? input.files[0].name : 'Datei auswählen...';
}
</script> </script>
</body> </body>
</html> </html>