findAccountId('1200'); $ausgabeId = $this->findAccountId('4920'); $konto1576 = \Steuerkram\Database::fetchOne( "SELECT id FROM accounts WHERE kontonummer = '1576' AND aktiv = 1" ); $konto1571 = \Steuerkram\Database::fetchOne( "SELECT id FROM accounts WHERE kontonummer = '1571' AND aktiv = 1" ); if (!$zahlungsId || !$ausgabeId || !$konto1576 || !$konto1571) { $this->assert(false, 'Benötigte Konten nicht gefunden (1200, 4920, 1571, 1576)'); return; } $this->assert(true, 'Konten 1200, 4920, 1571, 1576 vorhanden'); // ── UStVA-Stand vor den Testbuchungen ───────────────────────── $calc = new \Steuerkram\Services\UstvaCalculator(); $kzPre = $calc->calculate($year, $month); // ── Beleg anlegen ────────────────────────────────────────────── $belegId = \Steuerkram\Models\Beleg::create([ 'beleg_nr' => 'VST-TEST-' . uniqid(), 'filename' => 'vst-test.pdf', 'storage_path' => '', 'mime_type' => 'application/pdf', 'file_hash' => hash('sha256', 'vst-test-' . uniqid()), 'beleg_datum' => $datum, 'belegtyp' => 'Eingangsrechnung', ]); // ── Ausgabe-Buchung 19 % (Netto=200, VSt=38) ────────────────── $bookingId19 = \Steuerkram\Models\Booking::create([ 'beleg_id' => $belegId, 'buchungs_datum' => $datum, 'buchungstyp' => 'Ausgabe', 'kategorie_konto_id' => $ausgabeId, 'zahlungskonto_id' => $zahlungsId, 'betrag_netto' => 200.00, 'ust_satz' => 19.0, 'buchungstext' => 'Test VSt 19%', ]); // VSt-Buchungszeile muss auf 1576 angelegt worden sein $vstRow19 = \Steuerkram\Database::fetchOne( 'SELECT bk.*, a.kontonummer FROM bookings bk JOIN accounts a ON bk.kategorie_konto_id = a.id WHERE bk.parent_booking_id = ?', [$bookingId19] ); $this->assert($vstRow19 !== false, 'VSt-Buchungszeile für 19%-Buchung angelegt'); $this->assertEquals('1576', $vstRow19['kontonummer'] ?? '', 'VSt-Buchungszeile auf Konto 1576'); $this->assertEquals(38.00, round((float)($vstRow19['betrag_netto'] ?? 0), 2), 'VSt betrag_netto = 38'); $this->assertEquals(0.00, round((float)($vstRow19['betrag_ust'] ?? 0), 2), 'VSt betrag_ust = 0'); // ── Ausgabe-Buchung 7 % (Netto=100, VSt=7) ──────────────────── $bookingId7 = \Steuerkram\Models\Booking::create([ 'beleg_id' => $belegId, 'buchungs_datum' => $datum, 'buchungstyp' => 'Ausgabe', 'kategorie_konto_id' => $ausgabeId, 'zahlungskonto_id' => $zahlungsId, 'betrag_netto' => 100.00, 'ust_satz' => 7.0, 'buchungstext' => 'Test VSt 7%', ]); $vstRow7 = \Steuerkram\Database::fetchOne( 'SELECT bk.*, a.kontonummer FROM bookings bk JOIN accounts a ON bk.kategorie_konto_id = a.id WHERE bk.parent_booking_id = ?', [$bookingId7] ); $this->assert($vstRow7 !== false, 'VSt-Buchungszeile für 7%-Buchung angelegt'); $this->assertEquals('1571', $vstRow7['kontonummer'] ?? '', 'VSt-Buchungszeile auf Konto 1571'); $this->assertEquals(7.00, round((float)($vstRow7['betrag_netto'] ?? 0), 2), 'VSt betrag_netto = 7'); // ── UStVA: Kz 66 enthält beide VSt-Buchungszeilen (1571 + 1576) ── $kzMit = $calc->calculate($year, $month); $this->assert( round((float)$kzMit['66']['steuer'] - (float)$kzPre['66']['steuer'], 2) >= 45.00, 'UStVA Kz 66 stieg durch 19%- und 7%-Buchung um ≥ 45 €' ); // ── Drill-down Kz 66 enthält die VSt-Buchungszeile (1576) ───── $drilldown = $calc->fetchBookingsForKz($year, $month, '66'); $vstFound = false; foreach ($drilldown as $b) { if ((int)$b['id'] === (int)$vstRow19['id']) { $vstFound = true; break; } } $this->assert($vstFound, 'Drill-down Kz 66 enthält die VSt-Buchungszeile (1576)'); // ── Storno: VSt-Zeile muss mitaufgehoben werden ─────────────── $reversalId = \Steuerkram\Models\Booking::reverse($bookingId19, 'Testfall VSt-Storno'); $vstStornoRow = \Steuerkram\Database::fetchOne( 'SELECT * FROM bookings WHERE parent_booking_id = ? AND is_reversal = 1', [$reversalId] ); $this->assert($vstStornoRow !== false, 'VSt-Stornobuchung wurde angelegt'); $this->assertEquals(-38.00, round((float)($vstStornoRow['betrag_netto'] ?? 0), 2), 'VSt-Storno betrag_netto = -38'); // Original-VSt-Zeile muss als storniert markiert sein $vstOriginal = \Steuerkram\Database::fetchOne( 'SELECT reversed_by FROM bookings WHERE id = ?', [(int)$vstRow19['id']] ); $this->assert(!empty($vstOriginal['reversed_by']), 'Original-VSt-Buchungszeile hat reversed_by gesetzt'); // UStVA Kz 66 nach Storno: Netto-Delta = 7 € (nur 7%-Buchung verbleibt) $kzNach = $calc->calculate($year, $month); $delta66 = round((float)$kzNach['66']['steuer'] - (float)$kzPre['66']['steuer'], 2); $this->assertEquals(7.00, $delta66, 'Kz 66: Netto-Delta = 7 € nach Storno der 19%-Buchung'); } }