Feature: UStVA Kz 83 = ust81 + ust89 - Kz66 - Kz61 (nicht mehr kontobasiert)

This commit is contained in:
fds2610-bot 2026-05-14 19:22:42 +02:00
parent 6c4faec3bd
commit b28ac191fa
4 changed files with 27 additions and 20 deletions

View file

@ -2,6 +2,11 @@
--- ---
## 2026-05-14 – Feature: UStVA Kz 83 = ust81 + ust89 − Kz66 − Kz61
- `UstvaCalculator`: Kz 83 wird nun als `ust81 + ust89 − Kz66 − Kz61` berechnet (nicht mehr kontobasiert via ustva_kz='83')
- Test 29: angepasst (7%-Buchung auf korrektes Konto 8300; delta83-Erwartung aktualisiert)
## 2026-05-14 – Feature: UStVA Kz 61 aus ust89 abgeleitet (ReverseCharge-Symmetrie) ## 2026-05-14 – Feature: UStVA Kz 61 aus ust89 abgeleitet (ReverseCharge-Symmetrie)
- `UstvaCalculator`: Kz 61 = ust89 (nicht mehr kontobasiert via Konto 1574) – fachlich korrekt, da VSt ig. Erwerb = USt ig. Erwerb - `UstvaCalculator`: Kz 61 = ust89 (nicht mehr kontobasiert via Konto 1574) – fachlich korrekt, da VSt ig. Erwerb = USt ig. Erwerb

View file

@ -51,7 +51,7 @@ Diese Berechnung wird ausschließlich für **monatliche** Voranmeldungen unterst
| **89** | Steuerpfl. innergemeinschaftliche Erwerbe 19 % | Steuerart | `floor(SUM(betrag_netto))` WHERE `steuerart='ig_erwerb_19'` – **auf volle Euro abgerundet (floor)** | | **89** | Steuerpfl. innergemeinschaftliche Erwerbe 19 % | Steuerart | `floor(SUM(betrag_netto))` WHERE `steuerart='ig_erwerb_19'` – **auf volle Euro abgerundet (floor)** |
| **66** | Gesamte Vorsteuerbeträge | Konto | `SUM(betrag_netto)` WHERE `ustva_kz='66'` (Konten 1571, 1576) | | **66** | Gesamte Vorsteuerbeträge | Konto | `SUM(betrag_netto)` WHERE `ustva_kz='66'` (Konten 1571, 1576) |
| **61** | Vorsteuerbeträge i.g. Erwerb | abgeleitet | `ust89` (= `round(Kz 89 × 0,19, 2)`) – ReverseCharge-Symmetrie: VSt ig. Erwerb = USt ig. Erwerb | | **61** | Vorsteuerbeträge i.g. Erwerb | abgeleitet | `ust89` (= `round(Kz 89 × 0,19, 2)`) – ReverseCharge-Symmetrie: VSt ig. Erwerb = USt ig. Erwerb |
| **83** | Vorauszahlung / Überschuss | abgeleitet | `(Summe ustva_kz='83') − Kz 66 − Kz 61` | | **83** | Vorauszahlung / Überschuss | abgeleitet | `ust81 + ust89 − Kz 66 − Kz 61` |
Zusätzlich werden zwei **reine Anzeigefelder** berechnet (erscheinen im Bericht, aber **nicht** im XML): Zusätzlich werden zwei **reine Anzeigefelder** berechnet (erscheinen im Bericht, aber **nicht** im XML):

View file

