Feature: Anlagevermögen-Modal beim Buchen auf AV-Konto
This commit is contained in:
parent
f77b228fdb
commit
05a964edbb
2 changed files with 186 additions and 1 deletions
|
|
@ -7,6 +7,7 @@ namespace Steuerkram\Controllers;
|
||||||
use Steuerkram\Models\Beleg;
|
use Steuerkram\Models\Beleg;
|
||||||
use Steuerkram\Models\Booking;
|
use Steuerkram\Models\Booking;
|
||||||
use Steuerkram\Models\Account;
|
use Steuerkram\Models\Account;
|
||||||
|
use Steuerkram\Models\Asset;
|
||||||
use Steuerkram\Models\Supplier;
|
use Steuerkram\Models\Supplier;
|
||||||
use Steuerkram\Config;
|
use Steuerkram\Config;
|
||||||
use Steuerkram\View;
|
use Steuerkram\View;
|
||||||
|
|
@ -198,6 +199,7 @@ class BelegController
|
||||||
$integrity = Beleg::verifyIntegrity((int)$params['id']);
|
$integrity = Beleg::verifyIntegrity((int)$params['id']);
|
||||||
$accounts = Account::all(['aktiv' => 1]);
|
$accounts = Account::all(['aktiv' => 1]);
|
||||||
$suppliers = Supplier::all();
|
$suppliers = Supplier::all();
|
||||||
|
$assets = Asset::all();
|
||||||
|
|
||||||
$svc = new KontierungsvorschlagService();
|
$svc = new KontierungsvorschlagService();
|
||||||
$kiAktiv = $svc->isEnabled();
|
$kiAktiv = $svc->isEnabled();
|
||||||
|
|
@ -214,6 +216,7 @@ class BelegController
|
||||||
'bookings' => $bookings,
|
'bookings' => $bookings,
|
||||||
'integrity' => $integrity,
|
'integrity' => $integrity,
|
||||||
'accounts' => $accounts,
|
'accounts' => $accounts,
|
||||||
|
'assets' => $assets,
|
||||||
'kiAktiv' => $kiAktiv,
|
'kiAktiv' => $kiAktiv,
|
||||||
'kiVorschlag' => $kiVorschlag,
|
'kiVorschlag' => $kiVorschlag,
|
||||||
]),
|
]),
|
||||||
|
|
|
||||||
|
|
@ -380,7 +380,7 @@ $suppliers = $suppliers ?? [];
|
||||||
data-target="kategorie_konto_id" autocomplete="off" style="margin-bottom:.3rem"
|
data-target="kategorie_konto_id" autocomplete="off" style="margin-bottom:.3rem"
|
||||||
value="<?= $h($formData['kategorie_kontonummer'] ?? '') ?>">
|
value="<?= $h($formData['kategorie_kontonummer'] ?? '') ?>">
|
||||||
<select id="kategorie_konto_id" name="kategorie_konto_id" size="6"
|
<select id="kategorie_konto_id" name="kategorie_konto_id" size="6"
|
||||||
onchange="showKontoArt('kategorie_konto_id','kategorie_konto_art',this);updateEigenanteilState();updateBewirtungsState();updateAutoUst()">
|
onchange="showKontoArt('kategorie_konto_id','kategorie_konto_art',this);updateEigenanteilState();updateBewirtungsState();updateAutoUst();checkAnlagevermoegen()">
|
||||||
<option value="">– Konto wählen –</option>
|
<option value="">– Konto wählen –</option>
|
||||||
<?php foreach (['Betriebsausgabe', 'Betriebseinnahme', 'Anlagevermögen', 'Privat', 'Verbindlichkeit'] as $art): ?>
|
<?php foreach (['Betriebsausgabe', 'Betriebseinnahme', 'Anlagevermögen', 'Privat', 'Verbindlichkeit'] as $art): ?>
|
||||||
<?php if (empty($kontenGruppen[$art])) continue; ?>
|
<?php if (empty($kontenGruppen[$art])) continue; ?>
|
||||||
|
|
@ -477,6 +477,9 @@ $suppliers = $suppliers ?? [];
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- hidden: wird bei Auswahl eines bestehenden Anlageguts gesetzt -->
|
||||||
|
<input type="hidden" name="asset_id" id="asset_id_hidden" value="">
|
||||||
|
|
||||||
<button type="submit" class="btn btn-primary">Buchung erfassen</button>
|
<button type="submit" class="btn btn-primary">Buchung erfassen</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -488,6 +491,185 @@ $suppliers = $suppliers ?? [];
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- ── Modal: Anlagevermögen ──────────────────────────────────── -->
|
||||||
|
<div id="modal-anlagevermoegen" class="modal-overlay" role="dialog" aria-modal="true">
|
||||||
|
<div class="modal" style="max-width:640px;width:100%">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h3 class="modal-title">Anlagevermögen buchen</h3>
|
||||||
|
<button type="button" class="modal-close" data-modal-close aria-label="Schließen">×</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p style="margin-bottom:1rem">Das gewählte Konto gehört zum Anlagevermögen. Soll ein neues Anlagegut angelegt oder ein bestehendes ausgewählt werden?</p>
|
||||||
|
|
||||||
|
<div style="display:flex;flex-direction:column;gap:.6rem;margin-bottom:1.25rem">
|
||||||
|
<label style="display:flex;align-items:center;gap:.6rem;cursor:pointer">
|
||||||
|
<input type="radio" name="anlage_wahl" id="anlage_radio_neu" value="neu" checked>
|
||||||
|
<span>Neues Anlagegut anlegen</span>
|
||||||
|
</label>
|
||||||
|
<label style="display:flex;align-items:center;gap:.6rem;cursor:pointer">
|
||||||
|
<input type="radio" name="anlage_wahl" id="anlage_radio_vorhanden" value="vorhanden">
|
||||||
|
<span>Bestehendes Anlagegut auswählen</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Liste bestehender Anlagegüter (nur bei "vorhanden") -->
|
||||||
|
<div id="anlage_liste_wrap" style="display:none">
|
||||||
|
<p id="anlage_liste_leer" class="text-muted text-sm" style="display:none">
|
||||||
|
Für dieses Konto sind noch keine Anlagegüter vorhanden.
|
||||||
|
</p>
|
||||||
|
<div class="table-wrap" id="anlage_liste_table_wrap">
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style="width:3rem">Nr.</th>
|
||||||
|
<th>Wirtschaftsgut</th>
|
||||||
|
<th>Anschaffungsdatum</th>
|
||||||
|
<th class="td-num">Anschaffungskosten</th>
|
||||||
|
<th style="width:2.5rem"></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="anlage_liste_tbody"></tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer" style="display:flex;justify-content:flex-end;gap:.75rem;padding:1rem 1.5rem;border-top:1px solid var(--color-border)">
|
||||||
|
<button type="button" class="btn btn-secondary" data-modal-close>Abbrechen</button>
|
||||||
|
<button type="button" class="btn btn-primary" id="anlage_modal_ok">Weiter</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
// Assets als JSON für JS vorbereiten
|
||||||
|
$assetsJson = json_encode(array_map(fn($a) => [
|
||||||
|
'id' => (int)$a['id'],
|
||||||
|
'lfd_nr' => (int)$a['lfd_nr'],
|
||||||
|
'bezeichnung' => $a['bezeichnung'],
|
||||||
|
'anschaffungsdatum' => $a['anschaffungsdatum'],
|
||||||
|
'anschaffungskosten' => (float)$a['anschaffungskosten'],
|
||||||
|
'account_id' => $a['account_id'] !== null ? (int)$a['account_id'] : null,
|
||||||
|
], $assets ?? []), JSON_HEX_TAG);
|
||||||
|
?>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const _assets = <?= $assetsJson ?>;
|
||||||
|
|
||||||
|
// Formatierung Datum de-DE
|
||||||
|
function _fmtDate(str) {
|
||||||
|
if (!str) return '–';
|
||||||
|
const [y, m, d] = str.split('-');
|
||||||
|
return d + '.' + m + '.' + y;
|
||||||
|
}
|
||||||
|
function _fmtEur(n) {
|
||||||
|
return n.toLocaleString('de-DE', {minimumFractionDigits:2, maximumFractionDigits:2}) + ' €';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wird aufgerufen wenn Kategorie-Konto geändert wird
|
||||||
|
function checkAnlagevermoegen() {
|
||||||
|
const sel = document.getElementById('kategorie_konto_id');
|
||||||
|
if (!sel) return;
|
||||||
|
const opt = sel.options[sel.selectedIndex];
|
||||||
|
if (!opt || opt.value === '') return;
|
||||||
|
|
||||||
|
const art = opt.getAttribute('data-art') || '';
|
||||||
|
if (art !== 'Anlagevermögen') {
|
||||||
|
// Kein AV-Konto – Asset-Auswahl zurücksetzen
|
||||||
|
document.getElementById('asset_id_hidden').value = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// AV-Konto gewählt → Modal öffnen
|
||||||
|
const accountId = parseInt(opt.value, 10);
|
||||||
|
_openAnlageModal(accountId);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _openAnlageModal(accountId) {
|
||||||
|
// Radio auf "neu" zurücksetzen
|
||||||
|
document.getElementById('anlage_radio_neu').checked = true;
|
||||||
|
document.getElementById('anlage_liste_wrap').style.display = 'none';
|
||||||
|
document.getElementById('asset_id_hidden').value = '';
|
||||||
|
|
||||||
|
// Liste für dieses Konto befüllen
|
||||||
|
const passend = _assets.filter(a => a.account_id === accountId);
|
||||||
|
_renderAnlageListe(passend);
|
||||||
|
|
||||||
|
document.getElementById('modal-anlagevermoegen').classList.add('open');
|
||||||
|
}
|
||||||
|
|
||||||
|
function _renderAnlageListe(list) {
|
||||||
|
const tbody = document.getElementById('anlage_liste_tbody');
|
||||||
|
const leer = document.getElementById('anlage_liste_leer');
|
||||||
|
const table = document.getElementById('anlage_liste_table_wrap');
|
||||||
|
|
||||||
|
tbody.innerHTML = '';
|
||||||
|
|
||||||
|
if (list.length === 0) {
|
||||||
|
leer.style.display = '';
|
||||||
|
table.style.display = 'none';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
leer.style.display = 'none';
|
||||||
|
table.style.display = '';
|
||||||
|
|
||||||
|
list.forEach(a => {
|
||||||
|
const tr = document.createElement('tr');
|
||||||
|
tr.style.cursor = 'pointer';
|
||||||
|
tr.dataset.assetId = a.id;
|
||||||
|
tr.innerHTML =
|
||||||
|
'<td class="td-mono text-sm">' + a.lfd_nr + '</td>' +
|
||||||
|
'<td>' + _esc(a.bezeichnung) + '</td>' +
|
||||||
|
'<td class="td-muted text-sm">' + _fmtDate(a.anschaffungsdatum) + '</td>' +
|
||||||
|
'<td class="td-num">' + _fmtEur(a.anschaffungskosten) + '</td>' +
|
||||||
|
'<td><input type="radio" name="anlage_auswahl" value="' + a.id + '"></td>';
|
||||||
|
tbody.appendChild(tr);
|
||||||
|
|
||||||
|
// Klick auf Zeile wählt Radio
|
||||||
|
tr.addEventListener('click', () => {
|
||||||
|
const radio = tr.querySelector('input[type="radio"]');
|
||||||
|
if (radio) radio.checked = true;
|
||||||
|
tbody.querySelectorAll('tr').forEach(r => r.classList.remove('row-selected'));
|
||||||
|
tr.classList.add('row-selected');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function _esc(s) {
|
||||||
|
return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Radio-Wahl: neu / vorhanden
|
||||||
|
document.addEventListener('change', e => {
|
||||||
|
if (e.target.name !== 'anlage_wahl') return;
|
||||||
|
const wrap = document.getElementById('anlage_liste_wrap');
|
||||||
|
wrap.style.display = e.target.value === 'vorhanden' ? '' : 'none';
|
||||||
|
if (e.target.value === 'neu') {
|
||||||
|
document.getElementById('asset_id_hidden').value = '';
|
||||||
|
document.querySelectorAll('input[name="anlage_auswahl"]').forEach(r => r.checked = false);
|
||||||
|
document.querySelectorAll('#anlage_liste_tbody tr').forEach(r => r.classList.remove('row-selected'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// OK-Button
|
||||||
|
document.getElementById('anlage_modal_ok').addEventListener('click', () => {
|
||||||
|
const wahl = document.querySelector('input[name="anlage_wahl"]:checked')?.value;
|
||||||
|
|
||||||
|
if (wahl === 'vorhanden') {
|
||||||
|
const gewählt = document.querySelector('input[name="anlage_auswahl"]:checked');
|
||||||
|
if (!gewählt) {
|
||||||
|
alert('Bitte ein Anlagegut aus der Liste auswählen.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
document.getElementById('asset_id_hidden').value = gewählt.value;
|
||||||
|
} else {
|
||||||
|
// "neu" – asset_id bleibt leer, Backend legt neues Gut an
|
||||||
|
document.getElementById('asset_id_hidden').value = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('modal-anlagevermoegen').classList.remove('open');
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
function filterKategorieKonten(buchungstyp) {
|
function filterKategorieKonten(buchungstyp) {
|
||||||
const select = document.getElementById('kategorie_konto_id');
|
const select = document.getElementById('kategorie_konto_id');
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue