findAccountId('1200'); // Bank $ausgabeId = $this->findAccountId('4920'); // Telefon (ustva_kz=61) if (!$zahlungsId || !$ausgabeId) { $this->assert(false, 'Benötigte Konten nicht gefunden – SKR03-Seed vorhanden?'); return; } // ── EÜR: Konto-Stand VOR den Test-Buchungen ermitteln ───────── $euerCalcPre = new \Steuerkram\Services\EuerCalculator(); $euerPre = $euerCalcPre->calculate($year); $kontoNettoPre = 0.0; foreach ($euerPre['ausgaben'] as $row) { if ((int)$row['id'] === (int)$ausgabeId) { $kontoNettoPre = (float)$row['netto']; break; } } // ── Beleg anlegen ────────────────────────────────────────────── $belegId = \Steuerkram\Models\Beleg::create([ 'beleg_nr' => 'STORNO-TEST-' . uniqid(), 'filename' => 'storno-test.pdf', 'storage_path' => '', 'mime_type' => 'application/pdf', 'file_hash' => hash('sha256', 'storno-test-' . uniqid()), 'beleg_datum' => $datum, 'belegtyp' => 'Ausgangsrechnung', ]); // ── Ausgabe-Buchung anlegen (Netto=100, USt 19%=19, Brutto=119) ─ $bookingId = \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' => 19.0, 'buchungstext' => 'Test-Ausgabe für Storno', ]); $original = \Steuerkram\Models\Booking::find($bookingId); $this->assertEquals(100.00, round((float)$original['betrag_netto'], 2), 'Originalbuchung: betrag_netto = 100'); $this->assertEquals(19.00, round((float)$original['betrag_ust'], 2), 'Originalbuchung: betrag_ust = 19'); $this->assertEquals(119.00, round((float)$original['betrag_brutto'], 2),'Originalbuchung: betrag_brutto = 119'); // ── Stornieren ──────────────────────────────────────────────── $reversalId = \Steuerkram\Models\Booking::reverse($bookingId, 'Testfall Storno'); $reversal = \Steuerkram\Models\Booking::find($reversalId); $this->assertEquals(1, (int)$reversal['is_reversal'], 'Stornobuchung: is_reversal = 1'); $this->assertEquals(-100.00, round((float)$reversal['betrag_netto'], 2), 'Stornobuchung: betrag_netto = -100'); $this->assertEquals(-19.00, round((float)$reversal['betrag_ust'], 2), 'Stornobuchung: betrag_ust = -19'); $this->assertEquals(-119.00, round((float)$reversal['betrag_brutto'], 2), 'Stornobuchung: betrag_brutto = -119'); // ── EÜR: Storno + Original heben sich auf (Konto-Saldo unverändert) ─ // Vergleich mit dem Stand vor den Test-Buchungen, da auf dem Produktivsystem // bereits echte Buchungen auf dem Konto existieren können. $euerCalc = new \Steuerkram\Services\EuerCalculator(); $euer = $euerCalc->calculate($year); $kontoNetto = 0.0; foreach ($euer['ausgaben'] as $row) { if ((int)$row['id'] === (int)$ausgabeId) { $kontoNetto = (float)$row['netto']; break; } } $this->assertEquals( round($kontoNettoPre, 2), round($kontoNetto, 2), 'EÜR: Storno + Original = 0 auf Ausgabe-Konto' ); // ── UStVA: Vorsteuer-KZ hebt sich auf ───────────────────────── // Kz 66 wird kontobasiert über VSt-Buchungszeilen (Konten 1571/1576) ermittelt. // Zur Original-Ausgabe gehört eine VSt-Zeile (+19), zum Storno eine VSt-Storno-Zeile (-19). // Wir prüfen, dass die VSt-Storno-Zeile im Drill-down für Kz 66 erscheint. $vstReversalRow = \Steuerkram\Database::fetchOne( 'SELECT * FROM bookings WHERE parent_booking_id = ? AND is_reversal = 1', [$reversalId] ); $this->assert($vstReversalRow !== false, 'VSt-Stornobuchung wurde zu Storno angelegt'); if ($vstReversalRow) { $this->assertEquals(-19.00, round((float)$vstReversalRow['betrag_netto'], 2), 'VSt-Storno: betrag_netto = -19'); } $ustvaCalc = new \Steuerkram\Services\UstvaCalculator(); $bookings = $ustvaCalc->fetchBookingsForKz($year, $month, '66'); $vstReversalFound = false; foreach ($bookings as $b) { if ($vstReversalRow && (int)$b['id'] === (int)$vstReversalRow['id']) { $vstReversalFound = true; $this->assertEquals(1, (int)$b['is_reversal'], 'UStVA-Drilldown: VSt-Stornobuchung is_reversal = 1'); $this->assertEquals(-19.00, round((float)$b['betrag_netto'], 2), 'UStVA-Drilldown: VSt-Storno betrag_netto = -19'); break; } } $this->assert($vstReversalFound, 'UStVA-Drilldown Kz 66: VSt-Stornobuchung erscheint in Detailansicht'); } }