Quelltext des aktuellen Standes
<?php
/**
* Standalone-Druckansicht (kein Layout-Wrapper) – öffnet sich in neuem Tab,
* Druckdialog wird automatisch ausgelöst → "Als PDF speichern".
*/
$statusLabels = ['draft' => 'Entwurf', 'active' => 'Aktiv', 'closed' => 'Abgeschlossen', 'archived' => 'Archiviert'];
$typeLabels = ['department' => 'Abteilung', 'project' => 'Projekt'];
// Planwerte netto erfasst, Ist brutto aus den Belegen – jede Kennzahl steht in
// beiden Größen, verglichen wird jeweils auf gleicher Grundlage.
$isLumpSum = ($budget['budget_mode'] ?? 'structured') === 'lump_sum';
$taxRate = (float)($summary['tax_rate'] ?? 19.0);
$totalPlanned = (float)($summary['total_planned_net'] ?? $summary['total_planned']);
$totalPlannedGross = (float)($summary['total_planned_gross'] ?? $totalPlanned);
$totalActual = (float)($summary['total_actual_gross'] ?? $summary['total_actual']);
$totalActualNet = (float)($summary['total_actual_net'] ?? $totalActual);
$variance = $totalPlanned - $totalActualNet;
$varianceGross = $totalPlannedGross - $totalActual;
$utilPct = $totalPlanned > 0 ? round($totalActualNet / $totalPlanned * 100, 1) : 0;
$taxRateLabel = rtrim(rtrim($this->number($taxRate, 2), '0'), ',') . ' %';
?>
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<title><?= $this->te('Budget – {titel}', ['titel' => $budget['title']]) ?></title>
<link rel="stylesheet" href="/assets/css/tokens.css">
<style>
* { box-sizing:border-box; }
body { font-family:'DejaVu Sans', Arial, sans-serif; color:var(--gray-800); margin:2rem; font-size:13px; }
h1 { font-size:1.4rem; margin:0 0 0.25rem; }
.meta { color:var(--gray-500); font-size:0.8rem; margin-bottom:1.5rem; }
.kpi-row { display:flex; gap:1rem; margin-bottom:1.5rem; }
.kpi { flex:1; border:1px solid var(--gray-200); border-radius:6px; padding:0.75rem 1rem; }
.kpi .label { font-size:0.7rem; text-transform:uppercase; letter-spacing:0.04em; color:var(--gray-500); }
.kpi .value { font-size:1.15rem; font-weight:700; margin-top:0.2rem; }
.kpi .sub { font-size:0.7rem; color:var(--gray-500); margin-top:0.15rem; }
.over { color:var(--red-600); }
.pos { color:var(--green-600); }
table { width:100%; border-collapse:collapse; margin-bottom:1.5rem; }
th { text-align:left; font-size:0.7rem; text-transform:uppercase; letter-spacing:0.03em; color:var(--gray-500); border-bottom:2px solid var(--gray-200); padding:0.4rem 0.5rem; }
td { padding:0.4rem 0.5rem; border-bottom:1px solid var(--gray-100); }
td.num, th.num { text-align:right; font-variant-numeric:tabular-nums; }
tfoot td { font-weight:700; border-top:2px solid var(--gray-200); }
.info-table td { border:none; padding:0.15rem 0.5rem 0.15rem 0; }
.info-table td.k { color:var(--gray-500); width:160px; }
h2 { font-size:0.95rem; margin:1.5rem 0 0.5rem; border-bottom:1px solid var(--gray-200); padding-bottom:0.25rem; }
.footer { margin-top:2rem; font-size:0.7rem; color:var(--gray-400); border-top:1px solid var(--gray-200); padding-top:0.5rem; }
.dot { display:inline-block; width:9px; height:9px; border-radius:50%; margin-right:4px; vertical-align:middle; }
@media print { body { margin:0.5cm; } .noprint { display:none; } }
</style>
</head>
<body onload="window.print()">
<div class="noprint" style="margin-bottom:1rem;">
<button onclick="window.print()" style="padding:0.4rem 0.9rem;cursor:pointer;"><?= $this->te('Drucken / Als PDF speichern') ?></button>
<button onclick="window.close()" style="padding:0.4rem 0.9rem;cursor:pointer;"><?= $this->te('Schließen') ?></button>
</div>
<h1><?= $this->e($budget['title']) ?></h1>
<div class="meta">
<?= $typeLabels[$budget['budget_type']] ?? '' ?>
<?php if (!empty($budget['department'])): ?> · <?= $this->e($budget['department']) ?><?php endif; ?>
· <?= $this->te('Status: {status}', ['status' => $statusLabels[$budget['status']] ?? $budget['status']]) ?>
· <?= $this->te('Erstellt am {datum}', ['datum' => date('d.m.Y')]) ?>
</div>
<div class="kpi-row">
<div class="kpi"><div class="label"><?= $this->te($isLumpSum ? 'Pauschalbudget (netto)' : 'Geplant gesamt (netto)') ?></div><div class="value"><?= $this->money($totalPlanned, 2) ?></div><div class="sub"><?= $this->te('brutto {betrag}', ['betrag' => $this->money($totalPlannedGross, 2)]) ?></div></div>
<div class="kpi"><div class="label"><?= $this->te('Tatsächlich (netto)') ?></div><div class="value <?= $totalActualNet > $totalPlanned ? 'over' : '' ?>"><?= $this->money($totalActualNet, 2) ?></div><div class="sub"><?= $this->te('brutto {betrag}', ['betrag' => $this->money($totalActual, 2)]) ?></div></div>
<div class="kpi"><div class="label"><?= $this->te($variance >= 0 ? 'Restbudget (netto)' : 'Überschreitung (netto)') ?></div><div class="value <?= $variance >= 0 ? 'pos' : 'over' ?>"><?= $this->money(abs($variance), 2) ?></div><div class="sub"><?= $this->te('brutto {betrag}', ['betrag' => $this->money(abs($varianceGross), 2)]) ?></div></div>
<div class="kpi"><div class="label"><?= $this->te('Auslastung (netto)') ?></div><div class="value <?= $utilPct > 100 ? 'over' : '' ?>"><?= $utilPct ?>%</div><div class="sub"><?= $this->t('MwSt') ?> <?= $taxRateLabel ?></div></div>
</div>
<table class="info-table">
<?php if (!empty($budget['responsible_name'])): ?><tr><td class="k"><?= $this->te('Verantwortlich') ?></td><td><?= $this->e($budget['responsible_name']) ?></td></tr><?php endif; ?>
<?php if (!empty($budget['fiscal_year'])): ?><tr><td class="k"><?= $this->te('Geschäftsjahr') ?></td><td><?= (int)$budget['fiscal_year'] ?></td></tr><?php endif; ?>
<?php if (!empty($budget['period_start'])): ?><tr><td class="k"><?= $this->te('Zeitraum') ?></td><td><?= date('d.m.Y', strtotime($budget['period_start'])) ?><?php if (!empty($budget['period_end'])): ?> – <?= date('d.m.Y', strtotime($budget['period_end'])) ?><?php endif; ?></td></tr><?php endif; ?>
<?php if (!empty($budget['description'])): ?><tr><td class="k"><?= $this->te('Beschreibung') ?></td><td><?= nl2br($this->e($budget['description'])) ?></td></tr><?php endif; ?>
</table>
<?php if (!$isLumpSum && !empty($positions)): ?>
<h2><?= $this->te('Budgetpositionen') ?></h2>
<table>
<thead>
<tr>
<th scope="col"><?= $this->te('Position') ?></th>
<th scope="col"><?= $this->te('Kategorie') ?></th>
<th scope="col" class="num"><?= $this->te('Geplant (netto)') ?></th>
<th scope="col" class="num"><?= $this->te('Verplant (netto)') ?></th>
<th scope="col" class="num"><?= $this->te('Tatsächlich (netto)') ?></th>
<th scope="col" class="num"><?= $this->te('Differenz (netto)') ?></th>
</tr>
</thead>
<tbody>
<?php
$sumP = 0; $sumC = 0; $sumA = 0;
foreach ($positions as $p):
$pp = (float)$p['planned_amount']; $pc = (float)($p['committed_amount'] ?? 0);
$pa = (float)($p['actual_net'] ?? $p['actual_amount']);
$sumP += $pp; $sumC += $pc; $sumA += $pa; $d = $pp - $pa;
?>
<tr>
<td><?= $this->e($p['title']) ?></td>
<td><?php if (!empty($p['category_name'])): ?><span class="dot" style="background:<?= $this->e($p['category_color'] ?? '#ccc') ?>"></span><?= $this->e($p['category_name']) ?><?php else: ?>–<?php endif; ?></td>
<td class="num"><?= $this->money($pp, 2) ?></td>
<td class="num"><?= $this->money($pc, 2) ?></td>
<td class="num <?= $pa > $pp ? 'over' : '' ?>"><?= $this->money($pa, 2) ?></td>
<td class="num <?= $d < 0 ? 'over' : 'pos' ?>"><?= $this->money($d, 2) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
<tfoot>
<tr>
<td colspan="2"><?= $this->te('Summe') ?></td>
<td class="num"><?= $this->money($sumP, 2) ?></td>
<td class="num"><?= $this->money($sumC, 2) ?></td>
<td class="num"><?= $this->money($sumA, 2) ?></td>
<td class="num"><?= $this->money($sumP - $sumA, 2) ?></td>
</tr>
</tfoot>
</table>
<?php if (!empty($breakdown)): ?>
<h2><?= $this->te('Aufschlüsselung nach Kategorie') ?></h2>
<table>
<thead><tr><th scope="col"><?= $this->te('Kategorie') ?></th><th scope="col" class="num"><?= $this->te('Positionen') ?></th><th scope="col" class="num"><?= $this->te('Geplant') ?></th><th scope="col" class="num"><?= $this->te('Tatsächlich') ?></th></tr></thead>
<tbody>
<?php foreach ($breakdown as $row): ?>
<tr>
<td><span class="dot" style="background:<?= $this->e($row['category_color']) ?>"></span><?= $this->e($row['category_name']) ?></td>
<td class="num"><?= (int)$row['position_count'] ?></td>
<td class="num"><?= $this->money((float)$row['planned'], 2) ?></td>
<td class="num <?= (float)$row['actual'] > (float)$row['planned'] ? 'over' : '' ?>"><?= $this->money((float)$row['actual'], 2) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
<?php endif; ?>
<div class="footer">
<?= $this->e($app_name) ?> · <?= $this->te('Budgetbericht · generiert am {zeitpunkt} Uhr', ['zeitpunkt' => date('d.m.Y H:i')]) ?>
</div>
</body>
</html>