Logo-Upload und Rechnungserstellung überarbeitet
This commit is contained in:
parent
56b1cebfac
commit
d8cba3e1f5
7 changed files with 342 additions and 156 deletions
6
migrations/029_logo_position.sql
Normal file
6
migrations/029_logo_position.sql
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
ALTER TABLE company_settings
|
||||||
|
ADD COLUMN IF NOT EXISTS logo_max_width INT NOT NULL DEFAULT 600,
|
||||||
|
ADD COLUMN IF NOT EXISTS logo_max_height INT NOT NULL DEFAULT 100,
|
||||||
|
ADD COLUMN IF NOT EXISTS logo_position VARCHAR(10) NOT NULL DEFAULT 'center',
|
||||||
|
ADD COLUMN IF NOT EXISTS logo_margin_side INT NOT NULL DEFAULT 75,
|
||||||
|
ADD COLUMN IF NOT EXISTS logo_margin_top INT NOT NULL DEFAULT 6;
|
||||||
|
|
@ -109,8 +109,9 @@ img { max-width: 100%; }
|
||||||
.main-content {
|
.main-content {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
min-height: 100vh;
|
height: 100vh;
|
||||||
overflow: hidden;
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.topbar {
|
.topbar {
|
||||||
|
|
@ -121,6 +122,9 @@ img { max-width: 100%; }
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
.topbar-title {
|
.topbar-title {
|
||||||
|
|
@ -368,6 +372,18 @@ td { padding: .65rem 1rem; vertical-align: middle; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Invoice preview (DIN 5008) ──────────────────────────────── */
|
/* ── Invoice preview (DIN 5008) ──────────────────────────────── */
|
||||||
|
.invoice-preview thead th {
|
||||||
|
text-transform: none;
|
||||||
|
letter-spacing: normal;
|
||||||
|
font-size: inherit;
|
||||||
|
font-weight: normal;
|
||||||
|
background: revert;
|
||||||
|
padding: revert;
|
||||||
|
white-space: revert;
|
||||||
|
color: inherit;
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
.invoice-preview {
|
.invoice-preview {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
padding: 2cm 2cm 2.5cm;
|
padding: 2cm 2cm 2.5cm;
|
||||||
|
|
@ -379,6 +395,29 @@ td { padding: .65rem 1rem; vertical-align: middle; }
|
||||||
color: #000;
|
color: #000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Adressbereich: DIN 5008-Proportionen (50% = 85mm bei vollem A4-Preview).
|
||||||
|
* Breakpoint: nav (220px) + page-body-padding (45px) + A4-max-width (794px) = 1059px.
|
||||||
|
* Darunter stapeln sich die Spalten vertikal.
|
||||||
|
*/
|
||||||
|
.invoice-address {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 50% 1fr;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1059px) {
|
||||||
|
.invoice-address {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
.invoice-address > div:last-child {
|
||||||
|
justify-content: flex-start !important;
|
||||||
|
border-top: 1px solid #ddd;
|
||||||
|
padding-top: 0.75rem;
|
||||||
|
font-size: .85rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* ── Modals ──────────────────────────────────────────────────── */
|
/* ── Modals ──────────────────────────────────────────────────── */
|
||||||
.modal-overlay {
|
.modal-overlay {
|
||||||
display: none;
|
display: none;
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ class SettingsController
|
||||||
$view = new View();
|
$view = new View();
|
||||||
$view->render('layout/base.php', [
|
$view->render('layout/base.php', [
|
||||||
'pageTitle' => 'Einstellungen',
|
'pageTitle' => 'Einstellungen',
|
||||||
|
'topbarActions' => '<button type="submit" form="settings-form" class="btn btn-primary">Einstellungen speichern</button>',
|
||||||
'content' => $this->renderView('settings/form', [
|
'content' => $this->renderView('settings/form', [
|
||||||
'settings' => $settings,
|
'settings' => $settings,
|
||||||
'errors' => [],
|
'errors' => [],
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,10 @@ class Setting
|
||||||
|
|
||||||
public static function update(array $data): void
|
public static function update(array $data): void
|
||||||
{
|
{
|
||||||
|
$logoPosition = in_array($data['logo_position'] ?? '', ['center', 'left', 'right'])
|
||||||
|
? $data['logo_position']
|
||||||
|
: 'center';
|
||||||
|
|
||||||
Database::execute(
|
Database::execute(
|
||||||
'UPDATE company_settings SET
|
'UPDATE company_settings SET
|
||||||
firma=?, inhaber=?, strasse=?, plz=?, ort=?,
|
firma=?, inhaber=?, strasse=?, plz=?, ort=?,
|
||||||
|
|
@ -31,7 +35,9 @@ class Setting
|
||||||
invoice_nr_prefix=?, beleg_nr_prefix=?,
|
invoice_nr_prefix=?, beleg_nr_prefix=?,
|
||||||
payment_days_default=?, test_mode=?, ki_vorschlag_aktiv=?, ocr_aktiv=?,
|
payment_days_default=?, test_mode=?, ki_vorschlag_aktiv=?, ocr_aktiv=?,
|
||||||
nextcloud_url=?, nextcloud_user=?, nextcloud_pass=?,
|
nextcloud_url=?, nextcloud_user=?, nextcloud_pass=?,
|
||||||
nextcloud_folder=?, nextcloud_done=?
|
nextcloud_folder=?, nextcloud_done=?,
|
||||||
|
logo_max_width=?, logo_max_height=?,
|
||||||
|
logo_position=?, logo_margin_side=?, logo_margin_top=?
|
||||||
WHERE id=1',
|
WHERE id=1',
|
||||||
[
|
[
|
||||||
$data['firma'] ?? '',
|
$data['firma'] ?? '',
|
||||||
|
|
@ -61,6 +67,11 @@ class Setting
|
||||||
$data['nextcloud_pass'] ?: null,
|
$data['nextcloud_pass'] ?: null,
|
||||||
$data['nextcloud_folder'] ?: '/Belege/Import',
|
$data['nextcloud_folder'] ?: '/Belege/Import',
|
||||||
$data['nextcloud_done'] ?: null,
|
$data['nextcloud_done'] ?: null,
|
||||||
|
max(50, (int)($data['logo_max_width'] ?? 600)),
|
||||||
|
max(20, (int)($data['logo_max_height'] ?? 100)),
|
||||||
|
$logoPosition,
|
||||||
|
max(0, (int)($data['logo_margin_side'] ?? 75)),
|
||||||
|
max(0, (int)($data['logo_margin_top'] ?? 6)),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,6 @@ declare(strict_types=1);
|
||||||
namespace Steuerkram\Services;
|
namespace Steuerkram\Services;
|
||||||
|
|
||||||
use Mpdf\Mpdf;
|
use Mpdf\Mpdf;
|
||||||
use Mpdf\Config\ConfigVariables;
|
|
||||||
use Mpdf\Config\FontVariables;
|
|
||||||
|
|
||||||
class PdfInvoiceService
|
class PdfInvoiceService
|
||||||
{
|
{
|
||||||
|
|
@ -19,8 +17,8 @@ class PdfInvoiceService
|
||||||
'format' => 'A4',
|
'format' => 'A4',
|
||||||
'margin_top' => 15,
|
'margin_top' => 15,
|
||||||
'margin_bottom' => 25,
|
'margin_bottom' => 25,
|
||||||
'margin_left' => 20,
|
'margin_left' => 25, // DIN 5008: 25mm links
|
||||||
'margin_right' => 20,
|
'margin_right' => 15, // DIN 5008: 15mm rechts
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$mpdf->SetTitle('Rechnung ' . ($invoice['rechnungs_nr'] ?? 'Entwurf'));
|
$mpdf->SetTitle('Rechnung ' . ($invoice['rechnungs_nr'] ?? 'Entwurf'));
|
||||||
|
|
@ -31,28 +29,45 @@ class PdfInvoiceService
|
||||||
$mpdf->WriteHTML($html, \Mpdf\HTMLParserMode::HTML_BODY);
|
$mpdf->WriteHTML($html, \Mpdf\HTMLParserMode::HTML_BODY);
|
||||||
|
|
||||||
$filename = 'Rechnung-' . ($invoice['rechnungs_nr'] ?? 'Entwurf') . '.pdf';
|
$filename = 'Rechnung-' . ($invoice['rechnungs_nr'] ?? 'Entwurf') . '.pdf';
|
||||||
$mpdf->Output($filename, 'D'); // D = Download
|
$mpdf->Output($filename, 'D');
|
||||||
}
|
}
|
||||||
|
|
||||||
private function buildHtml(array $inv, array $lines, array $settings): string
|
private function buildHtml(array $inv, array $lines, array $settings): string
|
||||||
{
|
{
|
||||||
$h = fn($v) => htmlspecialchars((string)$v, ENT_QUOTES, 'UTF-8');
|
$h = fn($v) => htmlspecialchars((string)$v, ENT_QUOTES, 'UTF-8');
|
||||||
$fmt = fn($v) => number_format((float)$v, 2, ',', '.') . ' €';
|
$fmt = fn($v) => number_format((float)$v, 2, ',', '.') . ' €';
|
||||||
$fmtNum = fn($v) => number_format((float)$v, 3, ',', '.');
|
$fmtNum = fn($v) => number_format((float)$v, 1, ',', '.');
|
||||||
$fmtDate = fn($v) => $v ? date('d.m.Y', strtotime($v)) : '–';
|
$fmtDate = fn($v) => $v ? date('d.m.Y', strtotime($v)) : '–';
|
||||||
|
|
||||||
// Customer address
|
// Kundenname
|
||||||
$customerName = !empty($inv['firma']) ? $inv['firma'] : trim(($inv['anrede'] ?? '') . ' ' . ($inv['vorname'] ?? '') . ' ' . $inv['nachname']);
|
$customerName = !empty($inv['firma'])
|
||||||
|
? $inv['firma']
|
||||||
|
: trim(($inv['anrede'] ?? '') . ' ' . ($inv['vorname'] ?? '') . ' ' . $inv['nachname']);
|
||||||
|
|
||||||
// Logo
|
// Logo
|
||||||
$logoHtml = '';
|
$logoHtml = '';
|
||||||
if (!empty($settings['logo_path'])) {
|
if (!empty($settings['logo_path'])) {
|
||||||
$logoPath = dirname(__DIR__, 2) . '/public/' . $settings['logo_path'];
|
$logoPath = dirname(__DIR__, 2) . '/public/' . $settings['logo_path'];
|
||||||
if (file_exists($logoPath)) {
|
if (file_exists($logoPath)) {
|
||||||
$logoHtml = '<img src="' . $h($settings['logo_path']) . '" alt="Logo" class="logo">';
|
$maxW = max(50, (int)($settings['logo_max_width'] ?? 600));
|
||||||
|
$maxH = max(20, (int)($settings['logo_max_height'] ?? 100));
|
||||||
|
$logoHtml = '<img src="' . $h($settings['logo_path']) . '" alt="Logo"'
|
||||||
|
. ' style="max-width:' . $maxW . 'px;max-height:' . $maxH . 'px">';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$logoPosition = $settings['logo_position'] ?? 'center';
|
||||||
|
$logoMarginSide = max(0, (int)($settings['logo_margin_side'] ?? 75));
|
||||||
|
$logoMarginTop = max(0, (int)($settings['logo_margin_top'] ?? 6));
|
||||||
|
|
||||||
|
if ($logoPosition === 'left') {
|
||||||
|
$logoWrapStyle = "text-align:left;margin-left:{$logoMarginSide}px;margin-top:{$logoMarginTop}px";
|
||||||
|
} elseif ($logoPosition === 'right') {
|
||||||
|
$logoWrapStyle = "text-align:right;margin-right:{$logoMarginSide}px;margin-top:{$logoMarginTop}px";
|
||||||
|
} else {
|
||||||
|
$logoWrapStyle = 'text-align:center';
|
||||||
|
}
|
||||||
|
|
||||||
// Leistungszeitraum
|
// Leistungszeitraum
|
||||||
$lz = '';
|
$lz = '';
|
||||||
if (!empty($inv['leistungszeitraum_von'])) {
|
if (!empty($inv['leistungszeitraum_von'])) {
|
||||||
|
|
@ -62,7 +77,12 @@ class PdfInvoiceService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build positions table
|
// Fälligkeitsdatum
|
||||||
|
$fälligText = !empty($inv['faellig_datum'])
|
||||||
|
? ' Fällig am: <strong>' . $fmtDate($inv['faellig_datum']) . '</strong>'
|
||||||
|
: '';
|
||||||
|
|
||||||
|
// Positionszeilen
|
||||||
$posRows = '';
|
$posRows = '';
|
||||||
foreach ($lines as $line) {
|
foreach ($lines as $line) {
|
||||||
$posRows .= '<tr>
|
$posRows .= '<tr>
|
||||||
|
|
@ -78,26 +98,31 @@ class PdfInvoiceService
|
||||||
|
|
||||||
$payDays = $inv['payment_days'] ?? 14;
|
$payDays = $inv['payment_days'] ?? 14;
|
||||||
|
|
||||||
|
// Empfänger-Land nur wenn nicht DE
|
||||||
|
$land = ($inv['land'] ?? 'DE') !== 'DE' ? '<br>' . $h($inv['land']) : '';
|
||||||
|
|
||||||
return <<<HTML
|
return <<<HTML
|
||||||
<div class="invoice-wrap">
|
<div class="invoice-wrap">
|
||||||
|
|
||||||
<!-- Logo -->
|
<!-- Logo -->
|
||||||
<div class="logo-wrap">{$logoHtml}</div>
|
<div class="logo-wrap" style="{$logoWrapStyle}">{$logoHtml}</div>
|
||||||
|
|
||||||
<!-- Adressbereich (DIN 5008) -->
|
<!-- Adressbereich DIN 5008 Typ A (Anschriftfeld 85mm × 45mm) -->
|
||||||
<div class="address-block">
|
<table class="address-block">
|
||||||
<div class="address-left">
|
<tr>
|
||||||
|
<!-- Linke Spalte: Anschriftfeld 85mm (Kuvertfenster) -->
|
||||||
|
<td class="address-left">
|
||||||
<div class="return-address">{$h($settings['firma'] ?? '')} · {$h($settings['strasse'] ?? '')} · {$h($settings['plz'] ?? '')} {$h($settings['ort'] ?? '')}</div>
|
<div class="return-address">{$h($settings['firma'] ?? '')} · {$h($settings['strasse'] ?? '')} · {$h($settings['plz'] ?? '')} {$h($settings['ort'] ?? '')}</div>
|
||||||
<div class="recipient">
|
<div class="recipient">
|
||||||
{$h($customerName)}<br>
|
{$h($customerName)}<br>
|
||||||
{$h($inv['strasse'])}<br>
|
{$h($inv['strasse'])}<br>
|
||||||
{$h($inv['plz'])} {$h($inv['ort'])}<br>
|
{$h($inv['plz'])} {$h($inv['ort'])}{$land}
|
||||||
{$h($inv['land'] !== 'DE' ? $inv['land'] : '')}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</td>
|
||||||
<div class="address-right">
|
<!-- Rechte Spalte: Absenderinformationen -->
|
||||||
|
<td class="address-right">
|
||||||
<table class="sender-table">
|
<table class="sender-table">
|
||||||
<tr><td>{$h($settings['firma'] ?? '')}</td></tr>
|
<tr><td><strong>{$h($settings['firma'] ?? '')}</strong></td></tr>
|
||||||
<tr><td>{$h($settings['inhaber'] ?? '')}</td></tr>
|
<tr><td>{$h($settings['inhaber'] ?? '')}</td></tr>
|
||||||
<tr><td>{$h($settings['strasse'] ?? '')}</td></tr>
|
<tr><td>{$h($settings['strasse'] ?? '')}</td></tr>
|
||||||
<tr><td>{$h($settings['plz'] ?? '')} {$h($settings['ort'] ?? '')}</td></tr>
|
<tr><td>{$h($settings['plz'] ?? '')} {$h($settings['ort'] ?? '')}</td></tr>
|
||||||
|
|
@ -106,11 +131,13 @@ class PdfInvoiceService
|
||||||
<tr><td>{$h($settings['email'] ?? '')}</td></tr>
|
<tr><td>{$h($settings['email'] ?? '')}</td></tr>
|
||||||
<tr><td>USt-ID: {$h($settings['ust_id'] ?? '')}</td></tr>
|
<tr><td>USt-ID: {$h($settings['ust_id'] ?? '')}</td></tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</td>
|
||||||
</div>
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
<!-- Meta-Felder -->
|
<!-- Meta-Felder -->
|
||||||
<table class="meta-table">
|
<table class="meta-table">
|
||||||
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Betreff</th>
|
<th>Betreff</th>
|
||||||
<th>Rechnungsnummer</th>
|
<th>Rechnungsnummer</th>
|
||||||
|
|
@ -118,13 +145,16 @@ class PdfInvoiceService
|
||||||
<th>Leistungszeitraum</th>
|
<th>Leistungszeitraum</th>
|
||||||
<th>Datum</th>
|
<th>Datum</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td>{$h($inv['betreff'])}</td>
|
<td>{$h($inv['betreff'])}</td>
|
||||||
<td>{$h($inv['rechnungs_nr'] ?? 'Entwurf')}</td>
|
<td><strong>{$h($inv['rechnungs_nr'] ?? 'Entwurf')}</strong></td>
|
||||||
<td>{$h($inv['kundennummer'])}</td>
|
<td>{$h($inv['kundennummer'])}</td>
|
||||||
<td>{$h($lz)}</td>
|
<td>{$h($lz ?: '–')}</td>
|
||||||
<td>{$fmtDate($inv['rechnungs_datum'])}</td>
|
<td>{$fmtDate($inv['rechnungs_datum'])}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<!-- Positionen -->
|
<!-- Positionen -->
|
||||||
|
|
@ -145,16 +175,19 @@ class PdfInvoiceService
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<!-- Summen -->
|
<!-- Summen (rechtsbündig) -->
|
||||||
<table class="sum-table">
|
<table class="sum-table" align="right">
|
||||||
<tr><td>Nettobetrag:</td><td class="sum-val">{$fmt($inv['betrag_netto'])}</td></tr>
|
<tr><td>Nettobetrag:</td> <td class="sum-val">{$fmt($inv['betrag_netto'])}</td></tr>
|
||||||
<tr><td>zzgl. Umsatzsteuer:</td><td class="sum-val">{$fmt($inv['betrag_ust'])}</td></tr>
|
<tr><td>zzgl. Umsatzsteuer:</td><td class="sum-val">{$fmt($inv['betrag_ust'])}</td></tr>
|
||||||
<tr class="sum-total"><td><strong>Rechnungsbetrag:</strong></td><td class="sum-val"><strong>{$fmt($inv['betrag_brutto'])}</strong></td></tr>
|
<tr class="sum-total">
|
||||||
|
<td><strong>Rechnungsbetrag:</strong></td>
|
||||||
|
<td class="sum-val"><strong>{$fmt($inv['betrag_brutto'])}</strong></td>
|
||||||
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<!-- Zahlungshinweis -->
|
<!-- Zahlungshinweis -->
|
||||||
<p class="payment-note">
|
<p class="payment-note">
|
||||||
Bitte überweisen Sie den Rechnungsbetrag innerhalb {$h($payDays)} Tagen auf das unten genannte Konto.
|
Bitte überweisen Sie den Rechnungsbetrag innerhalb {$h($payDays)} Tagen auf das unten genannte Konto.{$fälligText}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -197,48 +230,60 @@ HTML;
|
||||||
return <<<CSS
|
return <<<CSS
|
||||||
body { font-family: DejaVu Sans, sans-serif; font-size: 10pt; color: #000; }
|
body { font-family: DejaVu Sans, sans-serif; font-size: 10pt; color: #000; }
|
||||||
.invoice-wrap { width: 100%; }
|
.invoice-wrap { width: 100%; }
|
||||||
.logo-wrap { text-align: center; margin-bottom: 10mm; }
|
|
||||||
.logo { max-height: 25mm; max-width: 80mm; }
|
/* Logo */
|
||||||
|
.logo-wrap { margin-bottom: 8mm; }
|
||||||
|
|
||||||
/* DIN 5008 Adressbereich */
|
/* DIN 5008 Adressbereich */
|
||||||
.address-block { display: flex; justify-content: space-between; margin-bottom: 10mm; }
|
.address-block { width: 100%; border-collapse: collapse; margin-bottom: 8mm; }
|
||||||
.address-left { width: 85mm; }
|
.address-left { width: 85mm; vertical-align: top; padding: 0; }
|
||||||
.return-address { font-size: 7pt; color: #555; border-bottom: 0.3pt solid #aaa; padding-bottom: 1mm; margin-bottom: 2mm; }
|
.address-right { vertical-align: top; padding: 0 0 0 5mm; }
|
||||||
.recipient { font-size: 10pt; line-height: 1.4; min-height: 27.3mm; }
|
|
||||||
.address-right { width: 75mm; text-align: left; }
|
.return-address {
|
||||||
.sender-table td { font-size: 9pt; padding: 0.5mm 0; }
|
font-size: 7pt; color: #555;
|
||||||
|
border-bottom: 0.3pt solid #aaa;
|
||||||
|
padding-bottom: 1mm; margin-bottom: 2mm;
|
||||||
|
}
|
||||||
|
.recipient { font-size: 10pt; line-height: 1.5; min-height: 27.3mm; }
|
||||||
|
|
||||||
|
/* Absendertabelle: auf Inhaltsbreite schrumpfen, rechtsbündig */
|
||||||
|
.sender-table { border-collapse: collapse; font-size: 9pt; margin-left: auto; }
|
||||||
|
.sender-table td { padding: 0.4mm 0; white-space: nowrap; }
|
||||||
|
|
||||||
/* Meta-Felder */
|
/* Meta-Felder */
|
||||||
.meta-table { width: 100%; border-collapse: collapse; margin-bottom: 8mm; font-size: 9pt; }
|
.meta-table { width: 100%; table-layout: fixed; border-collapse: collapse; margin-bottom: 8mm; font-size: 9pt; }
|
||||||
.meta-table th { background: #f0f0f0; padding: 2mm 2mm; text-align: left; font-size: 8pt; color: #555; border: 0.5pt solid #ccc; }
|
.meta-table th { background: #f0f0f0; padding: 2mm; text-align: left; font-size: 8pt; color: #555; border: 0.5pt solid #ccc; }
|
||||||
.meta-table td { padding: 2mm; border: 0.5pt solid #ccc; }
|
.meta-table td { padding: 2mm; border: 0.5pt solid #ccc; }
|
||||||
|
|
||||||
/* Positionen */
|
/* Positionen */
|
||||||
.pos-table { width: 100%; border-collapse: collapse; margin-bottom: 5mm; font-size: 9pt; }
|
.pos-table { width: 100%; table-layout: fixed; border-collapse: collapse; margin-bottom: 5mm; font-size: 9pt; }
|
||||||
.pos-table thead th { background: #1e293b; color: #fff; padding: 2mm; text-align: left; }
|
.pos-table thead th { background: #f0f0f0; color: #000; padding: 2mm; }
|
||||||
.pos-table tbody tr:nth-child(even) { background: #f8f8f8; }
|
.pos-table tbody tr:nth-child(even) { background: #f8f8f8; }
|
||||||
.pos-table tbody td { padding: 1.5mm 2mm; border-bottom: 0.3pt solid #e0e0e0; }
|
.pos-table tbody td { padding: 1.5mm 2mm; border-bottom: 0.3pt solid #e0e0e0; }
|
||||||
.pos-nr { width: 10mm; }
|
.pos-nr { width: 8%; }
|
||||||
.pos-bez { width: 55mm; }
|
.pos-bez { /* nimmt den Rest */ }
|
||||||
.pos-menge { width: 18mm; text-align: right; }
|
.pos-menge { width: 11%; text-align: right; }
|
||||||
.pos-einheit { width: 15mm; }
|
.pos-einheit { width: 10%; }
|
||||||
.pos-ust { width: 12mm; text-align: right; }
|
.pos-ust { width: 8%; text-align: right; }
|
||||||
.pos-preis { width: 20mm; text-align: right; }
|
.pos-preis { width: 15%; text-align: right; }
|
||||||
.pos-gesamt { width: 22mm; text-align: right; }
|
.pos-gesamt { width: 16%; text-align: right; }
|
||||||
|
|
||||||
/* Summen */
|
/* Summen */
|
||||||
.sum-table { float: right; width: 70mm; border-collapse: collapse; margin-bottom: 8mm; font-size: 9.5pt; }
|
.sum-table { border-collapse: collapse; margin-bottom: 8mm; font-size: 9pt; }
|
||||||
.sum-table td { padding: 1.5mm 2mm; }
|
.sum-table td { padding: 1.5mm 2mm; }
|
||||||
.sum-table .sum-val { text-align: right; border-top: 0.3pt solid #ccc; }
|
.sum-table .sum-val { text-align: right; border-top: 0.3pt solid #ccc; }
|
||||||
.sum-table .sum-total td { border-top: 1pt solid #000; font-size: 10.5pt; }
|
.sum-table .sum-total td { border-top: 1pt solid #000; }
|
||||||
|
|
||||||
/* Zahlungshinweis */
|
/* Zahlungshinweis */
|
||||||
.payment-note { clear: both; margin-top: 5mm; font-size: 9pt; padding: 3mm; background: #f8f8f8; border-left: 2pt solid #2563eb; }
|
.payment-note { margin-top: 5mm; font-size: 9pt; padding: 3mm; background: #f8f8f8; border-left: 2pt solid #2563eb; }
|
||||||
|
|
||||||
/* Fußzeile */
|
/* Fußzeile */
|
||||||
.footer-table { width: 100%; border-collapse: collapse; font-size: 7.5pt; border-top: 0.5pt solid #ccc; padding-top: 2mm; }
|
.footer-table { width: 100%; border-collapse: collapse; font-size: 7.5pt; border-top: 0.5pt solid #ccc; }
|
||||||
.footer-table td { width: 25%; padding: 1mm 2mm; vertical-align: top; }
|
.footer-table td { padding: 1mm 2mm; vertical-align: top; }
|
||||||
.page-nr { text-align: right; }
|
.footer-table td:nth-child(1) { width: 35%; }
|
||||||
|
.footer-table td:nth-child(2) { width: 25%; }
|
||||||
|
.footer-table td:nth-child(3) { width: 28%; }
|
||||||
|
.page-nr { width: 12%; text-align: right; }
|
||||||
CSS;
|
CSS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,15 @@ if (!empty($invoice['leistungszeitraum_von'])) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Leistungszeitraum mit <br> für die Vorschau-Tabelle (Datumsangaben untereinander)
|
||||||
|
$lzHtml = '';
|
||||||
|
if (!empty($invoice['leistungszeitraum_von'])) {
|
||||||
|
$lzHtml = $h(View::formatDate($invoice['leistungszeitraum_von']));
|
||||||
|
if (!empty($invoice['leistungszeitraum_bis']) && $invoice['leistungszeitraum_bis'] !== $invoice['leistungszeitraum_von']) {
|
||||||
|
$lzHtml .= '<br>' . $h(View::formatDate($invoice['leistungszeitraum_bis']));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$badgeClass = match($invoice['status']) {
|
$badgeClass = match($invoice['status']) {
|
||||||
'Bezahlt' => 'badge-green',
|
'Bezahlt' => 'badge-green',
|
||||||
'Storniert' => 'badge-red',
|
'Storniert' => 'badge-red',
|
||||||
|
|
@ -56,13 +65,28 @@ $badgeClass = match($invoice['status']) {
|
||||||
|
|
||||||
<!-- Logo -->
|
<!-- Logo -->
|
||||||
<?php if (!empty($settings['logo_path'])): ?>
|
<?php if (!empty($settings['logo_path'])): ?>
|
||||||
<div style="text-align:center;margin-bottom:1.5rem">
|
<?php
|
||||||
<img src="/<?= $h($settings['logo_path']) ?>" alt="Logo" style="max-height:60px;max-width:250px">
|
$logoMaxW = max(50, (int)($settings['logo_max_width'] ?? 600));
|
||||||
|
$logoMaxH = max(20, (int)($settings['logo_max_height'] ?? 100));
|
||||||
|
$logoPos = $settings['logo_position'] ?? 'center';
|
||||||
|
$logoMSide = max(0, (int)($settings['logo_margin_side'] ?? 75));
|
||||||
|
$logoMTop = max(0, (int)($settings['logo_margin_top'] ?? 6));
|
||||||
|
if ($logoPos === 'left') {
|
||||||
|
$logoWrapStyle = "text-align:left;margin-left:{$logoMSide}px;margin-top:{$logoMTop}px;margin-bottom:1.5rem";
|
||||||
|
} elseif ($logoPos === 'right') {
|
||||||
|
$logoWrapStyle = "text-align:right;margin-right:{$logoMSide}px;margin-top:{$logoMTop}px;margin-bottom:1.5rem";
|
||||||
|
} else {
|
||||||
|
$logoWrapStyle = 'text-align:center;margin-bottom:1.5rem';
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<div style="<?= $logoWrapStyle ?>">
|
||||||
|
<img src="/<?= $h($settings['logo_path']) ?>" alt="Logo"
|
||||||
|
style="max-width:<?= $logoMaxW ?>px;max-height:<?= $logoMaxH ?>px">
|
||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<!-- Adressbereich -->
|
<!-- Adressbereich (DIN 5008: 50% = 85mm bei vollem A4-Preview) -->
|
||||||
<div style="display:grid;grid-template-columns:100mm 1fr;gap:1rem;margin-bottom:1.5rem">
|
<div class="invoice-address" style="margin-bottom:1.5rem">
|
||||||
<div>
|
<div>
|
||||||
<div style="font-size:8pt;color:#666;border-bottom:1px solid #ccc;padding-bottom:2px;margin-bottom:4px">
|
<div style="font-size:8pt;color:#666;border-bottom:1px solid #ccc;padding-bottom:2px;margin-bottom:4px">
|
||||||
<?= $h($settings['firma'] ?? '') ?> · <?= $h($settings['strasse'] ?? '') ?> · <?= $h(($settings['plz'] ?? '') . ' ' . ($settings['ort'] ?? '')) ?>
|
<?= $h($settings['firma'] ?? '') ?> · <?= $h($settings['strasse'] ?? '') ?> · <?= $h(($settings['plz'] ?? '') . ' ' . ($settings['ort'] ?? '')) ?>
|
||||||
|
|
@ -74,7 +98,8 @@ $badgeClass = match($invoice['status']) {
|
||||||
<?php if ($invoice['land'] !== 'DE'): ?><br><?= $h($invoice['land']) ?><?php endif; ?>
|
<?php if ($invoice['land'] !== 'DE'): ?><br><?= $h($invoice['land']) ?><?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style="text-align:left;font-size:.85rem;line-height:1.7">
|
<div style="display:flex;justify-content:flex-end;font-size:.85rem;line-height:1.7">
|
||||||
|
<div style="text-align:left">
|
||||||
<strong><?= $h($settings['firma'] ?? '') ?></strong><br>
|
<strong><?= $h($settings['firma'] ?? '') ?></strong><br>
|
||||||
<?= $h($settings['inhaber'] ?? '') ?><br>
|
<?= $h($settings['inhaber'] ?? '') ?><br>
|
||||||
<?= $h($settings['strasse'] ?? '') ?><br>
|
<?= $h($settings['strasse'] ?? '') ?><br>
|
||||||
|
|
@ -84,40 +109,41 @@ $badgeClass = match($invoice['status']) {
|
||||||
USt-ID: <?= $h($settings['ust_id'] ?? '') ?>
|
USt-ID: <?= $h($settings['ust_id'] ?? '') ?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Meta-Tabelle -->
|
<!-- Meta-Tabelle -->
|
||||||
<table style="width:100%;border-collapse:collapse;margin-bottom:1.5rem;font-size:.9rem">
|
<table style="width:100%;border-collapse:collapse;margin-bottom:1.5rem;font-size:.9rem">
|
||||||
<thead>
|
<thead>
|
||||||
<tr style="background:#f0f0f0">
|
<tr style="background:#f0f0f0">
|
||||||
<th style="padding:.4rem .5rem;text-align:left;border:1px solid #ddd;font-size:.8rem">Betreff</th>
|
<th style="padding:.4rem .5rem;text-align:left;border:1px solid #ddd;font-weight:normal;white-space:nowrap">Betreff</th>
|
||||||
<th style="padding:.4rem .5rem;text-align:left;border:1px solid #ddd;font-size:.8rem">Rechnungsnummer</th>
|
<th style="padding:.4rem .5rem;text-align:left;border:1px solid #ddd;font-weight:normal;white-space:nowrap">Rechnungsnummer</th>
|
||||||
<th style="padding:.4rem .5rem;text-align:left;border:1px solid #ddd;font-size:.8rem">Kundennummer</th>
|
<th style="padding:.4rem .5rem;text-align:left;border:1px solid #ddd;font-weight:normal;white-space:nowrap">Kundennummer</th>
|
||||||
<th style="padding:.4rem .5rem;text-align:left;border:1px solid #ddd;font-size:.8rem">Leistungszeitraum</th>
|
<th style="padding:.4rem .5rem;text-align:left;border:1px solid #ddd;font-weight:normal;white-space:nowrap">Leistungszeitraum</th>
|
||||||
<th style="padding:.4rem .5rem;text-align:left;border:1px solid #ddd;font-size:.8rem">Datum</th>
|
<th style="padding:.4rem .5rem;text-align:left;border:1px solid #ddd;font-weight:normal;white-space:nowrap;width:100%">Datum</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td style="padding:.4rem .5rem;border:1px solid #ddd"><?= $h($invoice['betreff']) ?></td>
|
<td style="padding:.4rem .5rem;border:1px solid #ddd;white-space:nowrap"><?= $h($invoice['betreff']) ?></td>
|
||||||
<td style="padding:.4rem .5rem;border:1px solid #ddd;font-weight:700"><?= $h($invoice['rechnungs_nr'] ?? 'Entwurf') ?></td>
|
<td style="padding:.4rem .5rem;border:1px solid #ddd;white-space:nowrap;font-weight:700"><?= $h($invoice['rechnungs_nr'] ?? 'Entwurf') ?></td>
|
||||||
<td style="padding:.4rem .5rem;border:1px solid #ddd"><?= $h($invoice['kundennummer']) ?></td>
|
<td style="padding:.4rem .5rem;border:1px solid #ddd;white-space:nowrap"><?= $h($invoice['kundennummer']) ?></td>
|
||||||
<td style="padding:.4rem .5rem;border:1px solid #ddd"><?= $h($lz ?: '–') ?></td>
|
<td style="padding:.4rem .5rem;border:1px solid #ddd"><?= $lzHtml ?: '–' ?></td>
|
||||||
<td style="padding:.4rem .5rem;border:1px solid #ddd"><?= View::formatDate($invoice['rechnungs_datum']) ?></td>
|
<td style="padding:.4rem .5rem;border:1px solid #ddd;white-space:nowrap"><?= $h(View::formatDate($invoice['rechnungs_datum'])) ?></td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<!-- Positionen -->
|
<!-- Positionen -->
|
||||||
<table style="width:100%;border-collapse:collapse;margin-bottom:1.5rem;font-size:.9rem">
|
<table style="width:100%;table-layout:fixed;border-collapse:collapse;margin-bottom:1.5rem;font-size:.9rem">
|
||||||
<thead>
|
<thead>
|
||||||
<tr style="background:#1e293b;color:#fff">
|
<tr style="background:#f0f0f0">
|
||||||
<th style="padding:.5rem;text-align:left;width:40px">Pos.</th>
|
<th style="padding:.5rem;text-align:left;width:6%">Pos.</th>
|
||||||
<th style="padding:.5rem;text-align:left">Bezeichnung</th>
|
<th style="padding:.5rem;text-align:left">Bezeichnung</th>
|
||||||
<th style="padding:.5rem;text-align:right;width:70px">Menge</th>
|
<th style="padding:.5rem;text-align:right;width:11%">Menge</th>
|
||||||
<th style="padding:.5rem;text-align:left;width:70px">Einheit</th>
|
<th style="padding:.5rem;text-align:left;width:10%">Einheit</th>
|
||||||
<th style="padding:.5rem;text-align:right;width:60px">USt.</th>
|
<th style="padding:.5rem;text-align:right;width:8%">USt.</th>
|
||||||
<th style="padding:.5rem;text-align:right;width:100px">Einzelpreis</th>
|
<th style="padding:.5rem;text-align:right;width:15%">Einzelpreis</th>
|
||||||
<th style="padding:.5rem;text-align:right;width:110px">Gesamt</th>
|
<th style="padding:.5rem;text-align:right;width:16%">Gesamt</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|
@ -125,7 +151,7 @@ $badgeClass = match($invoice['status']) {
|
||||||
<tr style="border-bottom:1px solid #eee">
|
<tr style="border-bottom:1px solid #eee">
|
||||||
<td style="padding:.4rem .5rem"><?= $h($line['position']) ?></td>
|
<td style="padding:.4rem .5rem"><?= $h($line['position']) ?></td>
|
||||||
<td style="padding:.4rem .5rem"><?= $h($line['bezeichnung']) ?></td>
|
<td style="padding:.4rem .5rem"><?= $h($line['bezeichnung']) ?></td>
|
||||||
<td style="padding:.4rem .5rem;text-align:right"><?= number_format((float)$line['menge'], 3, ',', '.') ?></td>
|
<td style="padding:.4rem .5rem;text-align:right"><?= number_format((float)$line['menge'], 1, ',', '.') ?></td>
|
||||||
<td style="padding:.4rem .5rem"><?= $h($line['einheit']) ?></td>
|
<td style="padding:.4rem .5rem"><?= $h($line['einheit']) ?></td>
|
||||||
<td style="padding:.4rem .5rem;text-align:right"><?= number_format((float)$line['ust_satz'], 0) ?> %</td>
|
<td style="padding:.4rem .5rem;text-align:right"><?= number_format((float)$line['ust_satz'], 0) ?> %</td>
|
||||||
<td style="padding:.4rem .5rem;text-align:right"><?= $fmt($line['einzelpreis']) ?></td>
|
<td style="padding:.4rem .5rem;text-align:right"><?= $fmt($line['einzelpreis']) ?></td>
|
||||||
|
|
@ -154,20 +180,20 @@ $badgeClass = match($invoice['status']) {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Fußzeile -->
|
<!-- Fußzeile -->
|
||||||
<div style="border-top:1px solid #ccc;padding-top:.75rem;display:grid;grid-template-columns:1fr 1fr 1fr 80px;gap:1rem;font-size:.75rem;color:#555">
|
<div style="border-top:1px solid #ccc;padding-top:.75rem;display:grid;grid-template-columns:auto auto auto 1fr;gap:1rem;font-size:.75rem;color:#555">
|
||||||
<div>
|
<div style="min-width:0">
|
||||||
<strong><?= $h($settings['firma'] ?? '') ?></strong><br>
|
<strong><?= $h($settings['firma'] ?? '') ?></strong><br>
|
||||||
<?= $h($settings['inhaber'] ?? '') ?><br>
|
<?= $h($settings['inhaber'] ?? '') ?><br>
|
||||||
<?= $h($settings['strasse'] ?? '') ?><br>
|
<?= $h($settings['strasse'] ?? '') ?><br>
|
||||||
<?= $h(($settings['plz'] ?? '') . ' ' . ($settings['ort'] ?? '')) ?><br>
|
<?= $h(($settings['plz'] ?? '') . ' ' . ($settings['ort'] ?? '')) ?><br>
|
||||||
USt-ID: <?= $h($settings['ust_id'] ?? '') ?>
|
USt-ID: <?= $h($settings['ust_id'] ?? '') ?>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div style="min-width:0">
|
||||||
Tel: <?= $h($settings['telefon'] ?? '') ?><br>
|
Tel: <?= $h($settings['telefon'] ?? '') ?><br>
|
||||||
E-Mail: <?= $h($settings['email'] ?? '') ?><br>
|
E-Mail: <?= $h($settings['email'] ?? '') ?><br>
|
||||||
Web: <?= $h($settings['website'] ?? '') ?>
|
Web: <?= $h($settings['website'] ?? '') ?>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div style="min-width:0">
|
||||||
<?= $h($settings['bank_name'] ?? '') ?><br>
|
<?= $h($settings['bank_name'] ?? '') ?><br>
|
||||||
IBAN: <?= $h($settings['iban'] ?? '') ?><br>
|
IBAN: <?= $h($settings['iban'] ?? '') ?><br>
|
||||||
BIC: <?= $h($settings['bic'] ?? '') ?>
|
BIC: <?= $h($settings['bic'] ?? '') ?>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<?php use Steuerkram\View; $h = fn($v) => View::h($v); ?>
|
<?php use Steuerkram\View; $h = fn($v) => View::h($v); ?>
|
||||||
|
|
||||||
<form method="post" action="/settings">
|
<form id="settings-form" method="post" action="/settings">
|
||||||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:1.5rem;align-items:start">
|
<div style="display:grid;grid-template-columns:1fr 1fr;gap:1.5rem;align-items:start">
|
||||||
|
|
||||||
<!-- Firmendaten -->
|
<!-- Firmendaten -->
|
||||||
|
|
@ -85,8 +85,6 @@
|
||||||
|
|
||||||
<!-- Bankdaten + Logo + Nummernkreise -->
|
<!-- Bankdaten + Logo + Nummernkreise -->
|
||||||
<div>
|
<div>
|
||||||
<button type="submit" class="btn btn-primary btn-lg w-full" style="margin-bottom:1rem">Einstellungen speichern</button>
|
|
||||||
|
|
||||||
<!-- Betriebsmodus -->
|
<!-- Betriebsmodus -->
|
||||||
<div class="card mb-2" style="border:2px solid <?= ($settings['test_mode'] ?? 1) ? 'var(--color-warning,#f59e0b)' : 'var(--color-success,#22c55e)' ?>">
|
<div class="card mb-2" style="border:2px solid <?= ($settings['test_mode'] ?? 1) ? 'var(--color-warning,#f59e0b)' : 'var(--color-success,#22c55e)' ?>">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
|
|
@ -239,28 +237,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card mb-2">
|
|
||||||
<div class="card-header"><div class="card-title">Logo</div></div>
|
|
||||||
<div class="card-body">
|
|
||||||
<?php if (!empty($settings['logo_path'])): ?>
|
|
||||||
<div style="text-align:center;margin-bottom:1rem">
|
|
||||||
<img src="/<?= $h($settings['logo_path']) ?>" alt="Logo" style="max-height:80px;max-width:200px">
|
|
||||||
</div>
|
|
||||||
<form method="post" action="/settings/logo/delete" style="margin-bottom:1rem">
|
|
||||||
<button type="submit" class="btn btn-danger btn-sm w-full">Logo löschen</button>
|
|
||||||
</form>
|
|
||||||
<?php endif; ?>
|
|
||||||
<form method="post" action="/settings/logo" enctype="multipart/form-data">
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Logo hochladen (PNG, JPG)</label>
|
|
||||||
<input type="file" name="logo" accept=".png,.jpg,.jpeg">
|
|
||||||
<div class="form-hint">Empfohlen: transparentes PNG, mind. 300px breit</div>
|
|
||||||
</div>
|
|
||||||
<button type="submit" class="btn btn-secondary">Logo hochladen</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card mb-2">
|
<div class="card mb-2">
|
||||||
<div class="card-header"><div class="card-title">Nummernkreise</div></div>
|
<div class="card-header"><div class="card-title">Nummernkreise</div></div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
|
|
@ -286,6 +262,82 @@
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<div style="display:grid;grid-template-columns:1fr 1fr;gap:1.5rem;margin-top:0">
|
||||||
|
<div></div>
|
||||||
|
<div>
|
||||||
|
<div class="card mb-2">
|
||||||
|
<div class="card-header"><div class="card-title">Logo</div></div>
|
||||||
|
<div class="card-body">
|
||||||
|
<?php if (!empty($settings['logo_path'])): ?>
|
||||||
|
<div style="text-align:center;margin-bottom:1rem">
|
||||||
|
<img src="/<?= $h($settings['logo_path']) ?>" alt="Logo" style="max-height:80px;max-width:400px">
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
<form method="post" action="/settings/logo" enctype="multipart/form-data">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Logo hochladen (PNG, JPG)</label>
|
||||||
|
<input type="file" name="logo" accept=".png,.jpg,.jpeg">
|
||||||
|
<div class="form-hint">Empfohlen: transparentes PNG, mind. 300px breit</div>
|
||||||
|
</div>
|
||||||
|
<div style="display:flex;gap:.5rem;flex-wrap:wrap">
|
||||||
|
<button type="submit" class="btn btn-secondary">Hochladen</button>
|
||||||
|
<?php if (!empty($settings['logo_path'])): ?>
|
||||||
|
<button type="submit" form="delete-logo-form" class="btn btn-danger btn-sm">Löschen</button>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<?php if (!empty($settings['logo_path'])): ?>
|
||||||
|
<form id="delete-logo-form" method="post" action="/settings/logo/delete"></form>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<div style="margin-top:1rem;border-top:1px solid var(--color-border,#e5e7eb);padding-top:1rem">
|
||||||
|
<div class="form-hint" style="margin-bottom:.75rem">Darstellung in der Rechnung</div>
|
||||||
|
<div class="form-grid form-grid-2">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Max-Breite</label>
|
||||||
|
<input type="number" name="logo_max_width" form="settings-form"
|
||||||
|
value="<?= $h($settings['logo_max_width'] ?? 600) ?>" min="50" max="2000">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Max-Höhe</label>
|
||||||
|
<input type="number" name="logo_max_height" form="settings-form"
|
||||||
|
value="<?= $h($settings['logo_max_height'] ?? 100) ?>" min="20" max="500">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Position</label>
|
||||||
|
<div style="display:flex;gap:1.5rem;margin-top:.35rem">
|
||||||
|
<?php foreach (['center' => 'Zentriert', 'left' => 'Links', 'right' => 'Rechts'] as $val => $label): ?>
|
||||||
|
<label style="display:flex;align-items:center;gap:.35rem;cursor:pointer">
|
||||||
|
<input type="radio" name="logo_position" value="<?= $val ?>" form="settings-form"
|
||||||
|
<?= ($settings['logo_position'] ?? 'center') === $val ? 'checked' : '' ?>
|
||||||
|
onchange="logoPositionChanged()">
|
||||||
|
<?= $label ?>
|
||||||
|
</label>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php $showMargins = in_array($settings['logo_position'] ?? 'center', ['left', 'right']); ?>
|
||||||
|
<div id="logo-margin-fields" style="display:<?= $showMargins ? 'block' : 'none' ?>">
|
||||||
|
<div class="form-grid form-grid-2">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Rand Seite</label>
|
||||||
|
<input type="number" name="logo_margin_side" form="settings-form"
|
||||||
|
value="<?= $h($settings['logo_margin_side'] ?? 75) ?>" min="0">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Rand oben</label>
|
||||||
|
<input type="number" name="logo_margin_top" form="settings-form"
|
||||||
|
value="<?= $h($settings['logo_margin_top'] ?? 6) ?>" min="0">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
function updateModeLabel(cb) {
|
function updateModeLabel(cb) {
|
||||||
const track = document.getElementById('toggle_track');
|
const track = document.getElementById('toggle_track');
|
||||||
|
|
@ -312,4 +364,10 @@ function updateModeLabel(cb) {
|
||||||
cb.closest('.card').querySelector('.badge').textContent = 'Produktiv';
|
cb.closest('.card').querySelector('.badge').textContent = 'Produktiv';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function logoPositionChanged() {
|
||||||
|
const pos = document.querySelector('input[name="logo_position"]:checked')?.value;
|
||||||
|
document.getElementById('logo-margin-fields').style.display =
|
||||||
|
(pos === 'left' || pos === 'right') ? 'block' : 'none';
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue