Fix #21: Automatische USt-Vorauswahl und Sperrung bei Konten mit Steuercode

This commit is contained in:
fds2610 2026-04-01 00:16:38 +02:00
parent 7a893ceab8
commit 02eedcd392
2 changed files with 119 additions and 2 deletions

View file

@ -339,7 +339,8 @@ $suppliers = $suppliers ?? [];
</div> </div>
<div class="form-group"> <div class="form-group">
<label>USt-Satz (%)</label> <label>USt-Satz (%)</label>
<select name="ust_satz" id="ust_satz_select" onchange="updateSteuerartVisibility();updateNettoAnzeige()"> <input type="hidden" name="ust_satz" id="ust_satz_hidden" value="<?= $h($formData['ust_satz'] ?? '19') ?>">
<select id="ust_satz_select" onchange="updateSteuerartVisibility();updateNettoAnzeige();document.getElementById('ust_satz_hidden').value=this.value">
<?php foreach (['0','7','19'] as $s): ?> <?php foreach (['0','7','19'] as $s): ?>
<option value="<?= $s ?>"<?= ($formData['ust_satz'] ?? '19') === $s ? ' selected' : '' ?>><?= $s ?> %</option> <option value="<?= $s ?>"<?= ($formData['ust_satz'] ?? '19') === $s ? ' selected' : '' ?>><?= $s ?> %</option>
<?php endforeach; ?> <?php endforeach; ?>
@ -377,7 +378,7 @@ $suppliers = $suppliers ?? [];
<input type="search" placeholder="Kontonr. oder Bezeichnung filtern…" class="konto-filter" <input type="search" placeholder="Kontonr. oder Bezeichnung filtern…" class="konto-filter"
data-target="kategorie_konto_id" autocomplete="off" style="margin-bottom:.3rem"> data-target="kategorie_konto_id" autocomplete="off" style="margin-bottom:.3rem">
<select id="kategorie_konto_id" name="kategorie_konto_id" required size="6" <select id="kategorie_konto_id" name="kategorie_konto_id" required size="6"
onchange="showKontoArt('kategorie_konto_id','kategorie_konto_art',this);updateEigenanteilState();updateBewirtungsState()"> onchange="showKontoArt('kategorie_konto_id','kategorie_konto_art',this);updateEigenanteilState();updateBewirtungsState();updateAutoUst()">
<option value="">– Konto wählen –</option> <option value="">– Konto wählen –</option>
<?php foreach (['Betriebsausgabe', 'Betriebseinnahme', 'Privat'] as $art): ?> <?php foreach (['Betriebsausgabe', 'Betriebseinnahme', 'Privat'] as $art): ?>
<?php if (empty($kontenGruppen[$art])) continue; ?> <?php if (empty($kontenGruppen[$art])) continue; ?>
@ -386,6 +387,7 @@ $suppliers = $suppliers ?? [];
<option value="<?= $h($acc['id']) ?>" <option value="<?= $h($acc['id']) ?>"
data-art="<?= $h($acc['kontenart']) ?>" data-art="<?= $h($acc['kontenart']) ?>"
data-kontonummer="<?= $h($acc['kontonummer']) ?>" data-kontonummer="<?= $h($acc['kontonummer']) ?>"
data-steuercode="<?= $h($acc['steuercode'] ?? '') ?>"
data-search="<?= $h($acc['kontonummer'] . ' ' . $acc['bezeichnung'] . ' ' . $acc['kontenart']) ?>"> data-search="<?= $h($acc['kontonummer'] . ' ' . $acc['bezeichnung'] . ' ' . $acc['kontenart']) ?>">
<?= $h($acc['kontonummer']) ?> – <?= $h($acc['bezeichnung']) ?> <?= $h($acc['kontonummer']) ?> – <?= $h($acc['bezeichnung']) ?>
</option> </option>
@ -505,6 +507,32 @@ function filterKategorieKonten(buchungstyp) {
} }
updateEigenanteilState(); updateEigenanteilState();
} }
function updateAutoUst() {
const kategorieEl = document.getElementById('kategorie_konto_id');
const ustSatzEl = document.getElementById('ust_satz_select');
if (!kategorieEl || !ustSatzEl) return;
const selectedOpt = kategorieEl.options[kategorieEl.selectedIndex];
const steuercode = selectedOpt ? (selectedOpt.getAttribute('data-steuercode') || '') : '';
const ustMap = { 'VSt19': '19', 'USt19': '19', 'VSt7': '7', 'USt7': '7' };
const autoUst = ustMap[steuercode] || null;
const hiddenEl = document.getElementById('ust_satz_hidden');
if (autoUst !== null) {
ustSatzEl.value = autoUst;
ustSatzEl.disabled = true;
ustSatzEl.title = 'Automatisch durch Steuercode des Kontos gesetzt';
if (hiddenEl) hiddenEl.value = autoUst;
} else {
ustSatzEl.disabled = false;
ustSatzEl.title = '';
if (hiddenEl) hiddenEl.value = ustSatzEl.value;
}
updateSteuerartVisibility();
updateNettoAnzeige();
}
function updateBewirtungsState() { function updateBewirtungsState() {
const kategorieEl = document.getElementById('kategorie_konto_id'); const kategorieEl = document.getElementById('kategorie_konto_id');
const felder = document.getElementById('bewirtungsfelder'); const felder = document.getElementById('bewirtungsfelder');
@ -553,6 +581,7 @@ document.addEventListener('DOMContentLoaded', function() {
updateSteuerartVisibility(); updateSteuerartVisibility();
updateEigenanteilState(); updateEigenanteilState();
updateBewirtungsState(); updateBewirtungsState();
updateAutoUst();
updateNettoAnzeige(); updateNettoAnzeige();
// Leeres Lieferantenfeld fokussieren wenn kein KI-Block aktiv // Leeres Lieferantenfeld fokussieren wenn kein KI-Block aktiv

View file

@ -0,0 +1,88 @@
<?php
declare(strict_types=1);
/**
* Test 17: Automatische USt-Satz-Vorauswahl bei Konten mit Steuercode.
*
* Stellt sicher, dass:
* - Konten mit steuercode=VSt19/USt19 den ust_satz=19 tragen (serverseitige Absicherung)
* - Konten mit steuercode=VSt7/USt7 den ust_satz=7 tragen
* - Das steuercode-Feld in Account::all() korrekt geliefert wird
* - Buchungen auf Steuercode-Konten mit dem korrekten USt-Satz gespeichert werden
*/
class AutomatikkontenUstTest extends TestCase
{
protected string $testName = 'Test 17: Automatikkonten USt-Satz (Steuercode)';
public function run(): void
{
// ── Steuercode-Felder in der Datenbank prüfen ─────────────────
$kto4650 = \Steuerkram\Database::fetchOne(
"SELECT steuercode FROM accounts WHERE kontonummer = '4650'"
);
$this->assertEquals('VSt19', $kto4650['steuercode'] ?? null, 'Konto 4650 hat steuercode=VSt19');
$kto4651 = \Steuerkram\Database::fetchOne(
"SELECT steuercode FROM accounts WHERE kontonummer = '4651'"
);
$this->assertEquals('VSt7', $kto4651['steuercode'] ?? null, 'Konto 4651 hat steuercode=VSt7 (oder null)');
$kto8400 = \Steuerkram\Database::fetchOne(
"SELECT steuercode FROM accounts WHERE kontonummer = '8400'"
);
$this->assertEquals('USt19', $kto8400['steuercode'] ?? null, 'Konto 8400 hat steuercode=USt19');
// ── USt-Mapping prüfen (Frontend-Logik serverseitig validiert) ─
$mapping = ['VSt19' => 19, 'USt19' => 19, 'VSt7' => 7, 'USt7' => 7];
$kontenMitSteuercode = \Steuerkram\Database::fetchAll(
"SELECT kontonummer, steuercode FROM accounts WHERE steuercode IS NOT NULL LIMIT 20"
);
$this->assertNotEmpty($kontenMitSteuercode, 'Konten mit Steuercode vorhanden');
foreach ($kontenMitSteuercode as $kto) {
$erwarteterUst = $mapping[$kto['steuercode']] ?? null;
$this->assert(
$erwarteterUst !== null,
'Steuercode ' . $kto['steuercode'] . ' auf Konto ' . $kto['kontonummer'] . ' ist im Mapping'
);
}
// ── Buchung mit VSt19-Konto mit 19% USt ───────────────────────
$datum = date('Y-m-d');
$bankId = $this->findAccountId('1200');
$kto4650Id = $this->findAccountId('4650');
if (!$bankId || !$kto4650Id) {
$this->assert(false, 'Konten 1200 und 4650 müssen vorhanden sein');
return;
}
$belegId = \Steuerkram\Models\Beleg::create([
'beleg_nr' => 'TEST-AUTOUST-' . uniqid(),
'filename' => 'test.pdf',
'storage_path' => '',
'mime_type' => 'application/pdf',
'file_hash' => hash('sha256', 'autoust' . uniqid()),
'beleg_datum' => $datum,
'eingangs_datum' => $datum,
'belegtyp' => 'Eingangsrechnung',
]);
$bookingId = \Steuerkram\Models\Booking::create([
'beleg_id' => $belegId,
'buchungs_datum' => $datum,
'buchungstyp' => 'Ausgabe',
'kategorie_konto_id' => $kto4650Id,
'zahlungskonto_id' => $bankId,
'betrag_netto' => 100.0,
'ust_satz' => 19.0, // sollte durch Steuercode VSt19 vorgegeben sein
'buchungstext' => 'Test Automatikkonto VSt19',
]);
$booking = \Steuerkram\Models\Booking::find($bookingId);
$this->assertEquals(19.0, (float)$booking['ust_satz'], 'Buchung auf VSt19-Konto hat ust_satz=19');
$this->assertEquals(19.0, (float)$booking['betrag_ust'], 'Buchung auf VSt19-Konto hat betrag_ust=19');
}
}