findAccountId('1200'); // Bank $kategorieId = $this->findAccountId('8400'); // Erlöse 19 % USt if (!$zahlungsId || !$kategorieId) { $this->assert(false, 'Konten 1200/8400 nicht gefunden – SKR03-Seed vorhanden?'); return; } $customerId = \Steuerkram\Models\Customer::create([ 'nachname' => 'Zahlstatus', 'firma' => 'Zahlstatus GmbH', 'strasse' => 'Teststraße 7', 'plz' => '10115', 'ort' => 'Berlin', 'payment_days' => 14, ]); // Rechnung über 1000 € netto / 1190 € brutto $invoiceId = \Steuerkram\Models\Invoice::create([ 'customer_id' => $customerId, 'betreff' => 'Testrechnung Zahlungsstatus', 'rechnungs_datum' => date('Y-m-d'), 'lines' => [[ 'bezeichnung' => 'Beratungsleistung', 'menge' => 5, 'einheit' => 'Std.', 'einzelpreis' => 200.00, 'ust_satz' => 19.0, ]], ]); $nr = \Steuerkram\Models\Invoice::finalize($invoiceId); $invoice = \Steuerkram\Models\Invoice::find($invoiceId); $this->assertEquals(1190.00, (float)$invoice['betrag_brutto'], 'Rechnungsbetrag 1190 € brutto'); // Beleg anlegen und mit der Rechnung verknüpfen $belegId = \Steuerkram\Models\Beleg::create([ 'beleg_nr' => \Steuerkram\Models\Beleg::generateBelegNr(), 'filename' => $nr . '.pdf', 'storage_path' => '', 'mime_type' => 'application/pdf', 'file_hash' => hash('sha256', $nr), 'beleg_datum' => $invoice['rechnungs_datum'], 'rechnungs_nr' => $nr, 'betrag_brutto' => $invoice['betrag_brutto'], 'belegtyp' => 'Ausgangsrechnung', ]); \Steuerkram\Database::execute('UPDATE invoices SET beleg_id=? WHERE id=?', [$belegId, $invoiceId]); $buche = function (float $netto) use ($belegId, $kategorieId, $zahlungsId, $nr): int { return \Steuerkram\Models\Booking::create([ 'beleg_id' => $belegId, 'buchungs_datum' => date('Y-m-d'), 'buchungstyp' => 'Einnahme', 'kategorie_konto_id' => $kategorieId, 'zahlungskonto_id' => $zahlungsId, 'betrag_netto' => $netto, 'ust_satz' => 19.0, 'buchungstext' => 'Zahlung Ausgangsrechnung ' . $nr, ]); }; $status = fn(): string => (string)\Steuerkram\Database::fetchOne( 'SELECT status FROM invoices WHERE id=?', [$invoiceId] )['status']; // 1) Teilzahlung: 400 € netto = 476 € brutto von 1190 € $teilId = $buche(400.00); $this->assertEquals('Teilzahlung', $status(), 'Nach Teilbetrag ist der Status Teilzahlung'); // 2) Restzahlung: 600 € netto = 714 € brutto → Summe exakt 1190 € $restId = $buche(600.00); $this->assertEquals('Bezahlt', $status(), 'Nach Ausgleich des Restbetrags ist der Status Bezahlt'); // 3) Überzahlung: weitere 100 € netto $ueberId = $buche(100.00); $this->assertEquals('Überzahlt', $status(), 'Nach zusätzlicher Zahlung ist der Status Überzahlt'); // 4) Überzahlung stornieren → wieder Bezahlt \Steuerkram\Models\Booking::reverse($ueberId, 'Testkorrektur'); $this->assertEquals('Bezahlt', $status(), 'Nach Storno der Überzahlung wieder Bezahlt'); // 5) Alle übrigen Buchungen stornieren → zurück auf Finalisiert (nie versendet) \Steuerkram\Models\Booking::reverse($teilId, 'Testkorrektur'); \Steuerkram\Models\Booking::reverse($restId, 'Testkorrektur'); $this->assertEquals('Finalisiert', $status(), 'Ohne aktive Buchungen zurück auf Finalisiert'); // 6) Nach Versand fällt der Status stattdessen auf Gesendet zurück \Steuerkram\Database::execute( "UPDATE invoices SET status='Gesendet', sent_at=NOW() WHERE id=?", [$invoiceId] ); $letzteId = $buche(1000.00); $this->assertEquals('Bezahlt', $status(), 'Vollzahlung nach Versand ergibt Bezahlt'); \Steuerkram\Models\Booking::reverse($letzteId, 'Testkorrektur'); $this->assertEquals('Gesendet', $status(), 'Nach Storno mit sent_at zurück auf Gesendet'); // Summenhilfsfunktion prüft nur aktive Einnahme-Hauptbuchungen $this->assertEquals( 0.0, \Steuerkram\Models\Booking::sumBruttoForBeleg($belegId, 'Einnahme'), 'Zahlungssumme nach allen Stornos wieder 0 €' ); } }