Steuerkram/tests/73_InvoiceZahlungsstatusTest.php

121 lines
5.3 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
declare(strict_types=1);
/**
* Test 73: Zahlungsstatus einer Ausgangsrechnung aus den Buchungen ableiten.
*
* Erwartetes Verhalten: Teilbetrag → 'Teilzahlung', Restbetrag → 'Bezahlt',
* weitere Zahlung → 'Überzahlt', Storno aller Buchungen → zurück auf 'Gesendet'
* bzw. 'Finalisiert'.
*/
class InvoiceZahlungsstatusTest extends TestCase
{
protected string $testName = 'Test 73: Rechnungsstatus aus Zahlungen (Teilzahlung/Bezahlt/Überzahlt)';
public function run(): void
{
$zahlungsId = $this->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 €'
);
}
}