124 lines
4.6 KiB
PHP
124 lines
4.6 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Steuerkram\Services;
|
|
|
|
use Mpdf\Mpdf;
|
|
|
|
class PdfEuerService
|
|
{
|
|
public function render(array $euer, array $settings): void
|
|
{
|
|
$html = $this->buildHtml($euer, $settings);
|
|
|
|
$mpdf = new Mpdf([
|
|
'mode' => 'utf-8',
|
|
'format' => 'A4',
|
|
'margin_top' => 20,
|
|
'margin_bottom' => 20,
|
|
'margin_left' => 20,
|
|
'margin_right' => 20,
|
|
]);
|
|
|
|
$mpdf->SetTitle('EÜR ' . $euer['year']);
|
|
$mpdf->SetAuthor($settings['firma'] ?? '');
|
|
$mpdf->WriteHTML($this->buildCss(), \Mpdf\HTMLParserMode::HEADER_CSS);
|
|
$mpdf->WriteHTML($html, \Mpdf\HTMLParserMode::HTML_BODY);
|
|
$mpdf->Output('EÜR-' . $euer['year'] . '.pdf', 'D');
|
|
}
|
|
|
|
private function buildHtml(array $euer, array $settings): string
|
|
{
|
|
$h = fn($v) => htmlspecialchars((string)$v, ENT_QUOTES, 'UTF-8');
|
|
$fmt = fn($v) => number_format((float)$v, 2, ',', '.') . ' €';
|
|
|
|
$firma = $h($settings['firma'] ?? '');
|
|
$year = $euer['year'];
|
|
|
|
// Build table rows
|
|
$rows = '';
|
|
$maxRows = max(count($euer['einnahmen']), count($euer['ausgaben']));
|
|
for ($i = 0; $i < $maxRows; $i++) {
|
|
$e = $euer['einnahmen'][$i] ?? null;
|
|
$a = $euer['ausgaben'][$i] ?? null;
|
|
$rows .= '<tr>';
|
|
$rows .= $e
|
|
? '<td class="konto-nr">' . $h($e['kontonummer']) . '</td><td class="konto-bez">' . $h($e['bezeichnung']) . '</td><td class="konto-val">' . $fmt($e['netto']) . '</td>'
|
|
: '<td colspan="3"> </td>';
|
|
$rows .= '<td class="sep"></td>';
|
|
$rows .= $a
|
|
? '<td class="konto-nr">' . $h($a['kontonummer']) . '</td><td class="konto-bez">' . $h($a['bezeichnung']) . '</td><td class="konto-val">' . $fmt($a['netto']) . '</td>'
|
|
: '<td colspan="3"> </td>';
|
|
$rows .= '</tr>';
|
|
}
|
|
|
|
$gewinnLabel = $euer['gewinn'] >= 0 ? 'Überschuss (Gewinn)' : 'Fehlbetrag (Verlust)';
|
|
$gewinnClass = $euer['gewinn'] >= 0 ? 'positive' : 'negative';
|
|
|
|
return <<<HTML
|
|
<h1>Einnahmen-Überschuss-Rechnung {$year}</h1>
|
|
<p class="subtitle">{$firma} · Geschäftsjahr {$year}</p>
|
|
|
|
<table class="euer-table">
|
|
<thead>
|
|
<tr>
|
|
<th colspan="3">Betriebseinnahmen</th>
|
|
<th class="sep"></th>
|
|
<th colspan="3">Betriebsausgaben</th>
|
|
</tr>
|
|
<tr class="sub-header">
|
|
<th>Kto.</th><th>Bezeichnung</th><th>Betrag</th>
|
|
<th class="sep"></th>
|
|
<th>Kto.</th><th>Bezeichnung</th><th>Betrag</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{$rows}
|
|
</tbody>
|
|
<tfoot>
|
|
<tr class="total-row">
|
|
<td colspan="2"><strong>Summe Einnahmen</strong></td>
|
|
<td class="konto-val"><strong>{$fmt($euer['total_einnahmen'])}</strong></td>
|
|
<td class="sep"></td>
|
|
<td colspan="2"><strong>Summe Ausgaben</strong></td>
|
|
<td class="konto-val"><strong>{$fmt($euer['total_ausgaben'])}</strong></td>
|
|
</tr>
|
|
<tr class="gewinn-row {$gewinnClass}">
|
|
<td colspan="7" class="gewinn-cell">
|
|
<strong>{$gewinnLabel}:</strong>
|
|
<span class="gewinn-val">{$fmt($euer['gewinn'])}</span>
|
|
</td>
|
|
</tr>
|
|
</tfoot>
|
|
</table>
|
|
|
|
<p class="generated-note">Erstellt am {$h(date('d.m.Y H:i'))} · Steuerkram</p>
|
|
HTML;
|
|
}
|
|
|
|
private function buildCss(): string
|
|
{
|
|
return <<<CSS
|
|
body { font-family: DejaVu Sans, sans-serif; font-size: 9pt; }
|
|
h1 { font-size: 14pt; margin-bottom: 2mm; }
|
|
.subtitle { font-size: 9pt; color: #555; margin-bottom: 8mm; }
|
|
.euer-table { width: 100%; border-collapse: collapse; font-size: 8.5pt; }
|
|
.euer-table thead th { background: #1e293b; color: #fff; padding: 2mm; text-align: left; }
|
|
.euer-table .sub-header th { background: #334155; color: #e2e8f0; font-size: 7.5pt; }
|
|
.euer-table tbody tr:nth-child(even) { background: #f8f8f8; }
|
|
.euer-table tbody td { padding: 1.5mm 2mm; border-bottom: 0.2pt solid #e0e0e0; }
|
|
.konto-nr { width: 12mm; color: #555; }
|
|
.konto-bez { }
|
|
.konto-val { width: 24mm; text-align: right; font-family: DejaVu Sans Mono, monospace; }
|
|
.sep { width: 3mm; background: #fff; }
|
|
.total-row td { padding: 2mm; background: #e8ecf0; border-top: 1pt solid #555; font-size: 9pt; }
|
|
.gewinn-row td { padding: 3mm; }
|
|
.gewinn-row.positive td { background: #dcfce7; }
|
|
.gewinn-row.negative td { background: #fee2e2; }
|
|
.gewinn-cell { text-align: right; font-size: 11pt; }
|
|
.gewinn-val { margin-left: 4mm; }
|
|
.generated-note { font-size: 7pt; color: #888; margin-top: 8mm; }
|
|
CSS;
|
|
}
|
|
}
|