diff --git a/public/index.php b/public/index.php index dc3b910..422201f 100644 --- a/public/index.php +++ b/public/index.php @@ -104,6 +104,7 @@ $router->post('/invoices/{id}/delete', fn($p) => (new InvoiceController())->dest $router->post('/invoices/{id}/lines', fn($p) => (new InvoiceController())->addLine($p)); $router->post('/invoices/{id}/lines/{line_id}/delete', fn($p) => (new InvoiceController())->deleteLine($p)); $router->post('/invoices/{id}/send', fn($p) => (new InvoiceController())->send($p)); +$router->post('/invoices/{id}/quick-book', fn($p) => (new InvoiceController())->quickBook($p)); // ── Anlagevermögen ─────────────────────────────────────────── $router->get('/assets', fn($p) => (new AssetController())->index($p)); diff --git a/src/Controllers/InvoiceController.php b/src/Controllers/InvoiceController.php index 890674f..81045c2 100644 --- a/src/Controllers/InvoiceController.php +++ b/src/Controllers/InvoiceController.php @@ -119,6 +119,14 @@ class InvoiceController ); } + // Buchungen + Konten für das Zahlungs-Modal laden (nur wenn Beleg vorhanden) + $bookings = []; + $accounts = []; + if (!empty($invoice['beleg_id'])) { + $bookings = Booking::forBeleg((int)$invoice['beleg_id']); + $accounts = Account::all(['aktiv' => 1]); + } + $view = new View(); $view->render('layout/base.php', [ 'pageTitle' => 'Rechnung ' . ($invoice['rechnungs_nr'] ?? 'Entwurf'), @@ -128,6 +136,8 @@ class InvoiceController 'settings' => $settings, 'stornorechnung' => $stornorechnung ?: null, 'originalrechnung' => $originalrechnung, + 'bookings' => $bookings, + 'accounts' => $accounts, ]), ]); } @@ -340,6 +350,10 @@ class InvoiceController } if ($errors) { + if (!empty($data['_from_modal'])) { + View::redirect("/invoices/{$id}", implode(' ', $errors), 'error'); + return; + } $bookings = Booking::forBeleg($belegId); $accounts = Account::all(['aktiv' => 1]); $view = new View(); @@ -360,7 +374,8 @@ class InvoiceController $data['beleg_id'] = $belegId; $data['buchungstyp'] = 'Einnahme'; Booking::create($data); - View::redirect("/invoices/{$id}/book", 'Buchung erfasst.'); + $returnUrl = !empty($data['_from_modal']) ? "/invoices/{$id}" : "/invoices/{$id}/book"; + View::redirect($returnUrl, 'Buchung erfasst.'); } catch (\PDOException $e) { error_log('[Steuerkram] PDO-Fehler in InvoiceController::storeBooking(): ' . $e->getMessage()); $bookings = Booking::forBeleg($belegId); @@ -393,6 +408,30 @@ class InvoiceController } } + public function quickBook(array $params): void + { + $id = (int)$params['id']; + $invoice = Invoice::find($id); + if (!$invoice) { http_response_code(404); return; } + + $data = $_POST; + if (empty($data['kategorie_konto_id']) || empty($data['zahlungskonto_id']) || empty($data['buchungs_datum'])) { + View::redirect("/invoices/{$id}", 'Bitte Konto und Datum angeben.', 'error'); + return; + } + try { + $cnt = count(Invoice::createBookingsFromLines( + $id, + (int)$data['kategorie_konto_id'], + (int)$data['zahlungskonto_id'], + $data['buchungs_datum'] + )); + View::redirect("/invoices/{$id}", "{$cnt} Buchung(en) automatisch erfasst."); + } catch (\RuntimeException $e) { + View::redirect("/invoices/{$id}", $e->getMessage(), 'error'); + } + } + private function createBeleg(array $invoice): string { $id = (int)$invoice['id']; diff --git a/src/Models/Invoice.php b/src/Models/Invoice.php index 50c9358..a82cc4b 100644 --- a/src/Models/Invoice.php +++ b/src/Models/Invoice.php @@ -162,6 +162,39 @@ class Invoice Database::execute('UPDATE invoices SET status=\'Bezahlt\', updated_at=NOW() WHERE id=?', [$id]); } + public static function createBookingsFromLines( + int $invoiceId, + int $kategorieKontoId, + int $zahlungskontoId, + string $buchungsDatum + ): array { + $inv = self::find($invoiceId); + if (!$inv) throw new \RuntimeException('Rechnung nicht gefunden.'); + if (empty($inv['beleg_id'])) throw new \RuntimeException('Rechnung hat keinen Beleg (noch nicht finalisiert?).'); + + $gruppen = []; + foreach (self::lines($invoiceId) as $line) { + if (($line['type'] ?? 'position') !== 'position') continue; + $satz = (float)$line['ust_satz']; + $gruppen[$satz] = ($gruppen[$satz] ?? 0.0) + round((float)$line['betrag_netto'], 2); + } + + $ids = []; + foreach ($gruppen as $satz => $netto) { + $ids[] = Booking::create([ + 'beleg_id' => (int)$inv['beleg_id'], + 'buchungs_datum' => $buchungsDatum, + 'buchungstyp' => 'Einnahme', + 'kategorie_konto_id' => $kategorieKontoId, + 'zahlungskonto_id' => $zahlungskontoId, + 'betrag_netto' => round($netto, 2), + 'ust_satz' => $satz, + 'buchungstext' => 'Ausgangsrechnung ' . $inv['rechnungs_nr'], + ]); + } + return $ids; + } + public static function cancel(int $id): void { $invoice = self::find($id); diff --git a/src/Views/invoices/show.php b/src/Views/invoices/show.php index 5719302..a2340c2 100644 --- a/src/Views/invoices/show.php +++ b/src/Views/invoices/show.php @@ -50,8 +50,9 @@ $badgeClass = match($invoice['status']) { - - Zahlungseingang erfassen + +