Merge pull request 'Fix: Buchungsvorlagen-Modal – Gruppenunabhängiges Feldrendering und Komma als Dezimaltrenner' (#97) from fds2610-bot/Steuerkram:feature/buchungsvorlagen into main
Reviewed-on: https://codeberg.org/fds2610/Steuerkram/pulls/97
This commit is contained in:
commit
29b3f5eb39
6 changed files with 244 additions and 89 deletions
|
|
@ -524,17 +524,18 @@ document.addEventListener('input', function(e) {
|
||||||
if (!inp.matches('#vorlage-modal .vorlage-netto, #vorlage-modal .vorlage-brutto')) return;
|
if (!inp.matches('#vorlage-modal .vorlage-netto, #vorlage-modal .vorlage-brutto')) return;
|
||||||
const row = inp.closest('.vorlage-ust-row');
|
const row = inp.closest('.vorlage-ust-row');
|
||||||
if (!row) return;
|
if (!row) return;
|
||||||
|
const pDec = v => parseFloat(String(v).replace(',', '.')) || 0;
|
||||||
const satz = parseFloat(row.dataset.ustSatz) / 100;
|
const satz = parseFloat(row.dataset.ustSatz) / 100;
|
||||||
const netto = row.querySelector('.vorlage-netto');
|
const netto = row.querySelector('.vorlage-netto');
|
||||||
const brutto = row.querySelector('.vorlage-brutto');
|
const brutto = row.querySelector('.vorlage-brutto');
|
||||||
const ust = row.querySelector('.vorlage-ust');
|
const ust = row.querySelector('.vorlage-ust');
|
||||||
const ustAnzeige = row.querySelector('.vorlage-ust-anzeige');
|
const ustAnzeige = row.querySelector('.vorlage-ust-anzeige');
|
||||||
if (inp.classList.contains('vorlage-netto')) {
|
if (inp.classList.contains('vorlage-netto')) {
|
||||||
const n = parseFloat(netto.value) || 0;
|
const n = pDec(netto.value);
|
||||||
brutto.value = (n * (1 + satz)).toFixed(2);
|
brutto.value = (n * (1 + satz)).toFixed(2);
|
||||||
ust.value = (n * satz).toFixed(2);
|
ust.value = (n * satz).toFixed(2);
|
||||||
} else {
|
} else {
|
||||||
const b = parseFloat(brutto.value) || 0;
|
const b = pDec(brutto.value);
|
||||||
const n = b / (1 + satz);
|
const n = b / (1 + satz);
|
||||||
netto.value = n.toFixed(2);
|
netto.value = n.toFixed(2);
|
||||||
ust.value = (b - n).toFixed(2);
|
ust.value = (b - n).toFixed(2);
|
||||||
|
|
@ -547,7 +548,7 @@ function vorlageUpdateBruttoGesamt() {
|
||||||
const modal = document.getElementById('vorlage-modal');
|
const modal = document.getElementById('vorlage-modal');
|
||||||
if (!modal) return;
|
if (!modal) return;
|
||||||
const bruttoFelder = modal.querySelectorAll('.vorlage-brutto');
|
const bruttoFelder = modal.querySelectorAll('.vorlage-brutto');
|
||||||
const summe = Array.from(bruttoFelder).reduce((s, el) => s + (parseFloat(el.value) || 0), 0);
|
const summe = Array.from(bruttoFelder).reduce((s, el) => s + (parseFloat(String(el.value).replace(',', '.')) || 0), 0);
|
||||||
const anzeige = modal.querySelector('[data-feldname="brutto_gesamt"]');
|
const anzeige = modal.querySelector('[data-feldname="brutto_gesamt"]');
|
||||||
if (anzeige) anzeige.textContent = summe.toFixed(2).replace('.', ',') + ' €';
|
if (anzeige) anzeige.textContent = summe.toFixed(2).replace('.', ',') + ' €';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -211,7 +211,12 @@ class BankController extends BaseController
|
||||||
View::redirect('/bank/upload', 'Import fehlgeschlagen: ' . $e->getMessage(), 'error');
|
View::redirect('/bank/upload', 'Import fehlgeschlagen: ' . $e->getMessage(), 'error');
|
||||||
}
|
}
|
||||||
|
|
||||||
View::redirect('/bank', count($rows) . ' Buchungen importiert.');
|
$paired = \Steuerkram\Services\PayPalNvpFetchService::applyBankImportRules($batchId);
|
||||||
|
$msg = count($rows) . ' Buchungen importiert.';
|
||||||
|
if ($paired > 0) {
|
||||||
|
$msg .= ' ' . $paired . ' PayPal-Gegenbuchung(en) automatisch ignoriert.';
|
||||||
|
}
|
||||||
|
View::redirect('/bank', $msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function show(array $params): void
|
public function show(array $params): void
|
||||||
|
|
|
||||||
|
|
@ -115,6 +115,144 @@ class PayPalNvpFetchService
|
||||||
$this->updateLastFetched();
|
$this->updateLastFetched();
|
||||||
|
|
||||||
($this->log)(sprintf('Fertig. %d neu importiert, %d bereits vorhanden.', $totalNew, $totalSkip));
|
($this->log)(sprintf('Fertig. %d neu importiert, %d bereits vorhanden.', $totalNew, $totalSkip));
|
||||||
|
|
||||||
|
($this->log)('');
|
||||||
|
($this->log)('Automatische Regeln werden angewendet…');
|
||||||
|
$this->applyAutoRules();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wird nach einem Bank-CSV-Import aufgerufen.
|
||||||
|
* Findet Abbuchungen mit „paypal" im Verwendungszweck und setzt sie + den
|
||||||
|
* passenden PayPal-Transfer auf „ignoriert", wenn das Paar existiert.
|
||||||
|
*/
|
||||||
|
public static function applyBankImportRules(int $batchId): int
|
||||||
|
{
|
||||||
|
$debits = Database::fetchAll(
|
||||||
|
"SELECT bt.id, bt.buchungstag, bt.wert
|
||||||
|
FROM bank_transactions bt
|
||||||
|
WHERE bt.batch_id = ?
|
||||||
|
AND bt.wert < 0
|
||||||
|
AND LOWER(bt.buchungstext) LIKE '%paypal%'
|
||||||
|
AND bt.status = 'offen'",
|
||||||
|
[$batchId]
|
||||||
|
);
|
||||||
|
|
||||||
|
$paired = 0;
|
||||||
|
foreach ($debits as $debit) {
|
||||||
|
$dateFrom = date('Y-m-d', strtotime($debit['buchungstag'] . ' -7 days'));
|
||||||
|
$dateTo = date('Y-m-d', strtotime($debit['buchungstag'] . ' +2 days'));
|
||||||
|
$amount = abs((float)$debit['wert']);
|
||||||
|
|
||||||
|
// Passenden PayPal-Transfer suchen (positiver Betrag gleicher Höhe)
|
||||||
|
$transfer = Database::fetchOne(
|
||||||
|
"SELECT bt.id FROM bank_transactions bt
|
||||||
|
JOIN bank_import_batches bib ON bt.batch_id = bib.id
|
||||||
|
JOIN bank_sources bs ON bib.source_id = bs.id
|
||||||
|
WHERE bs.type = 'api'
|
||||||
|
AND bt.vorgang = 'Transfer'
|
||||||
|
AND bt.buchungstext = 'Bank Account (direct debit)'
|
||||||
|
AND bt.wert = ?
|
||||||
|
AND bt.buchungstag BETWEEN ? AND ?
|
||||||
|
AND bt.status != 'ignoriert'
|
||||||
|
LIMIT 1",
|
||||||
|
[$amount, $dateFrom, $dateTo]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!$transfer) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Database::execute(
|
||||||
|
"UPDATE bank_transactions SET status = 'ignoriert' WHERE id = ?",
|
||||||
|
[$debit['id']]
|
||||||
|
);
|
||||||
|
Database::execute(
|
||||||
|
"UPDATE bank_transactions SET status = 'ignoriert' WHERE id = ?",
|
||||||
|
[$transfer['id']]
|
||||||
|
);
|
||||||
|
$paired++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $paired;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function applyAutoRules(): void
|
||||||
|
{
|
||||||
|
// Regel 1: Authorization → ignoriert
|
||||||
|
$auths = Database::fetchAll(
|
||||||
|
"SELECT id FROM bank_transactions bt
|
||||||
|
JOIN bank_import_batches bib ON bt.batch_id = bib.id
|
||||||
|
WHERE bib.source_id = ? AND bt.vorgang = 'Authorization' AND bt.status = 'offen'",
|
||||||
|
[$this->sourceId]
|
||||||
|
);
|
||||||
|
foreach ($auths as $row) {
|
||||||
|
Database::execute(
|
||||||
|
"UPDATE bank_transactions SET status = 'ignoriert' WHERE id = ?",
|
||||||
|
[$row['id']]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if ($auths) {
|
||||||
|
($this->log)(count($auths) . ' Authorization-Buchung(en) auf „ignoriert" gesetzt.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Regel 2: Transfer „Bank Account (direct debit)" + passende Bank-Gegenbuchung
|
||||||
|
// Suche alle noch offenen Transfers dieser PayPal-Quelle
|
||||||
|
$transfers = Database::fetchAll(
|
||||||
|
"SELECT bt.id, bt.buchungstag, bt.wert
|
||||||
|
FROM bank_transactions bt
|
||||||
|
JOIN bank_import_batches bib ON bt.batch_id = bib.id
|
||||||
|
WHERE bib.source_id = ?
|
||||||
|
AND bt.vorgang = 'Transfer'
|
||||||
|
AND bt.buchungstext = 'Bank Account (direct debit)'
|
||||||
|
AND bt.wert > 0
|
||||||
|
AND bt.status = 'offen'",
|
||||||
|
[$this->sourceId]
|
||||||
|
);
|
||||||
|
|
||||||
|
$pairedCount = 0;
|
||||||
|
foreach ($transfers as $transfer) {
|
||||||
|
// Passende Bank-Abbuchung in allen anderen Quellen suchen (+/- 7 Tage, gleicher Betrag negiert)
|
||||||
|
$dateFrom = date('Y-m-d', strtotime($transfer['buchungstag'] . ' -2 days'));
|
||||||
|
$dateTo = date('Y-m-d', strtotime($transfer['buchungstag'] . ' +7 days'));
|
||||||
|
$amount = -abs((float)$transfer['wert']);
|
||||||
|
|
||||||
|
$bankDebit = Database::fetchOne(
|
||||||
|
"SELECT bt.id FROM bank_transactions bt
|
||||||
|
JOIN bank_import_batches bib ON bt.batch_id = bib.id
|
||||||
|
WHERE bib.source_id != ?
|
||||||
|
AND bt.wert = ?
|
||||||
|
AND bt.buchungstag BETWEEN ? AND ?
|
||||||
|
AND LOWER(bt.buchungstext) LIKE '%paypal%'
|
||||||
|
AND bt.status != 'ignoriert'
|
||||||
|
LIMIT 1",
|
||||||
|
[$this->sourceId, $amount, $dateFrom, $dateTo]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!$bankDebit) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Database::execute(
|
||||||
|
"UPDATE bank_transactions SET status = 'ignoriert' WHERE id = ?",
|
||||||
|
[$transfer['id']]
|
||||||
|
);
|
||||||
|
Database::execute(
|
||||||
|
"UPDATE bank_transactions SET status = 'ignoriert' WHERE id = ?",
|
||||||
|
[$bankDebit['id']]
|
||||||
|
);
|
||||||
|
($this->log)(sprintf(
|
||||||
|
'Gegenbuchung: PayPal-Transfer +%s€ (ID %d) + Bank-Abbuchung (ID %d) → ignoriert',
|
||||||
|
number_format((float)$transfer['wert'], 2, ',', '.'),
|
||||||
|
$transfer['id'],
|
||||||
|
$bankDebit['id']
|
||||||
|
));
|
||||||
|
$pairedCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($pairedCount === 0 && $transfers) {
|
||||||
|
($this->log)(count($transfers) . ' Transfer-Buchung(en) warten noch auf Bank-Gegenbuchung.');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── NVP-API ──────────────────────────────────────────────────
|
// ── NVP-API ──────────────────────────────────────────────────
|
||||||
|
|
@ -183,7 +321,6 @@ class PayPalNvpFetchService
|
||||||
'METHOD' => 'TransactionSearch',
|
'METHOD' => 'TransactionSearch',
|
||||||
'STARTDATE' => $from,
|
'STARTDATE' => $from,
|
||||||
'ENDDATE' => $to,
|
'ENDDATE' => $to,
|
||||||
'STATUS' => 'All',
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if ($result === null) {
|
if ($result === null) {
|
||||||
|
|
@ -193,9 +330,20 @@ class PayPalNvpFetchService
|
||||||
$ack = $result['ACK'] ?? '';
|
$ack = $result['ACK'] ?? '';
|
||||||
if (!in_array($ack, ['Success', 'SuccessWithWarning'], true)) {
|
if (!in_array($ack, ['Success', 'SuccessWithWarning'], true)) {
|
||||||
($this->log)(' API-Fehler: ' . ($result['L_LONGMESSAGE0'] ?? $ack));
|
($this->log)(' API-Fehler: ' . ($result['L_LONGMESSAGE0'] ?? $ack));
|
||||||
|
($this->log)(' ACK: ' . $ack);
|
||||||
|
($this->log)(' Felder in Antwort: ' . implode(', ', array_keys($result)));
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Debug: Anzahl gefundener Felder in der Antwort
|
||||||
|
$txCount = 0;
|
||||||
|
while (isset($result['L_TRANSACTIONID' . $txCount])) {
|
||||||
|
$txCount++;
|
||||||
|
}
|
||||||
|
if ($txCount === 0) {
|
||||||
|
($this->log)(' Debug: Antwort-Felder: ' . implode(', ', array_slice(array_keys($result), 0, 10)));
|
||||||
|
}
|
||||||
|
|
||||||
$rows = [];
|
$rows = [];
|
||||||
$i = 0;
|
$i = 0;
|
||||||
while (isset($result['L_TRANSACTIONID' . $i])) {
|
while (isset($result['L_TRANSACTIONID' . $i])) {
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ class VorlageBuchungsService
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
foreach ($regel['buchungen'] as $buchung) {
|
foreach ($regel['buchungen'] as $buchung) {
|
||||||
$betrag = round((float)($inputDaten[$buchung['betrag_ref']] ?? 0), 2);
|
$betrag = round((float)str_replace(',', '.', (string)($inputDaten[$buchung['betrag_ref']] ?? 0)), 2);
|
||||||
if ($betrag == 0.0) {
|
if ($betrag == 0.0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,14 @@
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
$h = fn($v) => htmlspecialchars((string)$v, ENT_QUOTES, 'UTF-8');
|
$h = fn($v) => htmlspecialchars((string)$v, ENT_QUOTES, 'UTF-8');
|
||||||
|
|
||||||
// Felder nach Gruppen aufteilen
|
// Felder nach Gruppe_nr sortiert gruppieren (dynamisch, unabhängig von festen Nummern)
|
||||||
$gruppe1 = array_filter($felder, fn($f) => $f['gruppe_nr'] == 1);
|
$gruppenFelder = [];
|
||||||
$gruppe2 = array_filter($felder, fn($f) => $f['gruppe_nr'] == 2);
|
foreach ($felder as $feld) {
|
||||||
$gruppe3 = array_filter($felder, fn($f) => $f['gruppe_nr'] == 3);
|
$gruppenFelder[(int)$feld['gruppe_nr']][] = $feld;
|
||||||
|
}
|
||||||
|
ksort($gruppenFelder);
|
||||||
|
|
||||||
|
$gruppenLabel = [1 => 'Belegdaten', 2 => 'Beträge', 3 => 'Zahlung'];
|
||||||
|
|
||||||
// Vorbelegen: Vorlage-Standard-Lieferant hat Vorrang vor Beleg-Lieferant
|
// Vorbelegen: Vorlage-Standard-Lieferant hat Vorrang vor Beleg-Lieferant
|
||||||
$standardLieferant = trim($vorlage['standard_lieferant'] ?? '');
|
$standardLieferant = trim($vorlage['standard_lieferant'] ?? '');
|
||||||
|
|
@ -44,19 +48,83 @@ if (!$defaultKontoId && !empty($zahlungskonten)) {
|
||||||
<input type="hidden" name="bank_tx_id" value="<?= $h($bankTxId) ?>">
|
<input type="hidden" name="bank_tx_id" value="<?= $h($bankTxId) ?>">
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<?php if (!empty($gruppe1)): ?>
|
<?php foreach ($gruppenFelder as $gruppeNr => $felderInGruppe): ?>
|
||||||
<!-- Gruppe 1: Metadaten -->
|
|
||||||
<div style="margin-bottom:1.25rem">
|
<div style="margin-bottom:1.25rem">
|
||||||
<div style="font-weight:600;font-size:.8rem;text-transform:uppercase;letter-spacing:.05em;color:var(--color-text-muted);margin-bottom:.75rem">Belegdaten</div>
|
<div style="font-weight:600;font-size:.8rem;text-transform:uppercase;letter-spacing:.05em;color:var(--color-text-muted);margin-bottom:.75rem">
|
||||||
<?php foreach ($gruppe1 as $feld): ?>
|
<?= $h($gruppenLabel[$gruppeNr] ?? 'Abschnitt ' . $gruppeNr) ?>
|
||||||
<?php if ($feld['typ'] === 'readonly'): ?>
|
</div>
|
||||||
|
<?php
|
||||||
|
$inUstTable = false;
|
||||||
|
foreach ($felderInGruppe as $feld):
|
||||||
|
$typ = $feld['typ'];
|
||||||
|
|
||||||
|
// ust_zeile braucht Tabellenkontext – öffnen/schließen bei Typwechsel
|
||||||
|
if ($typ === 'ust_zeile' && !$inUstTable):
|
||||||
|
$inUstTable = true;
|
||||||
|
?>
|
||||||
|
<table style="width:100%;border-collapse:collapse;font-size:.875rem">
|
||||||
|
<thead>
|
||||||
|
<tr style="color:var(--color-text-muted);font-size:.8rem">
|
||||||
|
<th style="text-align:left;padding:.3rem .5rem;font-weight:500">Steuer</th>
|
||||||
|
<th style="text-align:right;padding:.3rem .5rem;font-weight:500">Netto (€)</th>
|
||||||
|
<th style="text-align:right;padding:.3rem .5rem;font-weight:500">Brutto (€)</th>
|
||||||
|
<th style="text-align:right;padding:.3rem .5rem;font-weight:500;color:var(--color-text-muted)">USt (€)</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php elseif ($typ !== 'ust_zeile' && $inUstTable):
|
||||||
|
$inUstTable = false;
|
||||||
|
?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if ($typ === 'ust_zeile'):
|
||||||
|
$satz = (float)$feld['ust_satz'];
|
||||||
|
$satzInt = (int)$satz;
|
||||||
|
$nKey = $feld['feldname'];
|
||||||
|
$uKey = 'ust_' . $satzInt;
|
||||||
|
$bKey = 'brutto_' . $satzInt;
|
||||||
|
?>
|
||||||
|
<tr data-ust-satz="<?= $h($satz) ?>" class="vorlage-ust-row">
|
||||||
|
<td style="padding:.3rem .5rem"><?= $h($feld['label']) ?></td>
|
||||||
|
<td style="padding:.3rem .5rem">
|
||||||
|
<input type="text" inputmode="decimal" name="<?= $h($nKey) ?>" value=""
|
||||||
|
class="form-control form-control-sm vorlage-netto"
|
||||||
|
style="text-align:right;width:110px"
|
||||||
|
placeholder="0,00">
|
||||||
|
</td>
|
||||||
|
<td style="padding:.3rem .5rem">
|
||||||
|
<input type="text" inputmode="decimal" name="<?= $h($bKey) ?>" value=""
|
||||||
|
class="form-control form-control-sm vorlage-brutto"
|
||||||
|
style="text-align:right;width:110px"
|
||||||
|
placeholder="0,00">
|
||||||
|
</td>
|
||||||
|
<td style="padding:.3rem .5rem;text-align:right">
|
||||||
|
<span class="vorlage-ust-anzeige"
|
||||||
|
style="display:inline-block;width:90px;text-align:right;color:var(--color-text-muted);font-size:.875rem">0,00</span>
|
||||||
|
<input type="hidden" name="<?= $h($uKey) ?>" class="vorlage-ust" value="">
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php elseif ($typ === 'account_select'): ?>
|
||||||
|
<div class="form-group" style="display:flex;align-items:center;gap:.75rem;margin-bottom:.6rem">
|
||||||
|
<label style="min-width:130px;margin:0;font-size:.875rem"><?= $h($feld['label']) ?></label>
|
||||||
|
<select name="zahlungskonto_id" class="form-control form-control-sm" style="flex:1;max-width:320px">
|
||||||
|
<?php foreach ($zahlungskonten as $zk): ?>
|
||||||
|
<option value="<?= $h($zk['id']) ?>"<?= (int)$zk['id'] === $defaultKontoId ? ' selected' : '' ?>>
|
||||||
|
<?= $h($zk['kontonummer']) ?> – <?= $h($zk['bezeichnung']) ?>
|
||||||
|
</option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<?php elseif ($typ === 'readonly'): ?>
|
||||||
<div class="form-group" style="display:flex;align-items:center;gap:.75rem;margin-bottom:.6rem">
|
<div class="form-group" style="display:flex;align-items:center;gap:.75rem;margin-bottom:.6rem">
|
||||||
<label style="min-width:130px;margin:0;font-size:.875rem"><?= $h($feld['label']) ?></label>
|
<label style="min-width:130px;margin:0;font-size:.875rem"><?= $h($feld['label']) ?></label>
|
||||||
<span id="vorlage-field-<?= $h($feld['feldname']) ?>"
|
<span id="vorlage-field-<?= $h($feld['feldname']) ?>"
|
||||||
data-feldname="<?= $h($feld['feldname']) ?>"
|
data-feldname="<?= $h($feld['feldname']) ?>"
|
||||||
style="font-weight:600;font-size:.95rem">0,00 €</span>
|
style="font-weight:600;font-size:.95rem">0,00 €</span>
|
||||||
</div>
|
</div>
|
||||||
<?php elseif ($feld['typ'] === 'date'): ?>
|
<?php elseif ($typ === 'date'): ?>
|
||||||
<div class="form-group" style="display:flex;align-items:center;gap:.75rem;margin-bottom:.6rem;flex-wrap:wrap">
|
<div class="form-group" style="display:flex;align-items:center;gap:.75rem;margin-bottom:.6rem;flex-wrap:wrap">
|
||||||
<label style="min-width:130px;margin:0;font-size:.875rem"><?= $h($feld['label']) ?></label>
|
<label style="min-width:130px;margin:0;font-size:.875rem"><?= $h($feld['label']) ?></label>
|
||||||
<input type="date" name="<?= $h($feld['feldname']) ?>"
|
<input type="date" name="<?= $h($feld['feldname']) ?>"
|
||||||
|
|
@ -69,88 +137,21 @@ if (!$defaultKontoId && !empty($zahlungskonten)) {
|
||||||
style="width:150px" class="form-control form-control-sm">
|
style="width:150px" class="form-control form-control-sm">
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<div class="form-group" style="display:flex;align-items:center;gap:.75rem;margin-bottom:.6rem">
|
<div class="form-group" style="display:flex;align-items:center;gap:.75rem;margin-bottom:.6rem">
|
||||||
<label style="min-width:130px;margin:0;font-size:.875rem"><?= $h($feld['label']) ?></label>
|
<label style="min-width:130px;margin:0;font-size:.875rem"><?= $h($feld['label']) ?></label>
|
||||||
<input type="text" name="<?= $h($feld['feldname']) ?>"
|
<input type="text" name="<?= $h($feld['feldname']) ?>"
|
||||||
value="<?= $h($vorbelegt[$feld['feldname']] ?? '') ?>"
|
value="<?= $h($vorbelegt[$feld['feldname']] ?? '') ?>"
|
||||||
style="flex:1" class="form-control form-control-sm">
|
style="flex:1" class="form-control form-control-sm">
|
||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</div>
|
<?php if ($inUstTable): ?>
|
||||||
<?php endif; ?>
|
|
||||||
|
|
||||||
<?php if (!empty($gruppe2)): ?>
|
|
||||||
<!-- Gruppe 2: USt-Zeilen -->
|
|
||||||
<div style="margin-bottom:1.25rem">
|
|
||||||
<div style="font-weight:600;font-size:.8rem;text-transform:uppercase;letter-spacing:.05em;color:var(--color-text-muted);margin-bottom:.75rem">Beträge</div>
|
|
||||||
<table style="width:100%;border-collapse:collapse;font-size:.875rem">
|
|
||||||
<thead>
|
|
||||||
<tr style="color:var(--color-text-muted);font-size:.8rem">
|
|
||||||
<th style="text-align:left;padding:.3rem .5rem;font-weight:500">Steuer</th>
|
|
||||||
<th style="text-align:right;padding:.3rem .5rem;font-weight:500">Netto (€)</th>
|
|
||||||
<th style="text-align:right;padding:.3rem .5rem;font-weight:500">Brutto (€)</th>
|
|
||||||
<th style="text-align:right;padding:.3rem .5rem;font-weight:500;color:var(--color-text-muted)">USt (€)</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<?php foreach ($gruppe2 as $feld): ?>
|
|
||||||
<?php if ($feld['typ'] === 'ust_zeile'): ?>
|
|
||||||
<?php
|
|
||||||
$satz = (float)$feld['ust_satz'];
|
|
||||||
$satzInt = (int)$satz;
|
|
||||||
$nKey = $feld['feldname'];
|
|
||||||
$uKey = 'ust_' . $satzInt;
|
|
||||||
$bKey = 'brutto_' . $satzInt;
|
|
||||||
?>
|
|
||||||
<tr data-ust-satz="<?= $h($satz) ?>" class="vorlage-ust-row">
|
|
||||||
<td style="padding:.3rem .5rem"><?= $h($feld['label']) ?></td>
|
|
||||||
<td style="padding:.3rem .5rem">
|
|
||||||
<input type="number" name="<?= $h($nKey) ?>" step="0.01" min="0" value=""
|
|
||||||
class="form-control form-control-sm vorlage-netto"
|
|
||||||
style="text-align:right;width:110px"
|
|
||||||
placeholder="0,00">
|
|
||||||
</td>
|
|
||||||
<td style="padding:.3rem .5rem">
|
|
||||||
<input type="number" name="<?= $h($bKey) ?>" step="0.01" min="0" value=""
|
|
||||||
class="form-control form-control-sm vorlage-brutto"
|
|
||||||
style="text-align:right;width:110px"
|
|
||||||
placeholder="0,00">
|
|
||||||
</td>
|
|
||||||
<td style="padding:.3rem .5rem;text-align:right">
|
|
||||||
<span class="vorlage-ust-anzeige"
|
|
||||||
style="display:inline-block;width:90px;text-align:right;color:var(--color-text-muted);font-size:.875rem">0,00</span>
|
|
||||||
<input type="hidden" name="<?= $h($uKey) ?>" class="vorlage-ust" value="">
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endforeach; ?>
|
||||||
|
|
||||||
<?php if (!empty($gruppe3)): ?>
|
|
||||||
<!-- Gruppe 3: Zahlungskonto -->
|
|
||||||
<div style="margin-bottom:1.25rem">
|
|
||||||
<div style="font-weight:600;font-size:.8rem;text-transform:uppercase;letter-spacing:.05em;color:var(--color-text-muted);margin-bottom:.75rem">Zahlung</div>
|
|
||||||
<?php foreach ($gruppe3 as $feld): ?>
|
|
||||||
<?php if ($feld['typ'] === 'account_select'): ?>
|
|
||||||
<div class="form-group" style="display:flex;align-items:center;gap:.75rem;margin-bottom:.6rem">
|
|
||||||
<label style="min-width:130px;margin:0;font-size:.875rem"><?= $h($feld['label']) ?></label>
|
|
||||||
<select name="zahlungskonto_id" class="form-control form-control-sm" style="flex:1;max-width:320px">
|
|
||||||
<?php foreach ($zahlungskonten as $zk): ?>
|
|
||||||
<option value="<?= $h($zk['id']) ?>"<?= (int)$zk['id'] === $defaultKontoId ? ' selected' : '' ?>>
|
|
||||||
<?= $h($zk['kontonummer']) ?> – <?= $h($zk['bezeichnung']) ?>
|
|
||||||
</option>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
1-260601
|
1-260608
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue