diff --git a/public/index.php b/public/index.php index 2b0915d..15d739f 100644 --- a/public/index.php +++ b/public/index.php @@ -45,6 +45,7 @@ $router->get('/accounts/search', fn($p) => (new AccountController())->search($p) // ── Belege ─────────────────────────────────────────────────── $router->get('/belege', fn($p) => (new BelegController())->index($p)); +$router->get('/belege/export', fn($p) => (new BelegController())->export($p)); $router->get('/belege/upload', fn($p) => (new BelegController())->uploadForm($p)); $router->post('/belege/upload', fn($p) => (new BelegController())->upload($p)); $router->get('/belege/{id}', fn($p) => (new BelegController())->show($p)); diff --git a/public/static/js/app.js b/public/static/js/app.js index f744567..bf5f372 100644 --- a/public/static/js/app.js +++ b/public/static/js/app.js @@ -341,10 +341,23 @@ document.addEventListener('click', (e) => { }); // ── Konto-Select: Doppelklick / Enter → Kontonr. ins Filterfeld ── +// selectedIndex ist beim click-Event garantiert korrekt; beim dblclick-Event +// kann er in manchen Browsern bei List-Boxes zurückgesetzt sein (kein Prior-Select), +// daher wird er beim click gecacht und als Fallback genutzt. +const _kontoNrCache = {}; +document.addEventListener('click', (e) => { + const sel = e.target.closest('select#kategorie_konto_id, select#zahlungskonto_id'); + if (!sel) return; + const opt = sel.options[sel.selectedIndex]; + if (opt && opt.value) _kontoNrCache[sel.id] = opt.textContent.split(' – ')[0].trim(); +}); + function transferKontoNr(select) { const opt = select.options[select.selectedIndex]; - if (!opt || !opt.value) return; - const kontoNr = opt.textContent.split(' – ')[0].trim(); + const kontoNr = (opt && opt.value) + ? opt.textContent.split(' – ')[0].trim() + : (_kontoNrCache[select.id] ?? null); + if (!kontoNr) return; const filterInput = document.querySelector(`.konto-filter[data-target="${select.id}"]`); if (filterInput) filterInput.value = kontoNr; } diff --git a/scripts/backup.sh b/scripts/backup.sh index 416c70f..4e49b98 100755 --- a/scripts/backup.sh +++ b/scripts/backup.sh @@ -12,7 +12,7 @@ mkdir -p "$BACKUP_DIR" echo "Erstelle Backup $TIMESTAMP..." # MariaDB Dump (aus dem Container) -docker compose exec -T db mysqldump \ +docker compose exec -T db mariadb-dump \ --user=steuerkram \ --password=steuerkram \ --single-transaction \ diff --git a/src/Controllers/BelegController.php b/src/Controllers/BelegController.php index 86b4dd4..8a4448d 100644 --- a/src/Controllers/BelegController.php +++ b/src/Controllers/BelegController.php @@ -44,10 +44,58 @@ class BelegController 'page' => $page, 'filter' => $filter, ]), - 'topbarActions' => '+ Beleg hochladen', + 'topbarActions' => 'CSV Export + Beleg hochladen', ]); } + public function export(array $params): void + { + $filter = $_GET; + unset($filter['page']); + $result = Beleg::all($filter, 1, 99999); + + header('Content-Type: text/csv; charset=utf-8'); + header('Content-Disposition: attachment; filename="belegliste-' . date('Ymd') . '.csv"'); + + $out = fopen('php://output', 'w'); + fprintf($out, chr(0xEF) . chr(0xBB) . chr(0xBF)); // UTF-8 BOM for Excel + + fputcsv($out, [ + 'Belegnr.', 'Belegdatum', 'Belegart', 'Lieferant', 'Rechnungsnr.', + 'Dateiname', 'Geschäftsjahr', 'Notizen', + 'Bewirtungsgrund', 'Teilnehmerliste', + 'Brutto (€)', 'Status', + ], ';'); + + foreach ($result['rows'] as $row) { + if ($row['locked']) { + $status = 'Gesperrt'; + } elseif ($row['booked']) { + $status = 'Gebucht'; + } else { + $status = 'Offen'; + } + + fputcsv($out, [ + $this->csvSanitize((string)($row['beleg_nr'] ?? '')), + $row['beleg_datum'] ?? '', + $row['belegtyp'] ?? '', + $this->csvSanitize((string)($row['supplier_name'] ?? '')), + $this->csvSanitize((string)($row['rechnungs_nr'] ?? '')), + $this->csvSanitize((string)($row['filename'] ?? '')), + (string)($row['fiscal_year_nr'] ?? ''), + $this->csvSanitize((string)($row['notes'] ?? '')), + $this->csvSanitize((string)($row['bewirtungsgrund'] ?? '')), + $this->csvSanitize((string)($row['teilnehmerliste'] ?? '')), + $row['betrag_brutto'] !== null + ? number_format((float)$row['betrag_brutto'], 2, ',', '') + : '', + $status, + ], ';'); + } + fclose($out); + } + public function uploadForm(array $params): void { $view = new View(); @@ -310,6 +358,14 @@ class BelegController echo json_encode(['ok' => true]); } + private function csvSanitize(string $value): string + { + if ($value !== '' && in_array($value[0], ['=', '+', '-', '@'], true)) { + return "'" . $value; + } + return $value; + } + private function renderView(string $template, array $data): string { extract($data, EXTR_SKIP); diff --git a/src/Models/Account.php b/src/Models/Account.php index b27a785..7ca1796 100644 --- a/src/Models/Account.php +++ b/src/Models/Account.php @@ -54,8 +54,8 @@ class Account public static function create(array $data): int { Database::execute( - 'INSERT INTO accounts (kontonummer, bezeichnung, kontenart, beschreibung, steuercode, ustva_kz) - VALUES (?, ?, ?, ?, ?, ?)', + 'INSERT INTO accounts (kontonummer, bezeichnung, kontenart, beschreibung, steuercode, ustva_kz, euer_zeile) + VALUES (?, ?, ?, ?, ?, ?, ?)', [ $data['kontonummer'], $data['bezeichnung'], @@ -63,6 +63,7 @@ class Account $data['beschreibung'] ?? null, $data['steuercode'] ?? null, $data['ustva_kz'] ?? null, + $data['euer_zeile'] !== '' ? ($data['euer_zeile'] ?? null) : null, ] ); return (int)Database::lastInsertId(); @@ -71,7 +72,7 @@ class Account public static function update(int $id, array $data): void { Database::execute( - 'UPDATE accounts SET kontonummer=?, bezeichnung=?, kontenart=?, beschreibung=?, steuercode=?, ustva_kz=?, aktiv=? + 'UPDATE accounts SET kontonummer=?, bezeichnung=?, kontenart=?, beschreibung=?, steuercode=?, ustva_kz=?, euer_zeile=?, aktiv=? WHERE id=?', [ $data['kontonummer'], @@ -80,6 +81,7 @@ class Account $data['beschreibung'] ?? null, $data['steuercode'] ?? null, $data['ustva_kz'] ?? null, + $data['euer_zeile'] !== '' ? ($data['euer_zeile'] ?? null) : null, (int)($data['aktiv'] ?? 1), $id, ] diff --git a/src/Views/accounts/form.php b/src/Views/accounts/form.php index edad660..2de49c6 100644 --- a/src/Views/accounts/form.php +++ b/src/Views/accounts/form.php @@ -68,6 +68,44 @@ +