Feature: Bank-Zuordnung öffnet Buchungsmaske mit vorausgefüllten Feldern
This commit is contained in:
parent
b12f4c9b2b
commit
e4a6995a31
4 changed files with 125 additions and 16 deletions
|
|
@ -11,6 +11,7 @@ use Steuerkram\Models\Asset;
|
||||||
use Steuerkram\Models\Supplier;
|
use Steuerkram\Models\Supplier;
|
||||||
use Steuerkram\View;
|
use Steuerkram\View;
|
||||||
use Steuerkram\Services\ImageCropService;
|
use Steuerkram\Services\ImageCropService;
|
||||||
|
use Steuerkram\Models\BankTransaction;
|
||||||
use Steuerkram\Services\KontierungsvorschlagService;
|
use Steuerkram\Services\KontierungsvorschlagService;
|
||||||
|
|
||||||
class BelegController
|
class BelegController
|
||||||
|
|
@ -205,22 +206,60 @@ class BelegController
|
||||||
$kiVorschlag = $beleg['ki_vorschlag'] !== null
|
$kiVorschlag = $beleg['ki_vorschlag'] !== null
|
||||||
? json_decode($beleg['ki_vorschlag'], true)
|
? json_decode($beleg['ki_vorschlag'], true)
|
||||||
: null;
|
: null;
|
||||||
// KI-Sektion anzeigen wenn Ergebnis vorhanden ODER LLM gerade verfügbar
|
|
||||||
$kiSektion = $kiAktiv || $kiVorschlag !== null;
|
$kiSektion = $kiAktiv || $kiVorschlag !== null;
|
||||||
|
|
||||||
|
// Bank-Kontext: Vorausfüllen aus Bankbuchung
|
||||||
|
$bankTx = null;
|
||||||
|
$bankTxSupplier = '';
|
||||||
|
$bankTxBrutto = 0.0;
|
||||||
|
$bankFormData = [];
|
||||||
|
|
||||||
|
if (!empty($_GET['bank_tx'])) {
|
||||||
|
$bankTx = BankTransaction::find((int)$_GET['bank_tx']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($bankTx) {
|
||||||
|
// Auftraggeber extrahieren: "Auftraggeber: X Buchungstext" → X
|
||||||
|
if (preg_match('/Auftraggeber:\s*(.*?)(?:\s*Buchungstext|\s*$)/si', $bankTx['buchungstext'], $m)) {
|
||||||
|
$bankTxSupplier = trim($m[1]);
|
||||||
|
}
|
||||||
|
$bankTxBrutto = abs((float)$bankTx['wert']);
|
||||||
|
$netto = round($bankTxBrutto / 1.19, 2);
|
||||||
|
|
||||||
|
$bankFormData = [
|
||||||
|
'buchungs_datum' => $bankTx['wertstellung'],
|
||||||
|
'betrag_netto' => number_format($netto, 2, '.', ''),
|
||||||
|
'zahlungs_kontonummer'=> '1200',
|
||||||
|
];
|
||||||
|
|
||||||
|
// Lieferant am Beleg setzen, wenn noch keiner hinterlegt ist
|
||||||
|
if (empty($beleg['supplier_name']) && $bankTxSupplier !== '') {
|
||||||
|
$supplierId = \Steuerkram\Models\Supplier::findOrCreate($bankTxSupplier);
|
||||||
|
\Steuerkram\Database::execute(
|
||||||
|
'UPDATE belege SET supplier_id=?, updated_at=NOW() WHERE id=?',
|
||||||
|
[$supplierId, (int)$params['id']]
|
||||||
|
);
|
||||||
|
$beleg['supplier_name'] = $bankTxSupplier;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$view = new View();
|
$view = new View();
|
||||||
$view->render('layout/base.php', [
|
$view->render('layout/base.php', [
|
||||||
'pageTitle' => 'Beleg ' . $beleg['beleg_nr'],
|
'pageTitle' => 'Beleg ' . $beleg['beleg_nr'],
|
||||||
'content' => $this->renderView('belege/detail', [
|
'content' => $this->renderView('belege/detail', [
|
||||||
'beleg' => $beleg,
|
'beleg' => $beleg,
|
||||||
'suppliers' => $suppliers,
|
'suppliers' => $suppliers,
|
||||||
'bookings' => $bookings,
|
'bookings' => $bookings,
|
||||||
'integrity' => $integrity,
|
'integrity' => $integrity,
|
||||||
'accounts' => $accounts,
|
'accounts' => $accounts,
|
||||||
'assets' => $assets,
|
'assets' => $assets,
|
||||||
'kiAktiv' => $kiAktiv,
|
'kiAktiv' => $kiAktiv,
|
||||||
'kiSektion' => $kiSektion,
|
'kiSektion' => $kiSektion,
|
||||||
'kiVorschlag' => $kiVorschlag,
|
'kiVorschlag' => $kiVorschlag,
|
||||||
|
'bankTx' => $bankTx,
|
||||||
|
'bankTxSupplier' => $bankTxSupplier,
|
||||||
|
'bankTxBrutto' => $bankTxBrutto,
|
||||||
|
'formData' => $bankFormData,
|
||||||
]),
|
]),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ namespace Steuerkram\Controllers;
|
||||||
use Steuerkram\Models\Booking;
|
use Steuerkram\Models\Booking;
|
||||||
use Steuerkram\Models\Account;
|
use Steuerkram\Models\Account;
|
||||||
use Steuerkram\Models\Beleg;
|
use Steuerkram\Models\Beleg;
|
||||||
|
use Steuerkram\Models\BankTransaction;
|
||||||
use Steuerkram\Models\FiscalYear;
|
use Steuerkram\Models\FiscalYear;
|
||||||
use Steuerkram\Models\Setting;
|
use Steuerkram\Models\Setting;
|
||||||
use Steuerkram\View;
|
use Steuerkram\View;
|
||||||
|
|
@ -182,8 +183,16 @@ class BookingController
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Bankbuchung verknüpfen wenn aus Bank-Kontext
|
||||||
|
$bankTxId = (int)($data['bank_tx_id'] ?? 0);
|
||||||
|
if ($bankTxId > 0) {
|
||||||
|
BankTransaction::linkBeleg($bankTxId, $belegId);
|
||||||
|
}
|
||||||
|
|
||||||
if ($newAssetId !== null) {
|
if ($newAssetId !== null) {
|
||||||
View::redirect("/assets/{$newAssetId}/edit", 'Buchung erfasst. Bitte Wirtschaftsgut-Details ergänzen.');
|
View::redirect("/assets/{$newAssetId}/edit", 'Buchung erfasst. Bitte Wirtschaftsgut-Details ergänzen.');
|
||||||
|
} elseif ($bankTxId > 0) {
|
||||||
|
View::redirect('/bank', 'Buchung erfasst und Bankbuchung verknüpft.');
|
||||||
} else {
|
} else {
|
||||||
View::redirect("/belege/{$belegId}", 'Buchung erfasst.');
|
View::redirect("/belege/{$belegId}", 'Buchung erfasst.');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -123,10 +123,8 @@ $vorschauHoehe = (int)($settings['beleg_vorschau_hoehe'] ?? 300);
|
||||||
<?= $b['betrag_brutto'] ? $fmt($b['betrag_brutto']) : '<span class="text-muted">–</span>' ?>
|
<?= $b['betrag_brutto'] ? $fmt($b['betrag_brutto']) : '<span class="text-muted">–</span>' ?>
|
||||||
</td>
|
</td>
|
||||||
<td onclick="event.stopPropagation()">
|
<td onclick="event.stopPropagation()">
|
||||||
<form method="post" action="/bank/<?= (int)$transaction['id'] ?>/link" style="display:inline">
|
<a href="/belege/<?= (int)$b['id'] ?>?bank_tx=<?= (int)$transaction['id'] ?>"
|
||||||
<input type="hidden" name="beleg_id" value="<?= (int)$b['id'] ?>">
|
class="btn btn-primary btn-sm">Verknüpfen</a>
|
||||||
<button type="submit" class="btn btn-primary btn-sm">Verknüpfen</button>
|
|
||||||
</form>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
|
|
|
||||||
|
|
@ -147,7 +147,7 @@ $suppliers = $suppliers ?? [];
|
||||||
<div class="form-group" style="margin:0;min-width:180px">
|
<div class="form-group" style="margin:0;min-width:180px">
|
||||||
<label style="font-size:.8rem">Lieferant</label>
|
<label style="font-size:.8rem">Lieferant</label>
|
||||||
<input type="text" name="supplier_name" id="supplier_name_input"
|
<input type="text" name="supplier_name" id="supplier_name_input"
|
||||||
value="<?= $h($beleg['supplier_name'] ?? '') ?>"
|
value="<?= $h($beleg['supplier_name'] ?? ($bankTxSupplier ?? '')) ?>"
|
||||||
placeholder="Name eingeben oder wählen…"
|
placeholder="Name eingeben oder wählen…"
|
||||||
list="supplier-datalist"
|
list="supplier-datalist"
|
||||||
autocomplete="off">
|
autocomplete="off">
|
||||||
|
|
@ -295,8 +295,25 @@ $suppliers = $suppliers ?? [];
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- BankBuchung-Badge (ersetzt KI-Badge wenn aus Bank-Kontext) -->
|
||||||
|
<?php if (!$beleg['locked'] && ($bankTx ?? null)): ?>
|
||||||
|
<div class="card mb-2" style="border-left:4px solid var(--color-primary,#3b82f6)">
|
||||||
|
<div class="card-body" style="padding:.75rem 1.25rem">
|
||||||
|
<div style="display:flex;align-items:center;gap:.75rem;flex-wrap:wrap">
|
||||||
|
<span class="badge badge-blue" style="font-size:.8rem">Bankbuchung</span>
|
||||||
|
<span class="td-mono text-sm"><?= View::formatDate($bankTx['wertstellung']) ?></span>
|
||||||
|
<span class="text-sm"><?= $h($bankTx['vorgang']) ?></span>
|
||||||
|
<span class="text-sm" style="flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap"
|
||||||
|
title="<?= $h($bankTx['buchungstext']) ?>"><?= $h($bankTx['buchungstext']) ?></span>
|
||||||
|
<?php $bwert = (float)$bankTx['wert']; ?>
|
||||||
|
<strong style="color:<?= $bwert >= 0 ? 'var(--color-success)' : 'var(--color-danger)' ?>;white-space:nowrap">
|
||||||
|
<?= $bwert >= 0 ? '+' : '' ?><?= number_format($bwert, 2, ',', '.') ?> €
|
||||||
|
</strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php elseif (!$beleg['locked'] && ($kiSektion ?? false)): ?>
|
||||||
<!-- KI-Kontierungsvorschlag -->
|
<!-- KI-Kontierungsvorschlag -->
|
||||||
<?php if (!$beleg['locked'] && ($kiSektion ?? false)): ?>
|
|
||||||
<div id="ki-badge" style="margin-bottom:1rem"></div>
|
<div id="ki-badge" style="margin-bottom:1rem"></div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
|
|
@ -336,6 +353,9 @@ $suppliers = $suppliers ?? [];
|
||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<form method="post" action="/belege/<?= $h($beleg['id']) ?>/bookings">
|
<form method="post" action="/belege/<?= $h($beleg['id']) ?>/bookings">
|
||||||
|
<?php if ($bankTx ?? null): ?>
|
||||||
|
<input type="hidden" name="bank_tx_id" value="<?= (int)$bankTx['id'] ?>">
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
<!-- Buchungstyp -->
|
<!-- Buchungstyp -->
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
|
@ -508,6 +528,49 @@ $suppliers = $suppliers ?? [];
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<?php if ($bankTx ?? null): ?>
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
const brutto = <?= (float)($bankTxBrutto ?? 0) ?>;
|
||||||
|
let manualNetto = false;
|
||||||
|
|
||||||
|
const nettoInput = document.getElementById('betrag_netto_input');
|
||||||
|
const ustSelect = document.getElementById('ust_satz_select');
|
||||||
|
|
||||||
|
// Netto aus Brutto bei USt-Wechsel neu berechnen (solange nicht manuell geändert)
|
||||||
|
function recalcNetto() {
|
||||||
|
if (manualNetto || !brutto) return;
|
||||||
|
const ust = parseFloat(ustSelect.value) / 100;
|
||||||
|
const netto = Math.round(brutto / (1 + ust) * 100) / 100;
|
||||||
|
nettoInput.value = netto.toFixed(2);
|
||||||
|
if (typeof updateNettoAnzeige === 'function') updateNettoAnzeige();
|
||||||
|
}
|
||||||
|
|
||||||
|
nettoInput.addEventListener('input', function () { manualNetto = true; });
|
||||||
|
ustSelect.addEventListener('change', recalcNetto);
|
||||||
|
|
||||||
|
// Zahlungskonto 1200 vorauswählen
|
||||||
|
const zkSelect = document.getElementById('zahlungskonto_id');
|
||||||
|
if (zkSelect) {
|
||||||
|
for (const opt of zkSelect.options) {
|
||||||
|
if ((opt.dataset.search || '').startsWith('1200 ')) {
|
||||||
|
opt.selected = true;
|
||||||
|
zkSelect.dispatchEvent(new Event('change'));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Meta-Formular mit Lieferant aufklappen wenn noch nicht gespeichert
|
||||||
|
<?php if (!empty($bankTxSupplier) && empty($beleg['supplier_name'])): ?>
|
||||||
|
const metaForm = document.getElementById('beleg-meta-form');
|
||||||
|
if (metaForm) metaForm.style.display = 'block';
|
||||||
|
<?php endif; ?>
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<div class="flash flash-info">Dieser Beleg ist gesperrt (GoBD). Keine weiteren Buchungen möglich.</div>
|
<div class="flash flash-info">Dieser Beleg ist gesperrt (GoBD). Keine weiteren Buchungen möglich.</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue