574 lines
25 KiB
PHP
574 lines
25 KiB
PHP
<?php
|
||
use Steuerkram\View;
|
||
|
||
$customerName = !empty($invoice['firma'])
|
||
? $invoice['firma']
|
||
: trim(($invoice['anrede'] ?? '') . ' ' . ($invoice['vorname'] ?? '') . ' ' . $invoice['nachname']);
|
||
|
||
$lzHtml = $h($invoice['leistungszeitraum'] ?? '');
|
||
|
||
$xmlHinweise = $xmlHinweise ?? [];
|
||
$xmlTooltip = empty($xmlHinweise)
|
||
? 'XML-Rechnung (EN 16931 / XRechnung 3.0) herunterladen'
|
||
: 'XML-Rechnung (EN 16931 / XRechnung 3.0) herunterladen – es fehlen Angaben, siehe Hinweis oben';
|
||
|
||
$badgeClass = match($invoice['status']) {
|
||
'Bezahlt' => 'badge-green',
|
||
'Storniert' => 'badge-red',
|
||
'Gesendet' => 'badge-yellow',
|
||
'Finalisiert' => 'badge-blue',
|
||
'Teilzahlung' => 'badge-orange',
|
||
'Überzahlt' => 'badge-purple',
|
||
default => 'badge-gray',
|
||
};
|
||
?>
|
||
|
||
<!-- Querverweise Storno -->
|
||
<?php if (!empty($originalrechnung)): ?>
|
||
<div class="alert alert-warning">
|
||
Stornorechnung zu: <a href="/invoices/<?= $h($originalrechnung['id']) ?>"><strong><?= $h($originalrechnung['rechnungs_nr']) ?></strong></a>
|
||
</div>
|
||
<?php endif; ?>
|
||
<?php if (!empty($stornorechnung)): ?>
|
||
<div class="alert alert-info">
|
||
Wurde storniert durch: <a href="/invoices/<?= $h($stornorechnung['id']) ?>"><strong><?= $h($stornorechnung['rechnungs_nr']) ?></strong></a>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<!-- Hinweis auf fehlende Daten für die XML-Rechnung (EN 16931) -->
|
||
<?php if (!empty($xmlHinweise)): ?>
|
||
<div class="alert alert-warning">
|
||
<strong>XML-Rechnung (EN 16931 / XRechnung): unvollständige Daten.</strong>
|
||
Der Download ist trotzdem möglich, die Datei erfüllt die Norm aber nur eingeschränkt.
|
||
<?php foreach ($xmlHinweise as $bereich => $felder): ?>
|
||
<div class="mt-1">
|
||
<em><?= $h($bereich) ?>:</em>
|
||
<ul class="xml-hinweis-list">
|
||
<?php foreach ($felder as $feld): ?>
|
||
<li><?= $h($feld) ?></li>
|
||
<?php endforeach; ?>
|
||
</ul>
|
||
</div>
|
||
<?php endforeach; ?>
|
||
<?php if (isset($xmlHinweise['Stammdaten'])): ?>
|
||
<div class="mt-1">Stammdaten pflegen unter <a href="/settings">Einstellungen</a>.</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<!-- Aktionsleiste -->
|
||
<div class="toolbar mb-2">
|
||
<div class="toolbar-left">
|
||
<span class="badge badge-lg <?= $badgeClass ?>"><?= $h($invoice['status']) ?></span>
|
||
</div>
|
||
<div class="toolbar-right">
|
||
<?php if ($invoice['status'] === 'Entwurf'): ?>
|
||
<a href="/invoices/<?= $h($invoice['id']) ?>/edit"
|
||
class="icon-btn"
|
||
title="Bearbeiten">
|
||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24"
|
||
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/>
|
||
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>
|
||
</svg>
|
||
</a>
|
||
<form method="post" action="/invoices/<?= $h($invoice['id']) ?>/finalize"
|
||
data-confirm="Rechnung finalisieren? Danach kann sie nicht mehr bearbeitet werden.">
|
||
<button type="submit" class="btn btn-primary">Finalisieren & Nummerieren</button>
|
||
</form>
|
||
<?php endif; ?>
|
||
<?php if ($invoice['status'] !== 'Entwurf'): ?>
|
||
<a href="/invoices/<?= $h($invoice['id']) ?>/pdf"
|
||
class="icon-btn"
|
||
title="PDF ansehen">
|
||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24"
|
||
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<rect x="2" y="4" width="20" height="16" rx="2"/>
|
||
<path d="M5 12s2.5-4 7-4 7 4 7 4-2.5 4-7 4-7-4-7-4z"/>
|
||
<circle cx="12" cy="12" r="1.5"/>
|
||
</svg>
|
||
</a>
|
||
<?php endif; ?>
|
||
<a href="/invoices/<?= $h($invoice['id']) ?>/xml"
|
||
class="icon-btn"
|
||
<?= empty($xmlHinweise) ? '' : 'style="--icon-color: var(--color-warning)"' ?>
|
||
title="<?= $h($xmlTooltip) ?>">
|
||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24"
|
||
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
|
||
<polyline points="14 2 14 8 20 8"/>
|
||
<text x="12" y="18.5" text-anchor="middle" font-size="6.5" font-family="sans-serif"
|
||
font-weight="700" fill="currentColor" stroke="none">XML</text>
|
||
</svg>
|
||
</a>
|
||
<?php if (!in_array($invoice['status'], ['Entwurf', 'Storniert'])): ?>
|
||
<button type="button" class="btn btn-primary" data-modal-open="send-modal">Per E-Mail senden</button>
|
||
<?php endif; ?>
|
||
<?php if (!in_array($invoice['status'], ['Entwurf', 'Storniert']) && !empty($invoice['beleg_id'])): ?>
|
||
<button type="button" class="btn btn-success" data-modal-open="buchungs-modal">Zahlungen</button>
|
||
<?php endif; ?>
|
||
<?php if (in_array($invoice['status'], ['Entwurf','Finalisiert','Gesendet']) && empty($invoice['storno_of_id'])): ?>
|
||
<form method="post" action="/invoices/<?= $h($invoice['id']) ?>/cancel"
|
||
data-confirm="Rechnung stornieren und Stornorechnung erstellen?">
|
||
<button type="submit" class="btn btn-danger">Stornieren</button>
|
||
</form>
|
||
<?php endif; ?>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Rechnungsvorschau (DIN 5008 ähnlich) -->
|
||
<div id="invoice-pages">
|
||
<div class="invoice-preview">
|
||
<div class="invoice-page-body">
|
||
|
||
<!-- Logo -->
|
||
<?php if (!empty($settings['logo_path'])): ?>
|
||
<?php
|
||
$logoMaxW = max(50, (int)($settings['logo_max_width'] ?? 600));
|
||
$logoMaxH = max(20, (int)($settings['logo_max_height'] ?? 100));
|
||
$logoPosRaw = $settings['logo_position'] ?? 'center';
|
||
$logoPos = in_array($logoPosRaw, ['left', 'right'], true) ? $logoPosRaw : 'center';
|
||
$logoMSide = max(0, (int)($settings['logo_margin_side'] ?? 75));
|
||
$logoMTop = max(0, (int)($settings['logo_margin_top'] ?? 6));
|
||
?>
|
||
<div class="invoice-logo-wrap invoice-logo-wrap--<?= $logoPos ?>" style="--logo-mside: <?= $logoMSide ?>px; --logo-mtop: <?= $logoMTop ?>px">
|
||
<img src="/settings/logo/file" alt="Logo"
|
||
style="--logo-maxw: <?= $logoMaxW ?>px; --logo-maxh: <?= $logoMaxH ?>px">
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<!-- Adressbereich (DIN 5008: 50% = 85mm bei vollem A4-Preview) -->
|
||
<div class="invoice-address invoice-address-block">
|
||
<div>
|
||
<div class="invoice-sender-line">
|
||
<?= $h($settings['firma'] ?? '') ?> · <?= $h($settings['strasse'] ?? '') ?> · <?= $h(($settings['plz'] ?? '') . ' ' . ($settings['ort'] ?? '')) ?>
|
||
</div>
|
||
<div class="invoice-recipient-block">
|
||
<?= $h($customerName) ?><br>
|
||
<?= $h($invoice['strasse']) ?><br>
|
||
<?= $h($invoice['plz']) ?> <?= $h($invoice['ort']) ?>
|
||
<?php if ($invoice['land'] !== 'DE'): ?><br><?= $h($invoice['land']) ?><?php endif; ?>
|
||
</div>
|
||
</div>
|
||
<div class="invoice-title-block">
|
||
<?= !empty($invoice['storno_of_id']) ? 'STORNORECHNUNG' : 'Rechnung' ?>
|
||
<?php if (!empty($invoice['storno_of_id']) && !empty($originalrechnung)): ?>
|
||
<div class="invoice-title-ref">
|
||
zu Rechnung Nr. <strong><?= $h($originalrechnung['rechnungs_nr']) ?></strong>
|
||
vom <?= $h(View::formatDate($originalrechnung['rechnungs_datum'] ?? '')) ?>
|
||
</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Meta-Tabelle -->
|
||
<table class="invoice-meta-table">
|
||
<thead>
|
||
<tr>
|
||
<th>Betreff</th>
|
||
<th>Rechnungsnummer</th>
|
||
<th>Kundennummer</th>
|
||
<th>Leistungszeitraum</th>
|
||
<th>Datum</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td class="nowrap"><?= $h($invoice['betreff']) ?></td>
|
||
<td class="nowrap font-bold"><?= $h($invoice['rechnungs_nr'] ?? 'Entwurf') ?></td>
|
||
<td class="nowrap"><?= $h($invoice['kundennummer']) ?></td>
|
||
<td><?= $lzHtml ?: '–' ?></td>
|
||
<td><?= $h(View::formatDate($invoice['rechnungs_datum'])) ?></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<!-- Positionen -->
|
||
<table id="invoice-pos-table">
|
||
<thead>
|
||
<tr>
|
||
<th>Pos.</th>
|
||
<th>Bezeichnung</th>
|
||
<th>Menge</th>
|
||
<th>Einheit</th>
|
||
<th>USt.</th>
|
||
<th>Einzelpreis</th>
|
||
<th>Gesamt</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<?php foreach ($lines as $line): ?>
|
||
<?php if (($line['type'] ?? 'position') === 'heading'): ?>
|
||
<tr class="row-heading">
|
||
<td colspan="7"><?= nl2br($h($line['bezeichnung'])) ?></td>
|
||
</tr>
|
||
<?php else: ?>
|
||
<tr>
|
||
<td><?= $h($line['position']) ?></td>
|
||
<td><?= nl2br($h($line['bezeichnung'])) ?></td>
|
||
<td><?= number_format((float)$line['menge'], 1, ',', '.') ?></td>
|
||
<td><?= $h($line['einheit']) ?></td>
|
||
<td><?= number_format((float)$line['ust_satz'], 0) ?> %</td>
|
||
<td><?= $fmt($line['einzelpreis']) ?></td>
|
||
<td class="col-total"><?= $fmt($line['betrag_netto']) ?></td>
|
||
</tr>
|
||
<?php endif; ?>
|
||
<?php endforeach; ?>
|
||
</tbody>
|
||
</table>
|
||
|
||
<!-- Summen + Zahlungshinweis -->
|
||
<div id="invoice-summary">
|
||
<div class="invoice-summary-wrap">
|
||
<table class="invoice-summary-table">
|
||
<tr><td>Nettobetrag:</td><td><?= $fmt($invoice['betrag_netto']) ?></td></tr>
|
||
<tr><td>zzgl. Umsatzsteuer:</td><td><?= $fmt($invoice['betrag_ust']) ?></td></tr>
|
||
<tr class="row-total">
|
||
<td>Rechnungsbetrag:</td>
|
||
<td><?= $fmt($invoice['betrag_brutto']) ?></td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
<?php if (empty($invoice['storno_of_id'])): ?>
|
||
<div class="invoice-payment-hint">
|
||
Bitte überweisen Sie den Rechnungsbetrag innerhalb <?= $h($invoice['payment_days'] ?? 14) ?> Tagen auf das unten genannte Konto.
|
||
<strong>Fällig am: <?= View::formatDate($invoice['faellig_datum']) ?></strong>
|
||
</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
|
||
</div><!-- /.invoice-page-body -->
|
||
|
||
<!-- Fußzeile -->
|
||
<div class="invoice-footer-bar">
|
||
<div class="invoice-footer-grid">
|
||
<div class="min-w-0">
|
||
<strong><?= $h($settings['firma'] ?? '') ?></strong><br>
|
||
<?php if (trim((string)($settings['inhaber'] ?? '')) !== ''): ?>Inh. <?= $h($settings['inhaber']) ?><br><?php endif; ?>
|
||
<?= $h($settings['strasse'] ?? '') ?><br>
|
||
<?= $h(($settings['plz'] ?? '') . ' ' . ($settings['ort'] ?? '')) ?><br>
|
||
USt-ID: <?= $h($settings['ust_id'] ?? '') ?>
|
||
</div>
|
||
<div class="nowrap">
|
||
Tel: <?= $h($settings['telefon'] ?? '') ?><br>
|
||
E-Mail: <?= $h($settings['email'] ?? '') ?><br>
|
||
Web: <?= $h($settings['website'] ?? '') ?>
|
||
</div>
|
||
<div class="nowrap">
|
||
<?= $h($settings['bank_name'] ?? '') ?><br>
|
||
IBAN: <?= $h($settings['iban'] ?? '') ?><br>
|
||
BIC: <?= $h($settings['bic'] ?? '') ?>
|
||
</div>
|
||
<div class="text-right nowrap">Seite <span class="page-num">1</span> von <span class="page-total">1</span></div>
|
||
</div>
|
||
</div>
|
||
|
||
</div><!-- /.invoice-preview -->
|
||
</div><!-- /#invoice-pages -->
|
||
|
||
<script>
|
||
(function () {
|
||
'use strict';
|
||
|
||
// 1cm in px: Referenzelement messen, damit Browserzoom berücksichtigt wird
|
||
function getCmPx() {
|
||
var d = document.createElement('div');
|
||
d.style.cssText = 'position:absolute;visibility:hidden;width:1cm';
|
||
document.body.appendChild(d);
|
||
var px = d.offsetWidth;
|
||
document.body.removeChild(d);
|
||
return px;
|
||
}
|
||
|
||
function paginate() {
|
||
var wrap = document.getElementById('invoice-pages');
|
||
var page1 = wrap.querySelector('.invoice-preview');
|
||
var body1 = page1.querySelector('.invoice-page-body');
|
||
var footer1 = page1.querySelector('.invoice-footer-bar');
|
||
var posTable = document.getElementById('invoice-pos-table');
|
||
var summary = document.getElementById('invoice-summary');
|
||
|
||
if (!posTable || !summary) return;
|
||
|
||
var cmPx = getCmPx();
|
||
var a4H = 29.7 * cmPx;
|
||
var padTop = parseFloat(getComputedStyle(page1).paddingTop);
|
||
var padBot = parseFloat(getComputedStyle(page1).paddingBottom);
|
||
var footerH = footer1.offsetHeight;
|
||
var availH = a4H - padTop - padBot - footerH;
|
||
|
||
// Passt alles auf Seite 1?
|
||
if (body1.offsetHeight <= availH) {
|
||
return;
|
||
}
|
||
|
||
// Alle Zeilen sichern, tbody leeren
|
||
var tbody = posTable.querySelector('tbody');
|
||
var rows = Array.from(tbody.querySelectorAll('tr'));
|
||
rows.forEach(function (r) { tbody.removeChild(r); });
|
||
|
||
// summary aus body1 herauslösen
|
||
body1.removeChild(summary);
|
||
|
||
// Hilfsfunktionen
|
||
function makeFooter() {
|
||
var f = footer1.cloneNode(true);
|
||
return f;
|
||
}
|
||
|
||
function makePage() {
|
||
var p = document.createElement('div');
|
||
p.className = 'invoice-preview';
|
||
var b = document.createElement('div');
|
||
b.className = 'invoice-page-body';
|
||
p.appendChild(b);
|
||
p.appendChild(makeFooter());
|
||
wrap.appendChild(p);
|
||
return b;
|
||
}
|
||
|
||
function makeContTable(targetBody) {
|
||
var tbl = posTable.cloneNode(false);
|
||
tbl.removeAttribute('id');
|
||
var thead = posTable.querySelector('thead').cloneNode(true);
|
||
var tb = document.createElement('tbody');
|
||
tbl.appendChild(thead);
|
||
tbl.appendChild(tb);
|
||
targetBody.appendChild(tbl);
|
||
return tb;
|
||
}
|
||
|
||
// Zeilen seitenweise verteilen
|
||
var pages = [page1];
|
||
var currentBody = body1;
|
||
var currentTbody = tbody; // zeigt auf tbody in posTable auf Seite 1
|
||
|
||
for (var i = 0; i < rows.length; i++) {
|
||
currentTbody.appendChild(rows[i]);
|
||
|
||
var currentPage = pages[pages.length - 1];
|
||
var usedH = currentBody.offsetHeight;
|
||
|
||
if (usedH > availH) {
|
||
// Zeile wieder entfernen, neue Seite beginnen
|
||
currentTbody.removeChild(rows[i]);
|
||
var newBody = makePage();
|
||
pages.push(newBody.parentNode); // .invoice-preview
|
||
currentBody = newBody;
|
||
currentTbody = makeContTable(newBody);
|
||
currentTbody.appendChild(rows[i]);
|
||
}
|
||
}
|
||
|
||
// summary auf letzte Seite; falls dort kein Platz, nochmal neue Seite
|
||
currentBody.appendChild(summary);
|
||
var lastPage = pages[pages.length - 1];
|
||
if (currentBody.offsetHeight > availH) {
|
||
var extraBody = makePage();
|
||
pages.push(extraBody.parentNode);
|
||
extraBody.appendChild(summary);
|
||
}
|
||
|
||
// Seitenzahlen aktualisieren
|
||
var total = wrap.querySelectorAll('.invoice-preview').length;
|
||
wrap.querySelectorAll('.page-num').forEach(function (el, i) {
|
||
el.textContent = String(i + 1);
|
||
});
|
||
wrap.querySelectorAll('.page-total').forEach(function (el) {
|
||
el.textContent = String(total);
|
||
});
|
||
}
|
||
|
||
if (document.readyState === 'loading') {
|
||
document.addEventListener('DOMContentLoaded', paginate);
|
||
} else {
|
||
paginate();
|
||
}
|
||
}());
|
||
</script>
|
||
|
||
<!-- E-Mail-Senden-Modal -->
|
||
<div id="send-modal" class="modal-overlay">
|
||
<div class="modal-inline-form modal-card-invoice modal-card-invoice--sm">
|
||
<h3 class="modal-form-title">Rechnung per E-Mail senden</h3>
|
||
<form method="post" action="/invoices/<?= $h($invoice['id']) ?>/send">
|
||
<div class="form-group">
|
||
<label>Empfänger *</label>
|
||
<input type="email" name="email_to" required
|
||
value="<?= $h($invoice['email'] ?? '') ?>"
|
||
placeholder="empfaenger@example.com">
|
||
</div>
|
||
<div class="form-group">
|
||
<label title="optional">CC 1</label>
|
||
<input type="email" name="email_cc1" placeholder="cc1@example.com"
|
||
value="<?= $h($customer['email_cc1'] ?? '') ?>">
|
||
</div>
|
||
<div class="form-group">
|
||
<label title="optional">CC 2</label>
|
||
<input type="email" name="email_cc2" placeholder="cc2@example.com"
|
||
value="<?= $h($customer['email_cc2'] ?? '') ?>">
|
||
</div>
|
||
<div class="modal-actions">
|
||
<button type="button" class="btn btn-secondary" data-modal-close>Abbrechen</button>
|
||
<button type="submit" class="btn btn-primary">Senden</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Zahlungs-Modal -->
|
||
<?php if (!in_array($invoice['status'], ['Entwurf', 'Storniert']) && !empty($invoice['beleg_id'])): ?>
|
||
<?php
|
||
$kontenGruppen = \Steuerkram\View::gruppiereKonten($accounts, true);
|
||
?>
|
||
<div id="buchungs-modal" class="modal-overlay modal-overlay--scroll">
|
||
<div class="modal-card-invoice modal-card-invoice--lg">
|
||
<div class="modal-section modal-section-border row-between">
|
||
<h3 class="m-0">Zahlungen – <?= $h($invoice['rechnungs_nr']) ?></h3>
|
||
<button data-modal-close class="modal-card-close modal-card-close--lg">×</button>
|
||
</div>
|
||
|
||
<!-- Vorhandene Buchungen -->
|
||
<div class="modal-section modal-section-border table-wrap">
|
||
<table class="booking-table">
|
||
<thead>
|
||
<tr>
|
||
<th>Datum</th>
|
||
<th>Kategorie</th>
|
||
<th>Zahlungskonto</th>
|
||
<th class="num-right">Netto</th>
|
||
<th class="num-right">USt</th>
|
||
<th class="num-right">Brutto</th>
|
||
<th>Text</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<?php foreach ($bookings as $bk):
|
||
$bkStorno = (bool)$bk['is_reversal'] || !empty($bk['reversed_by']);
|
||
?>
|
||
<tr<?= $bkStorno ? ' class="is-storno"' : '' ?>>
|
||
<td class="nowrap"><?= $h(View::formatDate($bk['buchungs_datum'])) ?></td>
|
||
<td class="cell-sub"><?= $h($bk['kategorie_nr']) ?><br><span class="text-muted"><?= $h($bk['kategorie_name']) ?></span></td>
|
||
<td class="cell-sub"><?= $h($bk['zahlungs_nr']) ?><br><span class="text-muted"><?= $h($bk['zahlungs_name']) ?></span></td>
|
||
<td class="num-right text-mono"><?= $fmt($bk['betrag_netto']) ?></td>
|
||
<td class="num-right"><?= number_format((float)$bk['ust_satz'], 0) ?> %</td>
|
||
<td class="num-right text-mono font-bold"><?= $fmt($bk['betrag_brutto']) ?></td>
|
||
<td class="text-muted cell-sub">
|
||
<?= $h($bk['buchungstext']) ?>
|
||
<?php if ((bool)$bk['is_reversal']): ?><span class="text-danger cell-storno-tag">[Storno]</span>
|
||
<?php elseif (!empty($bk['reversed_by'])): ?><span class="text-danger cell-storno-tag">[Storniert]</span><?php endif; ?>
|
||
</td>
|
||
</tr>
|
||
<?php endforeach; ?>
|
||
<?php if (empty($bookings)): ?>
|
||
<tr><td colspan="7" class="text-center text-faint cell-empty">Noch keine Buchungen.</td></tr>
|
||
<?php endif; ?>
|
||
</tbody>
|
||
<?php
|
||
$gezahlt = \Steuerkram\Models\Booking::sumZahlungen($bookings);
|
||
$offen = round((float)$invoice['betrag_brutto'] - $gezahlt, 2);
|
||
?>
|
||
<tfoot>
|
||
<tr>
|
||
<td colspan="5" class="num-right">Rechnungsbetrag</td>
|
||
<td class="num-right text-mono"><?= $fmt($invoice['betrag_brutto']) ?></td>
|
||
<td></td>
|
||
</tr>
|
||
<tr>
|
||
<td colspan="5" class="num-right">Gezahlt</td>
|
||
<td class="num-right text-mono"><?= $fmt($gezahlt) ?></td>
|
||
<td></td>
|
||
</tr>
|
||
<tr>
|
||
<td colspan="5" class="num-right font-bold"><?= $offen < 0 ? 'Überzahlt' : 'Offen' ?></td>
|
||
<td class="num-right text-mono font-bold"><?= $fmt(abs($offen)) ?></td>
|
||
<td></td>
|
||
</tr>
|
||
</tfoot>
|
||
</table>
|
||
</div>
|
||
|
||
<!-- Neue Buchung erfassen -->
|
||
<?php
|
||
// Vorbelegung: noch offener Restbetrag, umgerechnet auf netto mit dem USt-Satz der Rechnung
|
||
$ustQuote = (float)$invoice['betrag_netto'] != 0.0
|
||
? (float)$invoice['betrag_ust'] / (float)$invoice['betrag_netto']
|
||
: 0.0;
|
||
$vorschlagNet = $offen > 0
|
||
? round($offen / (1 + $ustQuote), 2)
|
||
: abs((float)$invoice['betrag_netto']);
|
||
$ustVorgabe = (string)(int)round($ustQuote * 100);
|
||
if (!in_array($ustVorgabe, ['0', '7', '19'], true)) $ustVorgabe = '19';
|
||
?>
|
||
<div class="modal-section">
|
||
<div class="booking-form-title">Neue Buchung erfassen</div>
|
||
<form method="post" action="/invoices/<?= $h($invoice['id']) ?>/bookings">
|
||
<input type="hidden" name="_from_modal" value="1">
|
||
<div class="booking-form-row">
|
||
<div class="booking-form-field">
|
||
<label>Zahldatum *</label>
|
||
<input type="date" name="buchungs_datum" value="<?= date('Y-m-d') ?>" required class="w-full min-w-0">
|
||
</div>
|
||
<div class="booking-form-field">
|
||
<label>Nettobetrag (€) *</label>
|
||
<input type="number" name="betrag_netto" step="0.01" min="0.01" required
|
||
value="<?= $h(number_format(abs($vorschlagNet), 2, '.', '')) ?>"
|
||
class="w-full min-w-0">
|
||
</div>
|
||
</div>
|
||
<div class="booking-form-row">
|
||
<div class="booking-form-field">
|
||
<label>Kategorie-Konto *</label>
|
||
<select name="kategorie_konto_id" required class="w-full min-w-0">
|
||
<option value="">– Konto wählen –</option>
|
||
<?php foreach ($kontenGruppen['Betriebseinnahme'] ?? [] as $acc): ?>
|
||
<option value="<?= $h($acc['id']) ?>"><?= $h($acc['kontonummer']) ?> – <?= $h($acc['bezeichnung']) ?></option>
|
||
<?php endforeach; ?>
|
||
</select>
|
||
</div>
|
||
<div class="booking-form-field">
|
||
<label>Zahlungskonto *</label>
|
||
<select name="zahlungskonto_id" required class="w-full min-w-0">
|
||
<option value="">– Konto wählen –</option>
|
||
<?php foreach ($kontenGruppen['Zahlungskonto'] ?? [] as $acc): ?>
|
||
<option value="<?= $h($acc['id']) ?>"><?= $h($acc['kontonummer']) ?> – <?= $h($acc['bezeichnung']) ?></option>
|
||
<?php endforeach; ?>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div class="booking-form-row">
|
||
<div class="booking-form-field">
|
||
<label>USt-Satz (%)</label>
|
||
<select name="ust_satz" class="w-full min-w-0">
|
||
<?php foreach (['0','7','19'] as $s): ?>
|
||
<option value="<?= $s ?>"<?= $s === $ustVorgabe ? ' selected' : '' ?>><?= $s ?> %</option>
|
||
<?php endforeach; ?>
|
||
</select>
|
||
</div>
|
||
<div class="booking-form-field">
|
||
<label>Buchungstext *</label>
|
||
<input type="text" name="buchungstext" required
|
||
value="Ausgangsrechnung <?= $h($invoice['rechnungs_nr'] ?? '') ?>"
|
||
class="w-full min-w-0">
|
||
</div>
|
||
</div>
|
||
<div class="row-end">
|
||
<button type="button" class="btn btn-secondary" data-modal-close>Schließen</button>
|
||
<button type="submit" class="btn btn-primary">Buchung erfassen</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<script>
|
||
(function () {
|
||
var flash = document.querySelector('.flash-error, .flash-success');
|
||
if (flash) {
|
||
var ref = document.referrer || '';
|
||
if (ref.indexOf('/invoices/<?= (int)$invoice['id'] ?>/bookings') !== -1) {
|
||
document.getElementById('buchungs-modal').classList.add('open');
|
||
}
|
||
}
|
||
}());
|
||
</script>
|
||
<?php endif; ?>
|