From 4fb0b21a903cae655d44e833b3fe1d9f0db2f03b Mon Sep 17 00:00:00 2001 From: fds2610-bot Date: Fri, 15 May 2026 19:09:45 +0200 Subject: [PATCH] Feature: Bank-Zuordnung zeigt Ausgangsrechnungen inkl. Rechnungen ohne Beleg MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Eingabe-Spalte aus Beleg-Liste entfernt (alloc_betrag als Hidden-Field) - Beleg-Liste nach Datum neu->alt sortiert - Ausgangsrechnungen ohne Bank-Link in offene Belege aufgenommen - Rechnungen mit fehlendem Beleg (beleg_id IS NULL) werden angezeigt - Beim Verknüpfen solcher Rechnungen wird Beleg-PDF automatisch erstellt --- public/index.php | 1 + src/Controllers/BankController.php | 36 ++++++++++++++++++++-- src/Controllers/InvoiceController.php | 6 ++-- src/Models/Beleg.php | 44 +++++++++++++++++++++++++++ src/Views/bank/show.php | 33 ++++++++++++-------- 5 files changed, 103 insertions(+), 17 deletions(-) diff --git a/public/index.php b/public/index.php index 5a3e530..70ec656 100644 --- a/public/index.php +++ b/public/index.php @@ -72,6 +72,7 @@ $router->post('/bank/upload/confirm', fn($p) => (new BankController())->up $router->get('/bank/{id}', fn($p) => (new BankController())->show($p)); $router->post('/bank/{id}/status', fn($p) => (new BankController())->updateStatus($p)); $router->post('/bank/{id}/link', fn($p) => (new BankController())->linkBeleg($p)); +$router->post('/bank/{id}/link-invoice', fn($p) => (new BankController())->linkInvoice($p)); $router->post('/bank/{id}/unlink', fn($p) => (new BankController())->unlinkBeleg($p)); $router->post('/bank/{id}/beleg', fn($p) => (new BankController())->uploadBeleg($p)); diff --git a/src/Controllers/BankController.php b/src/Controllers/BankController.php index 47a9f11..069f8a7 100644 --- a/src/Controllers/BankController.php +++ b/src/Controllers/BankController.php @@ -6,8 +6,10 @@ namespace Steuerkram\Controllers; use Steuerkram\Models\BankImportFormat; use Steuerkram\Models\BankTransaction; +use Steuerkram\Controllers\InvoiceController; use Steuerkram\Database; use Steuerkram\Models\Beleg; +use Steuerkram\Models\Invoice; use Steuerkram\Services\BankImportParser; use Steuerkram\View; @@ -161,7 +163,7 @@ class BankController $linkedBelege = BankTransaction::getLinkedBelege($id); $remainingBetrag = BankTransaction::getRemainingBetrag($id); - $offeneBelege = Beleg::all(['booked' => '0'], 1, 200); + $offeneBelege = Beleg::getOffeneFuerBankzuordnung(); $view = new View(); $view->render('layout/base.php', [ @@ -170,7 +172,7 @@ class BankController 'transaction' => $transaction, 'linkedBelege' => $linkedBelege, 'remainingBetrag' => $remainingBetrag, - 'offeneBelege' => $offeneBelege['rows'], + 'offeneBelege' => $offeneBelege, ]), ]); } @@ -205,6 +207,36 @@ class BankController View::redirect("/belege/$belegId?bank_tx=$id&alloc_betrag=$betrag"); } + public function linkInvoice(array $params): void + { + $id = (int)($params['id'] ?? 0); + $invoiceId = (int)($_POST['invoice_id'] ?? 0); + $betrag = round((float)str_replace(',', '.', $_POST['alloc_betrag'] ?? ''), 2); + + if (!$invoiceId) { + View::redirect("/bank/$id", 'Keine Rechnung ausgewählt.', 'error'); + } + + $invoice = Invoice::find($invoiceId); + if (!$invoice) { + View::redirect("/bank/$id", 'Rechnung nicht gefunden.', 'error'); + } + + try { + InvoiceController::createBeleg($invoice); + $invoice = Invoice::find($invoiceId); + } catch (\Exception $e) { + View::redirect("/bank/$id", 'Beleg konnte nicht erstellt werden: ' . $e->getMessage(), 'error'); + } + + $belegId = (int)($invoice['beleg_id'] ?? 0); + if (!$belegId) { + View::redirect("/bank/$id", 'Beleg-Erstellung fehlgeschlagen.', 'error'); + } + + View::redirect("/belege/$belegId?bank_tx=$id&alloc_betrag=$betrag"); + } + public function unlinkBeleg(array $params): void { $id = (int)($params['id'] ?? 0); diff --git a/src/Controllers/InvoiceController.php b/src/Controllers/InvoiceController.php index 1eaeec4..bcd84e2 100644 --- a/src/Controllers/InvoiceController.php +++ b/src/Controllers/InvoiceController.php @@ -212,7 +212,7 @@ class InvoiceController \Steuerkram\Database::beginTransaction(); $nr = Invoice::finalize($id); $invoice = Invoice::find($id); - $belegNr = $this->createBeleg($invoice); + $belegNr = self::createBeleg($invoice); \Steuerkram\Database::commit(); View::redirect("/invoices/{$id}", "Rechnung {$nr} wurde finalisiert (Beleg {$belegNr}). Bitte Zahlungseingang erfassen."); } catch (\PDOException $e) { @@ -478,7 +478,7 @@ class InvoiceController } } - private function createBeleg(array $invoice): string + public static function createBeleg(array $invoice): string { $id = (int)$invoice['id']; $lines = Invoice::lines($id); @@ -550,7 +550,7 @@ class InvoiceController $stornoId = Invoice::createCancellation((int)$params['id']); $storno = Invoice::find($stornoId); if ($storno) { - $this->createBeleg($storno); + self::createBeleg($storno); } View::redirect('/invoices/' . $stornoId, 'Stornorechnung erstellt.'); } diff --git a/src/Models/Beleg.php b/src/Models/Beleg.php index 0faad49..1341d6e 100644 --- a/src/Models/Beleg.php +++ b/src/Models/Beleg.php @@ -72,6 +72,50 @@ class Beleg return ['rows' => $rows, 'total' => $total, 'pages' => (int)ceil($total / $perPage)]; } + public static function getOffeneFuerBankzuordnung(): array + { + $belege = Database::fetchAll( + 'SELECT b.*, + COALESCE( + (SELECT SUM(bk.betrag_brutto) FROM bookings bk + WHERE bk.beleg_id = b.id AND bk.is_reversal = 0 AND bk.parent_booking_id IS NULL), + b.betrag_brutto + ) AS betrag_brutto, + fy.year as fiscal_year_nr, + s.name as supplier_name + FROM belege b + LEFT JOIN fiscal_years fy ON b.fiscal_year_id = fy.id + LEFT JOIN suppliers s ON b.supplier_id = s.id + WHERE ( + (b.belegtyp = \'Eingangsrechnung\' + AND NOT EXISTS (SELECT 1 FROM bookings bk2 WHERE bk2.beleg_id = b.id)) + OR + (b.belegtyp = \'Ausgangsrechnung\' + AND NOT EXISTS (SELECT 1 FROM bank_transaction_belege btb WHERE btb.beleg_id = b.id)) + )', + [] + ); + + $rechnungenOhneBeleg = Database::fetchAll( + 'SELECT NULL AS id, + i.id AS invoice_id, + i.rechnungs_nr AS beleg_nr, + i.rechnungs_datum AS beleg_datum, + \'Ausgangsrechnung\' AS belegtyp, + i.betrag_brutto, + COALESCE(NULLIF(TRIM(c.firma), \'\'), CONCAT(TRIM(c.vorname), \' \', TRIM(c.nachname))) AS supplier_name + FROM invoices i + JOIN customers c ON c.id = i.customer_id + WHERE i.status IN (\'Finalisiert\', \'Gesendet\') + AND i.beleg_id IS NULL', + [] + ); + + $alle = array_merge($belege, $rechnungenOhneBeleg); + usort($alle, fn($a, $b) => strcmp($b['beleg_datum'] ?? '', $a['beleg_datum'] ?? '')); + return $alle; + } + public static function find(int $id): array|false { return Database::fetchOne( diff --git a/src/Views/bank/show.php b/src/Views/bank/show.php index 21723de..9919174 100644 --- a/src/Views/bank/show.php +++ b/src/Views/bank/show.php @@ -222,18 +222,22 @@ $restFormatted = number_format($remaining, 2, ',', '.'); Typ Lieferant Brutto - Zuzuordnen (€) + + style="cursor:" + > - + + + + + @@ -243,17 +247,22 @@ $restFormatted = number_format($remaining, 2, ',', '.'); –' ?> - -
+ + + - +
+ +
+ + + +
+