OpenNIT

Quelltext des aktuellen Standes

Fassung 1.1.1

print.php

9 KB
<?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>