Feature: Konto-Journal im Kontenrahmen
This commit is contained in:
parent
55c02b54de
commit
61bce80012
4 changed files with 180 additions and 0 deletions
|
|
@ -44,6 +44,7 @@ $router->get('/dashboard', fn($p) => (new AccountController())->dashboard($p));
|
|||
$router->get('/accounts', fn($p) => (new AccountController())->index($p));
|
||||
$router->get('/accounts/new', fn($p) => (new AccountController())->create($p));
|
||||
$router->post('/accounts', fn($p) => (new AccountController())->store($p));
|
||||
$router->get('/accounts/{id}/journal', fn($p) => (new AccountController())->journal($p));
|
||||
$router->get('/accounts/{id}/edit', fn($p) => (new AccountController())->edit($p));
|
||||
$router->post('/accounts/{id}', fn($p) => (new AccountController())->update($p));
|
||||
$router->post('/accounts/{id}/delete', fn($p) => (new AccountController())->destroy($p));
|
||||
|
|
|
|||
|
|
@ -198,6 +198,60 @@ class AccountController
|
|||
View::redirect($back, 'Konto gelöscht.');
|
||||
}
|
||||
|
||||
public function journal(array $params): void
|
||||
{
|
||||
$id = (int)$params['id'];
|
||||
$account = Account::find($id);
|
||||
if (!$account) { http_response_code(404); return; }
|
||||
|
||||
$availableYears = \Steuerkram\Database::fetchAll(
|
||||
'SELECT year FROM fiscal_years ORDER BY year DESC'
|
||||
);
|
||||
$years = array_column($availableYears, 'year');
|
||||
if (empty($years)) {
|
||||
$years = [(int)date('Y')];
|
||||
}
|
||||
|
||||
$year = isset($_GET['year']) ? (int)$_GET['year'] : (int)date('Y');
|
||||
if (!in_array($year, $years, true)) {
|
||||
$year = $years[0];
|
||||
}
|
||||
|
||||
// Buchungen wo dieses Konto Kategorie-Konto ist (alle, auch Kindzeilen)
|
||||
// ODER Zahlungs-Konto ist (nur Hauptbuchungen, Kindzeilen gehören nicht ans Zahlungskonto)
|
||||
$bookings = \Steuerkram\Database::fetchAll(
|
||||
'SELECT bk.*,
|
||||
k.kontonummer AS kategorie_nr, k.bezeichnung AS kategorie_name,
|
||||
z.kontonummer AS zahlungs_nr, z.bezeichnung AS zahlungs_name,
|
||||
bl.beleg_nr
|
||||
FROM bookings bk
|
||||
JOIN accounts k ON bk.kategorie_konto_id = k.id
|
||||
JOIN accounts z ON bk.zahlungskonto_id = z.id
|
||||
LEFT JOIN belege bl ON bk.beleg_id = bl.id
|
||||
JOIN fiscal_years fy ON bk.fiscal_year_id = fy.id
|
||||
WHERE fy.year = ?
|
||||
AND (
|
||||
bk.kategorie_konto_id = ?
|
||||
OR (bk.zahlungskonto_id = ? AND bk.parent_booking_id IS NULL
|
||||
AND bk.kategorie_konto_id != ?)
|
||||
)
|
||||
ORDER BY bk.buchungs_datum, bk.id',
|
||||
[$year, $id, $id, $id]
|
||||
);
|
||||
|
||||
$view = new View();
|
||||
$view->render('layout/base.php', [
|
||||
'pageTitle' => 'Konto ' . $account['kontonummer'] . ' – Journal',
|
||||
'content' => $this->renderView('accounts/journal', [
|
||||
'account' => $account,
|
||||
'bookings' => $bookings,
|
||||
'year' => $year,
|
||||
'years' => $years,
|
||||
'accountId' => $id,
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
public function search(array $params): void
|
||||
{
|
||||
$q = trim($_GET['q'] ?? '');
|
||||
|
|
|
|||
124
src/Views/accounts/journal.php
Normal file
124
src/Views/accounts/journal.php
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
<?php
|
||||
use Steuerkram\View;
|
||||
$h = fn($v) => View::h($v);
|
||||
$fmt = fn($v) => number_format(abs((float)$v), 2, ',', '.');
|
||||
|
||||
// Jahr-Navigation
|
||||
$prevYear = $year - 1;
|
||||
$nextYear = $year + 1;
|
||||
$hasPrev = in_array($prevYear, $years, true);
|
||||
$hasNext = in_array($nextYear, $years, true);
|
||||
?>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div>
|
||||
<div class="card-title">
|
||||
<?= $h($account['kontonummer']) ?> – <?= $h($account['bezeichnung']) ?>
|
||||
</div>
|
||||
<div class="text-muted text-sm"><?= $h($account['kontenart']) ?></div>
|
||||
</div>
|
||||
<div style="display:flex;gap:.5rem;align-items:center">
|
||||
<?php if ($hasPrev): ?>
|
||||
<a href="/accounts/<?= $accountId ?>/journal?year=<?= $prevYear ?>" class="btn btn-secondary btn-sm">← <?= $prevYear ?></a>
|
||||
<?php else: ?>
|
||||
<button class="btn btn-secondary btn-sm" disabled>←</button>
|
||||
<?php endif; ?>
|
||||
<span class="font-bold" style="min-width:4rem;text-align:center"><?= $year ?></span>
|
||||
<?php if ($hasNext): ?>
|
||||
<a href="/accounts/<?= $accountId ?>/journal?year=<?= $nextYear ?>" class="btn btn-secondary btn-sm"><?= $nextYear ?> →</a>
|
||||
<?php else: ?>
|
||||
<button class="btn btn-secondary btn-sm" disabled>→</button>
|
||||
<?php endif; ?>
|
||||
<a href="/accounts" class="btn btn-secondary btn-sm">← Kontenrahmen</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if (empty($bookings)): ?>
|
||||
<div class="card-body">
|
||||
<p class="text-muted">Keine Buchungen für dieses Konto in <?= $year ?>.</p>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<?php
|
||||
$saldo = 0.0;
|
||||
foreach ($bookings as $b) {
|
||||
$isKategorie = (int)$b['kategorie_konto_id'] === $accountId;
|
||||
if ($isKategorie) {
|
||||
$wert = (float)$b['betrag_netto'];
|
||||
} else {
|
||||
// Zahlungs-Seite: Brutto-Betrag, Ausgaben fließen ab
|
||||
$wert = (float)$b['betrag_brutto'];
|
||||
if ($b['buchungstyp'] === 'Ausgabe') {
|
||||
$wert = -$wert;
|
||||
}
|
||||
}
|
||||
$saldo += $wert;
|
||||
}
|
||||
?>
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Datum</th>
|
||||
<th>Nummer</th>
|
||||
<th>Beleg</th>
|
||||
<th>Typ</th>
|
||||
<th>Gegenkonto</th>
|
||||
<th>Text</th>
|
||||
<th class="td-right">Wert</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
$laufendSaldo = 0.0;
|
||||
foreach ($bookings as $b):
|
||||
$isKategorie = (int)$b['kategorie_konto_id'] === $accountId;
|
||||
|
||||
if ($isKategorie) {
|
||||
$wert = (float)$b['betrag_netto'];
|
||||
$gegenkonto = $b['zahlungs_nr'] . ' ' . $b['zahlungs_name'];
|
||||
} else {
|
||||
$wert = (float)$b['betrag_brutto'];
|
||||
if ($b['buchungstyp'] === 'Ausgabe') {
|
||||
$wert = -$wert;
|
||||
}
|
||||
$gegenkonto = $b['kategorie_nr'] . ' ' . $b['kategorie_name'];
|
||||
}
|
||||
$laufendSaldo += $wert;
|
||||
$isReversal = (bool)$b['is_reversal'];
|
||||
?>
|
||||
<tr<?= $isReversal ? ' style="font-style:italic;color:#888"' : '' ?>>
|
||||
<td class="td-mono text-sm"><?= $h($b['buchungs_datum']) ?></td>
|
||||
<td class="td-mono text-sm"><?= $h($b['buchungs_nr']) ?></td>
|
||||
<td>
|
||||
<?php if ($b['beleg_id']): ?>
|
||||
<a href="/belege/<?= (int)$b['beleg_id'] ?>"><?= $h($b['beleg_nr'] ?? '#' . $b['beleg_id']) ?></a>
|
||||
<?php else: ?>
|
||||
<span class="text-muted">–</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge <?= $b['buchungstyp'] === 'Einnahme' ? 'badge-green' : 'badge-red' ?>">
|
||||
<?= $h($b['buchungstyp']) ?>
|
||||
</span>
|
||||
</td>
|
||||
<td class="td-mono text-sm"><?= $h($gegenkonto) ?></td>
|
||||
<td><?= $h($b['buchungstext']) ?></td>
|
||||
<td class="td-right td-mono<?= $wert < 0 ? ' text-danger' : '' ?>">
|
||||
<?= $wert < 0 ? '−' : '' ?><?= $fmt($wert) ?> €
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr style="font-weight:700;border-top:2px solid var(--border-color,#e5e7eb)">
|
||||
<td colspan="6">Kontostand <?= $year ?> (<?= count($bookings) ?> Buchungen)</td>
|
||||
<td class="td-right td-mono<?= $saldo < 0 ? ' text-danger' : '' ?>">
|
||||
<?= $saldo < 0 ? '−' : '' ?><?= $fmt($saldo) ?> €
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
|
@ -61,6 +61,7 @@
|
|||
<td class="td-muted text-sm"><?= $h(mb_strimwidth($acc['beschreibung'] ?? '', 0, 60, '…')) ?></td>
|
||||
<td>
|
||||
<div style="display:flex;gap:.5rem">
|
||||
<a href="/accounts/<?= $h($acc['id']) ?>/journal" class="btn btn-secondary btn-sm">Journal</a>
|
||||
<a href="/accounts/<?= $h($acc['id']) ?>/edit" class="btn btn-secondary btn-sm">Bearbeiten</a>
|
||||
<form method="post" action="/accounts/<?= $h($acc['id']) ?>/delete"
|
||||
data-confirm="Konto '<?= $h($acc['kontonummer']) ?>' wirklich löschen?">
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue