Feature: Buchungsvorlagen – Bankbuchung verknüpfen, Zahldatum, Standard-Lieferant
- Vorlagenbuchung verknüpft jetzt korrekt die Bankbuchung (bank_tx_id) - Buchungs-Redirect bleibt nach Vorlagenbuchung auf dem Beleg - Zahldatum (Wertstellung der Bankbuchung) im Vorlagendialog neben Belegdatum - USt-Felder im Vorlagendialog als Text (nicht mehr als editierbare Inputs) - Standard-Lieferant pro Vorlage speicherbar; wird im Buchungsdialog vorbelegt - Migration 081: spalte standard_lieferant in buchungsvorlagen
This commit is contained in:
parent
9f1baf4034
commit
3fe5015027
7 changed files with 72 additions and 33 deletions
11
migrations/081_buchungsvorlage_standard_lieferant.sql
Normal file
11
migrations/081_buchungsvorlage_standard_lieferant.sql
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
-- 081_buchungsvorlage_standard_lieferant.sql
|
||||||
|
-- Standard-Lieferant pro Vorlage; wird im Buchungsdialog vorbelegt.
|
||||||
|
|
||||||
|
ALTER TABLE buchungsvorlagen
|
||||||
|
ADD COLUMN IF NOT EXISTS standard_lieferant VARCHAR(255) NULL DEFAULT NULL AFTER beschreibung;
|
||||||
|
|
||||||
|
-- Bestehende Vorlage "Essensbeleg" mit Standardlieferant "Lebensmittel" vorbelegen.
|
||||||
|
UPDATE buchungsvorlagen
|
||||||
|
SET standard_lieferant = 'Lebensmittel'
|
||||||
|
WHERE name = 'Essensbeleg n. abzugsfähig (nur USt.)'
|
||||||
|
AND (standard_lieferant IS NULL OR standard_lieferant = '');
|
||||||
|
|
@ -528,6 +528,7 @@ document.addEventListener('input', function(e) {
|
||||||
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');
|
||||||
if (inp.classList.contains('vorlage-netto')) {
|
if (inp.classList.contains('vorlage-netto')) {
|
||||||
const n = parseFloat(netto.value) || 0;
|
const n = parseFloat(netto.value) || 0;
|
||||||
brutto.value = (n * (1 + satz)).toFixed(2);
|
brutto.value = (n * (1 + satz)).toFixed(2);
|
||||||
|
|
@ -538,6 +539,7 @@ document.addEventListener('input', function(e) {
|
||||||
netto.value = n.toFixed(2);
|
netto.value = n.toFixed(2);
|
||||||
ust.value = (b - n).toFixed(2);
|
ust.value = (b - n).toFixed(2);
|
||||||
}
|
}
|
||||||
|
if (ustAnzeige) ustAnzeige.textContent = parseFloat(ust.value).toFixed(2).replace('.', ',');
|
||||||
vorlageUpdateBruttoGesamt();
|
vorlageUpdateBruttoGesamt();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -548,19 +550,11 @@ function vorlageUpdateBruttoGesamt() {
|
||||||
const summe = Array.from(bruttoFelder).reduce((s, el) => s + (parseFloat(el.value) || 0), 0);
|
const summe = Array.from(bruttoFelder).reduce((s, el) => s + (parseFloat(el.value) || 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('.', ',') + ' €';
|
||||||
// Auch das buchungs_datum-Hidden synchronisieren mit belegdatum-Feld falls vorhanden
|
|
||||||
const datumFeld = modal.querySelector('input[name="belegdatum"]');
|
|
||||||
const datumHidden = modal.querySelector('input[name="buchungs_datum"]');
|
|
||||||
if (datumFeld && datumHidden && datumFeld.value) {
|
|
||||||
datumHidden.value = datumFeld.value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Datum-Feld Änderung → buchungs_datum synchronisieren
|
// Zahldatum-Feld Änderung → buchungs_datum-Hidden synchronisieren
|
||||||
document.addEventListener('change', function(e) {
|
document.addEventListener('change', function(e) {
|
||||||
if (!e.target.matches('#vorlage-modal input[name="belegdatum"]')) return;
|
if (!e.target.matches('#vorlage-modal #vorlage-zahldatum')) return;
|
||||||
const modal = document.getElementById('vorlage-modal');
|
const hidden = document.getElementById('vorlage-buchungs-datum');
|
||||||
if (!modal) return;
|
|
||||||
const hidden = modal.querySelector('input[name="buchungs_datum"]');
|
|
||||||
if (hidden) hidden.value = e.target.value;
|
if (hidden) hidden.value = e.target.value;
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -141,14 +141,20 @@ class BuchungsvorlageController extends BaseController
|
||||||
$zahlungskonten = Account::all(['kontenart' => 'Zahlungskonto', 'aktiv' => 1]);
|
$zahlungskonten = Account::all(['kontenart' => 'Zahlungskonto', 'aktiv' => 1]);
|
||||||
$zahlungskonten = array_filter($zahlungskonten, fn($a) => (int)$a['kontonummer'] >= 1200 && (int)$a['kontonummer'] <= 1299);
|
$zahlungskonten = array_filter($zahlungskonten, fn($a) => (int)$a['kontonummer'] >= 1200 && (int)$a['kontonummer'] <= 1299);
|
||||||
|
|
||||||
|
$bankTxId = (int)($_GET['bank_tx'] ?? 0);
|
||||||
|
$bankTxDatum = '';
|
||||||
$hatBankbuchung = false;
|
$hatBankbuchung = false;
|
||||||
if ($beleg) {
|
if ($beleg) {
|
||||||
$linked = BankTransaction::findByBelegId((int)$beleg['id']);
|
$linked = BankTransaction::findByBelegId((int)$beleg['id']);
|
||||||
$hatBankbuchung = !empty($linked);
|
$hatBankbuchung = !empty($linked);
|
||||||
if (!$hatBankbuchung && !empty($_GET['bank_tx'])) {
|
if (!$hatBankbuchung && $bankTxId > 0) {
|
||||||
$hatBankbuchung = true;
|
$hatBankbuchung = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ($bankTxId > 0) {
|
||||||
|
$bankTx = BankTransaction::find($bankTxId);
|
||||||
|
$bankTxDatum = $bankTx['wertstellung'] ?? '';
|
||||||
|
}
|
||||||
|
|
||||||
echo $this->renderView('buchungsvorlagen/modal', [
|
echo $this->renderView('buchungsvorlagen/modal', [
|
||||||
'vorlage' => $vorlage,
|
'vorlage' => $vorlage,
|
||||||
|
|
@ -157,6 +163,8 @@ class BuchungsvorlageController extends BaseController
|
||||||
'belegId' => $belegId,
|
'belegId' => $belegId,
|
||||||
'zahlungskonten' => array_values($zahlungskonten),
|
'zahlungskonten' => array_values($zahlungskonten),
|
||||||
'hatBankbuchung' => $hatBankbuchung,
|
'hatBankbuchung' => $hatBankbuchung,
|
||||||
|
'bankTxId' => $bankTxId,
|
||||||
|
'bankTxDatum' => $bankTxDatum,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -171,16 +179,23 @@ class BuchungsvorlageController extends BaseController
|
||||||
if (!$beleg) { http_response_code(404); return; }
|
if (!$beleg) { http_response_code(404); return; }
|
||||||
|
|
||||||
$datum = !empty($_POST['buchungs_datum']) ? $_POST['buchungs_datum'] : date('Y-m-d');
|
$datum = !empty($_POST['buchungs_datum']) ? $_POST['buchungs_datum'] : date('Y-m-d');
|
||||||
|
$bankTxId = (int)($_POST['bank_tx_id'] ?? 0);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$svc = new VorlageBuchungsService();
|
$svc = new VorlageBuchungsService();
|
||||||
$svc->buchen($belegId, $vorlageId, $_POST, $datum);
|
$svc->buchen($belegId, $vorlageId, $_POST, $datum);
|
||||||
|
|
||||||
|
if ($bankTxId > 0) {
|
||||||
|
$totalBrutto = \Steuerkram\Models\Booking::sumBruttoForBeleg($belegId);
|
||||||
|
BankTransaction::linkBeleg($bankTxId, $belegId, $totalBrutto);
|
||||||
|
}
|
||||||
} catch (\RuntimeException $e) {
|
} catch (\RuntimeException $e) {
|
||||||
View::redirect("/belege/{$belegId}", $e->getMessage(), 'error');
|
View::redirect("/belege/{$belegId}", $e->getMessage(), 'error');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
View::redirect("/belege/{$belegId}", 'Buchungen aus Vorlage erfasst.');
|
$msg = $bankTxId > 0 ? 'Buchungen aus Vorlage erfasst und Bankbuchung verknüpft.' : 'Buchungen aus Vorlage erfasst.';
|
||||||
|
View::redirect("/belege/{$belegId}", $msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Hilfsmethoden ──────────────────────────────────────────────────────
|
// ── Hilfsmethoden ──────────────────────────────────────────────────────
|
||||||
|
|
|
||||||
|
|
@ -54,11 +54,12 @@ class Buchungsvorlage
|
||||||
public static function create(array $data): int
|
public static function create(array $data): int
|
||||||
{
|
{
|
||||||
Database::execute(
|
Database::execute(
|
||||||
'INSERT INTO buchungsvorlagen (name, beschreibung, aktiv, sort_order)
|
'INSERT INTO buchungsvorlagen (name, beschreibung, standard_lieferant, aktiv, sort_order)
|
||||||
VALUES (?, ?, ?, ?)',
|
VALUES (?, ?, ?, ?, ?)',
|
||||||
[
|
[
|
||||||
trim($data['name']),
|
trim($data['name']),
|
||||||
$data['beschreibung'] ?? null,
|
$data['beschreibung'] ?? null,
|
||||||
|
$data['standard_lieferant'] !== '' ? trim($data['standard_lieferant'] ?? '') : null,
|
||||||
isset($data['aktiv']) ? (int)$data['aktiv'] : 1,
|
isset($data['aktiv']) ? (int)$data['aktiv'] : 1,
|
||||||
(int)($data['sort_order'] ?? 0),
|
(int)($data['sort_order'] ?? 0),
|
||||||
]
|
]
|
||||||
|
|
@ -70,11 +71,12 @@ class Buchungsvorlage
|
||||||
{
|
{
|
||||||
Database::execute(
|
Database::execute(
|
||||||
'UPDATE buchungsvorlagen
|
'UPDATE buchungsvorlagen
|
||||||
SET name = ?, beschreibung = ?, aktiv = ?, sort_order = ?
|
SET name = ?, beschreibung = ?, standard_lieferant = ?, aktiv = ?, sort_order = ?
|
||||||
WHERE id = ?',
|
WHERE id = ?',
|
||||||
[
|
[
|
||||||
trim($data['name']),
|
trim($data['name']),
|
||||||
$data['beschreibung'] ?? null,
|
$data['beschreibung'] ?? null,
|
||||||
|
$data['standard_lieferant'] !== '' ? trim($data['standard_lieferant'] ?? '') : null,
|
||||||
isset($data['aktiv']) ? (int)$data['aktiv'] : 1,
|
isset($data['aktiv']) ? (int)$data['aktiv'] : 1,
|
||||||
(int)($data['sort_order'] ?? 0),
|
(int)($data['sort_order'] ?? 0),
|
||||||
$id,
|
$id,
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,11 @@ $bedOptn = ['neq_zero' => 'wenn ≠ 0', 'always' => 'immer'];
|
||||||
<label>Beschreibung</label>
|
<label>Beschreibung</label>
|
||||||
<textarea name="beschreibung" rows="2" class="form-control"><?= $h($vorlage['beschreibung'] ?? '') ?></textarea>
|
<textarea name="beschreibung" rows="2" class="form-control"><?= $h($vorlage['beschreibung'] ?? '') ?></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Standard-Lieferant</label>
|
||||||
|
<input type="text" name="standard_lieferant" value="<?= $h($vorlage['standard_lieferant'] ?? '') ?>"
|
||||||
|
class="form-control" placeholder="Wird im Buchungsdialog vorbelegt">
|
||||||
|
</div>
|
||||||
<div style="display:flex;gap:1.5rem">
|
<div style="display:flex;gap:1.5rem">
|
||||||
<div class="form-group" style="flex:0 0 auto">
|
<div class="form-group" style="flex:0 0 auto">
|
||||||
<label>Reihenfolge</label>
|
<label>Reihenfolge</label>
|
||||||
|
|
|
||||||
|
|
@ -7,13 +7,17 @@ $gruppe1 = array_filter($felder, fn($f) => $f['gruppe_nr'] == 1);
|
||||||
$gruppe2 = array_filter($felder, fn($f) => $f['gruppe_nr'] == 2);
|
$gruppe2 = array_filter($felder, fn($f) => $f['gruppe_nr'] == 2);
|
||||||
$gruppe3 = array_filter($felder, fn($f) => $f['gruppe_nr'] == 3);
|
$gruppe3 = array_filter($felder, fn($f) => $f['gruppe_nr'] == 3);
|
||||||
|
|
||||||
// Vorbelegen aus Beleg-Metadaten
|
// Vorbelegen: Vorlage-Standard-Lieferant hat Vorrang vor Beleg-Lieferant
|
||||||
|
$standardLieferant = trim($vorlage['standard_lieferant'] ?? '');
|
||||||
$vorbelegt = [
|
$vorbelegt = [
|
||||||
'lieferant' => $beleg['supplier_name'] ?? '',
|
'lieferant' => $standardLieferant !== '' ? $standardLieferant : ($beleg['supplier_name'] ?? ''),
|
||||||
'rechnungsnr' => $beleg['rechnungs_nr'] ?? '',
|
'rechnungsnr' => $beleg['rechnungs_nr'] ?? '',
|
||||||
'belegdatum' => $beleg['beleg_datum'] ?? date('Y-m-d'),
|
'belegdatum' => $beleg['beleg_datum'] ?? date('Y-m-d'),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// Zahldatum: Bankbuchungs-Wertstellung, Fallback heute
|
||||||
|
$zahldatum = ($bankTxDatum ?? '') ?: date('Y-m-d');
|
||||||
|
|
||||||
// Vorauswahl Zahlungskonto
|
// Vorauswahl Zahlungskonto
|
||||||
$defaultKontonummer = $hatBankbuchung ? '1200' : '1210';
|
$defaultKontonummer = $hatBankbuchung ? '1200' : '1210';
|
||||||
$defaultKontoId = 0;
|
$defaultKontoId = 0;
|
||||||
|
|
@ -35,7 +39,10 @@ if (!$defaultKontoId && !empty($zahlungskonten)) {
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body" style="padding:1.25rem 1.5rem">
|
<div class="modal-body" style="padding:1.25rem 1.5rem">
|
||||||
<form method="post" action="/belege/<?= $h($belegId) ?>/vorlage/<?= $h($vorlage['id']) ?>/buchen" id="vorlage-buchen-form">
|
<form method="post" action="/belege/<?= $h($belegId) ?>/vorlage/<?= $h($vorlage['id']) ?>/buchen" id="vorlage-buchen-form">
|
||||||
<input type="hidden" name="buchungs_datum" value="<?= $h($vorbelegt['belegdatum'] ?: date('Y-m-d')) ?>">
|
<input type="hidden" name="buchungs_datum" id="vorlage-buchungs-datum" value="<?= $h($zahldatum) ?>">
|
||||||
|
<?php if (!empty($bankTxId)): ?>
|
||||||
|
<input type="hidden" name="bank_tx_id" value="<?= $h($bankTxId) ?>">
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
<?php if (!empty($gruppe1)): ?>
|
<?php if (!empty($gruppe1)): ?>
|
||||||
<!-- Gruppe 1: Metadaten -->
|
<!-- Gruppe 1: Metadaten -->
|
||||||
|
|
@ -50,11 +57,17 @@ if (!$defaultKontoId && !empty($zahlungskonten)) {
|
||||||
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 ($feld['typ'] === 'date'): ?>
|
||||||
<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;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']) ?>"
|
||||||
value="<?= $h($vorbelegt[$feld['feldname']] ?? '') ?>"
|
value="<?= $h($vorbelegt[$feld['feldname']] ?? '') ?>"
|
||||||
style="flex:1;max-width:200px" class="form-control form-control-sm">
|
style="width:150px" class="form-control form-control-sm">
|
||||||
|
<?php if ($feld['feldname'] === 'belegdatum'): ?>
|
||||||
|
<label style="margin:0;font-size:.875rem;white-space:nowrap">Zahldatum</label>
|
||||||
|
<input type="date" id="vorlage-zahldatum"
|
||||||
|
value="<?= $h($zahldatum) ?>"
|
||||||
|
style="width:150px" class="form-control form-control-sm">
|
||||||
|
<?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">
|
||||||
|
|
@ -78,7 +91,7 @@ if (!$defaultKontoId && !empty($zahlungskonten)) {
|
||||||
<th style="text-align:left;padding:.3rem .5rem;font-weight:500">Steuer</th>
|
<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">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">Brutto (€)</th>
|
||||||
<th style="text-align:right;padding:.3rem .5rem;font-weight:500">USt (€)</th>
|
<th style="text-align:right;padding:.3rem .5rem;font-weight:500;color:var(--color-text-muted)">USt (€)</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|
@ -105,11 +118,10 @@ if (!$defaultKontoId && !empty($zahlungskonten)) {
|
||||||
style="text-align:right;width:110px"
|
style="text-align:right;width:110px"
|
||||||
placeholder="0,00">
|
placeholder="0,00">
|
||||||
</td>
|
</td>
|
||||||
<td style="padding:.3rem .5rem">
|
<td style="padding:.3rem .5rem;text-align:right">
|
||||||
<input type="number" name="<?= $h($uKey) ?>" step="0.01" value=""
|
<span class="vorlage-ust-anzeige"
|
||||||
class="form-control form-control-sm vorlage-ust"
|
style="display:inline-block;width:90px;text-align:right;color:var(--color-text-muted);font-size:.875rem">0,00</span>
|
||||||
style="text-align:right;width:90px;background:var(--color-bg-subtle)"
|
<input type="hidden" name="<?= $h($uKey) ?>" class="vorlage-ust" value="">
|
||||||
readonly placeholder="0,00">
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
1-260528
|
1-260530
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue