121 lines
5.3 KiB
PHP
121 lines
5.3 KiB
PHP
<?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 €'
|
||
);
|
||
}
|
||
}
|