@ -20,7 +20,7 @@ use Steuerkram\Database;
* Abgeleitete Kennzahlen: * Abgeleitete Kennzahlen:
* ust89 = round(Kz 89 × 0,19, 2) (abzuführende USt aus Kz 89) * ust89 = round(Kz 89 × 0,19, 2) (abzuführende USt aus Kz 89)
* Kz 61 = ust89 (VSt ig. Erwerb = USt ig. Erwerb, ReverseCharge-Symmetrie) * Kz 61 = ust89 (VSt ig. Erwerb = USt ig. Erwerb, ReverseCharge-Symmetrie)
* Kz 83 = (Summe ustva_kz='83') − Kz 66 − Kz 61 (Vorauszahlung / Überschuss) * Kz 83 = ust81 + ust89 − Kz 66 − Kz 61 (Vorauszahlung / Überschuss)
* *
* Nur-Anzeige (kein XML-Export): * Nur-Anzeige (kein XML-Export):
* ust81 = round(Kz 81 × 0,19, 2) (abzuführende USt aus Kz 81) * ust81 = round(Kz 81 × 0,19, 2) (abzuführende USt aus Kz 81)
@ -84,7 +84,7 @@ class UstvaCalculator
$ust89 = round($kz89 * 0.19, 2); $ust89 = round($kz89 * 0.19, 2);
// Kz 61 (VSt ig. Erwerb) = ust89: ReverseCharge ist immer symmetrisch zur USt aus Kz 89 // Kz 61 (VSt ig. Erwerb) = ust89: ReverseCharge ist immer symmetrisch zur USt aus Kz 89
$kz61 = $ust89; $kz61 = $ust89;
$kz83 = $n('83') - $kz66 - $kz61; $kz83 = $ust81 + $ust89 - $kz66 - $kz61;
return [ return [
'81' => ['name' => 'Steuerpflichtige Umsätze zum Steuersatz von 19 %', 'betrag' => $kz81, 'steuer' => null, 'xml' => true, 'ganze_euro' => true], '81' => ['name' => 'Steuerpflichtige Umsätze zum Steuersatz von 19 %', 'betrag' => $kz81, 'steuer' => null, 'xml' => true, 'ganze_euro' => true],

View file

@ -6,8 +6,8 @@ declare(strict_types=1);
* Test 29: USt-Buchungszeilen für Ausgangsrechnungen. * Test 29: USt-Buchungszeilen für Ausgangsrechnungen.
* *
* Prüft, dass Einnahme-Buchungen mit USt 7 % / 19 % automatisch eine * Prüft, dass Einnahme-Buchungen mit USt 7 % / 19 % automatisch eine
* USt-Buchungszeile auf 1771 / 1776 erhalten, die UStVA Kz 83/84 daraus * USt-Buchungszeile auf 1771 / 1776 erhalten und ein Storno die USt-Zeilen
* kontobasiert ableitet und ein Storno die USt-Zeilen mitaufhebt. * mitaufhebt. Kz 83 = ust81 + ust89 − Kz66 − Kz61 (abgeleitet, nicht kontobasiert).
*/ */
class UmsatzsteuerkontenTest extends TestCase class UmsatzsteuerkontenTest extends TestCase
{ {
@ -19,20 +19,21 @@ class UmsatzsteuerkontenTest extends TestCase
$month = (int)date('n'); $month = (int)date('n');
$datum = date('Y-m-d'); $datum = date('Y-m-d');
$zahlungsId = $this->findAccountId('1200'); $zahlungsId = $this->findAccountId('1200');
$einnahmeId = $this->findAccountId('8400'); $einnahmeId = $this->findAccountId('8400'); // Erlöse 19 % USt (ustva_kz='81')
$konto1776 = \Steuerkram\Database::fetchOne( $einnahme7Id = $this->findAccountId('8300'); // Erlöse 7 % USt (ustva_kz=NULL)
$konto1776 = \Steuerkram\Database::fetchOne(
"SELECT id FROM accounts WHERE kontonummer = '1776' AND aktiv = 1" "SELECT id FROM accounts WHERE kontonummer = '1776' AND aktiv = 1"
); );
$konto1771 = \Steuerkram\Database::fetchOne( $konto1771 = \Steuerkram\Database::fetchOne(
"SELECT id FROM accounts WHERE kontonummer = '1771' AND aktiv = 1" "SELECT id FROM accounts WHERE kontonummer = '1771' AND aktiv = 1"
); );
if (!$zahlungsId || !$einnahmeId || !$konto1776 || !$konto1771) { if (!$zahlungsId || !$einnahmeId || !$einnahme7Id || !$konto1776 || !$konto1771) {
$this->assert(false, 'Benötigte Konten nicht gefunden (1200, 8400, 1771, 1776)'); $this->assert(false, 'Benötigte Konten nicht gefunden (1200, 8300, 8400, 1771, 1776)');
return; return;
} }
$this->assert(true, 'Konten 1200, 8400, 1771, 1776 vorhanden'); $this->assert(true, 'Konten 1200, 8300, 8400, 1771, 1776 vorhanden');
// ── UStVA-Stand vor den Testbuchungen ───────────────────────── // ── UStVA-Stand vor den Testbuchungen ─────────────────────────
$calc = new \Steuerkram\Services\UstvaCalculator(); $calc = new \Steuerkram\Services\UstvaCalculator();
@ -73,12 +74,12 @@ class UmsatzsteuerkontenTest extends TestCase
$this->assertEquals(95.00, round((float)($ustRow19['betrag_netto'] ?? 0), 2), 'USt betrag_netto = 95'); $this->assertEquals(95.00, round((float)($ustRow19['betrag_netto'] ?? 0), 2), 'USt betrag_netto = 95');
$this->assertEquals(0.00, round((float)($ustRow19['betrag_ust'] ?? 0), 2), 'USt betrag_ust = 0'); $this->assertEquals(0.00, round((float)($ustRow19['betrag_ust'] ?? 0), 2), 'USt betrag_ust = 0');
// ── Einnahme-Buchung 7 % (Netto=200, USt=14) ────────────────── // ── Einnahme-Buchung 7 % (Netto=200, USt=14) auf korrektem 7%-Konto 8300
$bookingId7 = \Steuerkram\Models\Booking::create([ $bookingId7 = \Steuerkram\Models\Booking::create([
'beleg_id' => $belegId, 'beleg_id' => $belegId,
'buchungs_datum' => $datum, 'buchungs_datum' => $datum,
'buchungstyp' => 'Einnahme', 'buchungstyp' => 'Einnahme',
'kategorie_konto_id' => $einnahmeId, 'kategorie_konto_id' => $einnahme7Id,
'zahlungskonto_id' => $zahlungsId, 'zahlungskonto_id' => $zahlungsId,
'betrag_netto' => 200.00, 'betrag_netto' => 200.00,
'ust_satz' => 7.0, 'ust_satz' => 7.0,
@ -95,10 +96,10 @@ class UmsatzsteuerkontenTest extends TestCase
$this->assertEquals('1771', $ustRow7['kontonummer'] ?? '', 'USt-Buchungszeile auf Konto 1771'); $this->assertEquals('1771', $ustRow7['kontonummer'] ?? '', 'USt-Buchungszeile auf Konto 1771');
$this->assertEquals(14.00, round((float)($ustRow7['betrag_netto'] ?? 0), 2), 'USt betrag_netto = 14'); $this->assertEquals(14.00, round((float)($ustRow7['betrag_netto'] ?? 0), 2), 'USt betrag_netto = 14');
// ── UStVA: Kz 83 (Vorauszahlung) aus USt-Konten ────────────── // ── UStVA: Kz 83 = ust81 + ust89 − Kz66 − Kz61 ─────────────
// Kz 83 = (Summe USt-Konten 1771+1774+1776) - Kz66 - Kz61 // Nach 19%-Einnahme (500 netto → floor(500+X)*0,19 steigt um 95) und
// Nach 19%-Einnahme (+95 auf 1776) und 7%-Einnahme (+14 auf 1771): // 7%-Einnahme auf 8300 (ustva_kz=NULL → kein Einfluss auf Kz 81/83):
// Kz83 stieg um mindestens 95 € (19%-Anteil) // Kz 83 stieg um mindestens 95 €
$kzMit = $calc->calculate($year, $month); $kzMit = $calc->calculate($year, $month);
$this->assert( $this->assert(
@ -134,9 +135,10 @@ class UmsatzsteuerkontenTest extends TestCase
); );
$this->assert(!empty($ustOriginal['reversed_by']), 'Original-USt-Buchungszeile hat reversed_by gesetzt'); $this->assert(!empty($ustOriginal['reversed_by']), 'Original-USt-Buchungszeile hat reversed_by gesetzt');
// Kz 83 nach Storno der 19%-Buchung: nur noch 7%-USt (14 €) verbleibt // Kz 83 nach Storno der 19%-Buchung: kz81 zurück auf Baseline → delta83 = 0
// (7%-Buchung auf 8300 hat ustva_kz=NULL und beeinflusst Kz 81/83 nicht)
$kzNach = $calc->calculate($year, $month); $kzNach = $calc->calculate($year, $month);
$delta83 = round((float)$kzNach['83']['steuer'] - (float)$kzPre['83']['steuer'], 2); $delta83 = round((float)$kzNach['83']['steuer'] - (float)$kzPre['83']['steuer'], 2);
$this->assertEquals(14.00, $delta83, 'Kz 83: Netto-Delta = 14 € nach Storno der 19%-Buchung'); $this->assertEquals(0.00, $delta83, 'Kz 83: zurück auf Ausgangswert nach Storno der 19%-Buchung');
} }
} }