OpenNIT

Quelltext des aktuellen Standes

Fassung 1.1.1

calc_pdf.php

6 KB
<?php
/**
 * Kalkulation als PDF (Dompdf, Landscape). Optik wie der Vertrags-Export.
 * Endpreis in zwei Werten: einmalig und laufend (pro Jahr = monatlich×12 +
 * jährlich). Druck-/PDF-Kontext → Hex-Farben laut STYLEGUIDE.md zulässig.
 */
$disp  = ($display ?? 'net') === 'gross' ? 'gross' : 'net';
$money = fn($v) => $v === null ? '–' : $this->moneyHtml($v);
$billingLabels = $billing ?? ['once' => 'einmalig', 'monthly' => 'pro Monat', 'yearly' => 'pro Jahr'];
$rows = $rows ?? [];
$appName = $this->e($app_name ?? 'OpenNIT');
$today   = date('d.m.Y H:i');
$priceLabel = $disp === 'gross' ? 'Brutto' : 'Netto';

// Summen nach Zyklus, dann zu einmalig/laufend (Jahr) verdichtet.
$byCycle = ['once' => 0.0, 'monthly' => 0.0, 'yearly' => 0.0];
foreach ($rows as $r) {
    $pv = $r['price_' . $disp];
    if ($pv === null) { continue; }
    $byCycle[$r['billing_cycle'] ?? 'once'] += (float)$pv * (int)$r['quantity'];
}
$totalOnce = $byCycle['once'];
$totalRec  = $byCycle['monthly'] * 12 + $byCycle['yearly'];
?>
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title><?= $this->te('Kalkulation') ?></title>
<style>
@page { margin: 1.5cm 1.5cm 2cm; }
body { font-family: DejaVu Sans, Arial, sans-serif; font-size: 9pt; color: #1f2937; }
h1 { font-size: 16pt; margin: 0 0 2px; color: #1a1a2e; }
.meta { font-size: 8pt; color: #6b7280; margin-bottom: 12px; }
.price-mode { display: inline-block; font-size: 7.5pt; font-weight: bold; background: #dbeafe; color: #1e40af; border: 1px solid #bfdbfe; border-radius: 3px; padding: 1px 5px; letter-spacing: 0.04em; vertical-align: middle; margin-left: 6px; }
.summary-box { display: table; width: 100%; border-collapse: collapse; margin-bottom: 14px; }
.summary-cell { display: table-cell; background: #f0f7ff; border: 1px solid #bfd9f2; padding: 8px 14px; width: 50%; text-align: center; }
.summary-cell .label { font-size: 7.5pt; color: #6b7280; text-transform: uppercase; letter-spacing: 0.04em; }
.summary-cell .value { font-size: 15pt; font-weight: bold; color: #1a4a7a; }
.summary-cell .sub { font-size: 7.5pt; color: #6b7280; }
table.main { width: 100%; border-collapse: collapse; }
table.main th { background: #1a4a7a; color: #fff; font-size: 8pt; padding: 5px 6px; text-align: left; }
table.main th.num { text-align: right; }
table.main td { font-size: 8pt; padding: 4px 6px; border-bottom: 1px solid #e5e7eb; vertical-align: top; }
td.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
tr.total-row td { background: #1a4a7a; color: #fff; font-weight: bold; font-size: 9pt; padding: 6px 6px; }
tr.subtotal td { background: #eff6ff; font-size: 8pt; border-top: 1px solid #bfd9f2; padding: 4px 6px; }
.pill-req { display: inline-block; font-size: 6.8pt; font-weight: bold; background: #eef2ff; color: #4338ca; border: 1px solid #c7d2fe; border-radius: 3px; padding: 0 4px; margin-left: 4px; vertical-align: middle; }
.empty { color: #6b7280; padding: 10px 0; }
.footer { border-top: 1px solid #e5e7eb; font-size: 7.5pt; color: #9ca3af; padding-top: 4px; margin-top: 14px; }
</style>
</head>
<body>
<h1><?= $this->te('Kalkulation') ?> <span class="price-mode"><?= $priceLabel ?></span></h1>
<div class="meta"><?= $this->t('Erstellt am') ?> <?= $today ?> &bull; <?= $appName ?> &bull; <?= $this->te('„Laufend" = wiederkehrende Kosten pro Jahr (monatliche ×12)') ?></div>

<table class="summary-box">
  <tr>
    <td class="summary-cell">
      <div class="label"><?= $this->t('Einmalig') ?> (<?= $priceLabel ?>)</div>
      <div class="value"><?= $money($totalOnce) ?></div>
    </td>
    <td class="summary-cell" style="border-left:none">
      <div class="label"><?= $this->t('Laufend / Jahr') ?> (<?= $priceLabel ?>)</div>
      <div class="value"><?= $money($totalRec) ?></div>
      <?php if ($totalRec > 0): ?><div class="sub"><?= $this->te('≈ {betrag} / Monat', ['betrag' => $money($totalRec / 12)]) ?></div><?php endif; ?>
    </td>
  </tr>
</table>

<?php if (empty($rows)): ?>
<div class="empty"><?= $this->te('Keine Positionen in der Kalkulation.') ?></div>
<?php else: ?>
<table class="main">
  <thead>
    <tr>
      <th scope="col" style="width:8%"><?= $this->te('Menge') ?></th>
      <th scope="col" style="width:50%"><?= $this->te('Artikel') ?></th>
      <th scope="col" style="width:16%"><?= $this->te('Abrechnung') ?></th>
      <th scope="col" class="num" style="width:13%"><?= $this->t('Einzel') ?> (<?= $priceLabel ?>)</th>
      <th scope="col" class="num" style="width:13%"><?= $this->t('Summe') ?> (<?= $priceLabel ?>)</th>
    </tr>
  </thead>
  <tbody>
  <?php foreach ($rows as $r): $pv = $r['price_' . $disp]; $q = (int)$r['quantity']; ?>
    <tr>
      <td><?= $q ?>×</td>
      <td><?= $this->e($r['name']) ?><?php if (!empty($r['required'])): ?><span class="pill-req"><?= $this->te('Pflicht') ?></span><?php endif; ?></td>
      <td><?= $this->e($billingLabels[$r['billing_cycle'] ?? 'once'] ?? 'einmalig') ?></td>
      <td class="num"><?= $pv === null ? 'kein Preis' : $money($pv) ?></td>
      <td class="num"><?= $pv === null ? '–' : $money((float)$pv * $q) ?></td>
    </tr>
  <?php endforeach; ?>
    <tr class="subtotal">
      <td colspan="4"><strong><?= $this->te('Einmalige Kosten') ?></strong></td>
      <td class="num"><strong><?= $money($totalOnce) ?></strong></td>
    </tr>
    <tr class="total-row">
      <td colspan="4"><?= $this->t('Laufende Kosten pro Jahr') ?><?= $totalRec > 0 ? ' (≈ ' . $money($totalRec / 12) . '/Monat)' : '' ?></td>
      <td class="num"><?= $money($totalRec) ?></td>
    </tr>
  </tbody>
</table>
<?php endif; ?>

<div class="footer"><?= $appName ?> &bull; <?= $this->te('Unverbindliche Richtpreise, Änderungen vorbehalten') ?> &bull; <?= $today ?></div>
</body>
</html>