Feature: Bank-Zuordnung zeigt Ausgangsrechnungen inkl. Rechnungen ohne Beleg
- 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
This commit is contained in:
parent
700dc2952f
commit
4fb0b21a90
5 changed files with 103 additions and 17 deletions
|
|
@ -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->get('/bank/{id}', fn($p) => (new BankController())->show($p));
|
||||||
$router->post('/bank/{id}/status', fn($p) => (new BankController())->updateStatus($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', 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}/unlink', fn($p) => (new BankController())->unlinkBeleg($p));
|
||||||
$router->post('/bank/{id}/beleg', fn($p) => (new BankController())->uploadBeleg($p));
|
$router->post('/bank/{id}/beleg', fn($p) => (new BankController())->uploadBeleg($p));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,10 @@ namespace Steuerkram\Controllers;
|
||||||
|
|
||||||
use Steuerkram\Models\BankImportFormat;
|
use Steuerkram\Models\BankImportFormat;
|
||||||
use Steuerkram\Models\BankTransaction;
|
use Steuerkram\Models\BankTransaction;
|
||||||
|
use Steuerkram\Controllers\InvoiceController;
|
||||||
use Steuerkram\Database;
|
use Steuerkram\Database;
|
||||||
use Steuerkram\Models\Beleg;
|
use Steuerkram\Models\Beleg;
|
||||||
|
use Steuerkram\Models\Invoice;
|
||||||
use Steuerkram\Services\BankImportParser;
|
use Steuerkram\Services\BankImportParser;
|
||||||
use Steuerkram\View;
|
use Steuerkram\View;
|
||||||
|
|
||||||
|
|
@ -161,7 +163,7 @@ class BankController
|
||||||
|
|
||||||
$linkedBelege = BankTransaction::getLinkedBelege($id);
|
$linkedBelege = BankTransaction::getLinkedBelege($id);
|
||||||
$remainingBetrag = BankTransaction::getRemainingBetrag($id);
|
$remainingBetrag = BankTransaction::getRemainingBetrag($id);
|
||||||
$offeneBelege = Beleg::all(['booked' => '0'], 1, 200);
|
$offeneBelege = Beleg::getOffeneFuerBankzuordnung();
|
||||||
|
|
||||||
$view = new View();
|
$view = new View();
|
||||||
$view->render('layout/base.php', [
|
$view->render('layout/base.php', [
|
||||||
|
|
@ -170,7 +172,7 @@ class BankController
|
||||||
'transaction' => $transaction,
|
'transaction' => $transaction,
|
||||||
'linkedBelege' => $linkedBelege,
|
'linkedBelege' => $linkedBelege,
|
||||||
'remainingBetrag' => $remainingBetrag,
|
'remainingBetrag' => $remainingBetrag,
|
||||||
'offeneBelege' => $offeneBelege['rows'],
|
'offeneBelege' => $offeneBelege,
|
||||||
]),
|
]),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
@ -205,6 +207,36 @@ class BankController
|
||||||
View::redirect("/belege/$belegId?bank_tx=$id&alloc_betrag=$betrag");
|
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
|
public function unlinkBeleg(array $params): void
|
||||||
{
|
{
|
||||||
$id = (int)($params['id'] ?? 0);
|
$id = (int)($params['id'] ?? 0);
|
||||||
|
|
|
||||||
|
|
@ -212,7 +212,7 @@ class InvoiceController
|
||||||
\Steuerkram\Database::beginTransaction();
|
\Steuerkram\Database::beginTransaction();
|
||||||
$nr = Invoice::finalize($id);
|
$nr = Invoice::finalize($id);
|
||||||
$invoice = Invoice::find($id);
|
$invoice = Invoice::find($id);
|
||||||
$belegNr = $this->createBeleg($invoice);
|
$belegNr = self::createBeleg($invoice);
|
||||||
\Steuerkram\Database::commit();
|
\Steuerkram\Database::commit();
|
||||||
View::redirect("/invoices/{$id}", "Rechnung {$nr} wurde finalisiert (Beleg {$belegNr}). Bitte Zahlungseingang erfassen.");
|
View::redirect("/invoices/{$id}", "Rechnung {$nr} wurde finalisiert (Beleg {$belegNr}). Bitte Zahlungseingang erfassen.");
|
||||||
} catch (\PDOException $e) {
|
} 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'];
|
$id = (int)$invoice['id'];
|
||||||
$lines = Invoice::lines($id);
|
$lines = Invoice::lines($id);
|
||||||
|
|
@ -550,7 +550,7 @@ class InvoiceController
|
||||||
$stornoId = Invoice::createCancellation((int)$params['id']);
|
$stornoId = Invoice::createCancellation((int)$params['id']);
|
||||||
$storno = Invoice::find($stornoId);
|
$storno = Invoice::find($stornoId);
|
||||||
if ($storno) {
|
if ($storno) {
|
||||||
$this->createBeleg($storno);
|
self::createBeleg($storno);
|
||||||
}
|
}
|
||||||
View::redirect('/invoices/' . $stornoId, 'Stornorechnung erstellt.');
|
View::redirect('/invoices/' . $stornoId, 'Stornorechnung erstellt.');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,50 @@ class Beleg
|
||||||
return ['rows' => $rows, 'total' => $total, 'pages' => (int)ceil($total / $perPage)];
|
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
|
public static function find(int $id): array|false
|
||||||
{
|
{
|
||||||
return Database::fetchOne(
|
return Database::fetchOne(
|
||||||
|
|
|
||||||
|
|
@ -222,18 +222,22 @@ $restFormatted = number_format($remaining, 2, ',', '.');
|
||||||
<th>Typ</th>
|
<th>Typ</th>
|
||||||
<th>Lieferant</th>
|
<th>Lieferant</th>
|
||||||
<th class="td-right">Brutto</th>
|
<th class="td-right">Brutto</th>
|
||||||
<th class="td-right" style="width:130px">Zuzuordnen (€)</th>
|
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<?php foreach ($offeneBelege as $b): ?>
|
<?php foreach ($offeneBelege as $b): ?>
|
||||||
|
<?php $hatBeleg = $b['id'] !== null; ?>
|
||||||
<tr class="beleg-row"
|
<tr class="beleg-row"
|
||||||
data-beleg-id="<?= (int)$b['id'] ?>"
|
data-beleg-id="<?= (int)$b['id'] ?>"
|
||||||
style="cursor:pointer"
|
style="cursor:<?= $hatBeleg ? 'pointer' : 'default' ?>"
|
||||||
title="Klicken zum Vorschau anzeigen">
|
<?= $hatBeleg ? 'title="Klicken zum Vorschau anzeigen"' : '' ?>>
|
||||||
<td class="td-mono" onclick="event.stopPropagation()">
|
<td class="td-mono" onclick="event.stopPropagation()">
|
||||||
<a href="/belege/<?= (int)$b['id'] ?>" target="_blank"><?= $h($b['beleg_nr']) ?></a>
|
<?php if ($hatBeleg): ?>
|
||||||
|
<a href="/belege/<?= (int)$b['id'] ?>" target="_blank"><?= $h($b['beleg_nr']) ?></a>
|
||||||
|
<?php else: ?>
|
||||||
|
<a href="/invoices/<?= (int)$b['invoice_id'] ?>" target="_blank"><?= $h($b['beleg_nr']) ?></a>
|
||||||
|
<?php endif; ?>
|
||||||
</td>
|
</td>
|
||||||
<td class="td-mono"><?= $h(View::formatDate($b['beleg_datum'])) ?></td>
|
<td class="td-mono"><?= $h(View::formatDate($b['beleg_datum'])) ?></td>
|
||||||
<td><span class="badge badge-gray"><?= $h($b['belegtyp']) ?></span></td>
|
<td><span class="badge badge-gray"><?= $h($b['belegtyp']) ?></span></td>
|
||||||
|
|
@ -243,17 +247,22 @@ $restFormatted = number_format($remaining, 2, ',', '.');
|
||||||
<td class="td-right td-mono">
|
<td class="td-right td-mono">
|
||||||
<?= $b['betrag_brutto'] ? $fmt($b['betrag_brutto']) . ' €' : '<span class="text-muted">–</span>' ?>
|
<?= $b['betrag_brutto'] ? $fmt($b['betrag_brutto']) . ' €' : '<span class="text-muted">–</span>' ?>
|
||||||
</td>
|
</td>
|
||||||
<td class="td-right" onclick="event.stopPropagation()">
|
<td onclick="event.stopPropagation()">
|
||||||
<form method="post" action="/bank/<?= (int)$transaction['id'] ?>/link"
|
<?php if ($hatBeleg): ?>
|
||||||
style="display:flex;gap:.4rem;align-items:center;justify-content:flex-end">
|
<form method="post" action="/bank/<?= (int)$transaction['id'] ?>/link">
|
||||||
<input type="hidden" name="beleg_id" value="<?= (int)$b['id'] ?>">
|
<input type="hidden" name="beleg_id" value="<?= (int)$b['id'] ?>">
|
||||||
<input type="number" name="alloc_betrag"
|
<input type="hidden" name="alloc_betrag"
|
||||||
value="<?= $h(number_format($remaining, 2, '.', '')) ?>"
|
value="<?= $h(number_format($remaining, 2, '.', '')) ?>">
|
||||||
min="0.01" step="0.01"
|
|
||||||
style="width:90px;text-align:right"
|
|
||||||
required>
|
|
||||||
<button type="submit" class="btn btn-primary btn-sm">Verknüpfen</button>
|
<button type="submit" class="btn btn-primary btn-sm">Verknüpfen</button>
|
||||||
</form>
|
</form>
|
||||||
|
<?php else: ?>
|
||||||
|
<form method="post" action="/bank/<?= (int)$transaction['id'] ?>/link-invoice">
|
||||||
|
<input type="hidden" name="invoice_id" value="<?= (int)$b['invoice_id'] ?>">
|
||||||
|
<input type="hidden" name="alloc_betrag"
|
||||||
|
value="<?= $h(number_format($remaining, 2, '.', '')) ?>">
|
||||||
|
<button type="submit" class="btn btn-primary btn-sm">Verknüpfen</button>
|
||||||
|
</form>
|
||||||
|
<?php endif; ?>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue