Steuerkram/tests/27_StornorechnungTest.php

106 lines
4.6 KiB
PHP

<?php
declare(strict_types=1);
/**
* Test 27: Stornorechnung erstellen via Invoice::createCancellation().
*/
class StornorechnungTest extends TestCase
{
protected string $testName = 'Test 27: Stornorechnung erstellen';
public function run(): void
{
// Testkunden anlegen
$customerId = \Steuerkram\Models\Customer::create([
'nachname' => 'StornoTester',
'vorname' => 'Test',
'firma' => 'Storno GmbH',
'strasse' => 'Testweg 1',
'plz' => '10115',
'ort' => 'Berlin',
'land' => 'DE',
'payment_days' => 14,
]);
// Originalrechnung erstellen und finalisieren
$originalId = \Steuerkram\Models\Invoice::create([
'customer_id' => $customerId,
'betreff' => 'Testrechnung für Storno',
'rechnungs_datum' => date('Y-m-d'),
'lines' => [
[
'bezeichnung' => 'Beratung',
'menge' => 2,
'einheit' => 'Std.',
'einzelpreis' => 100.00,
'ust_satz' => 19.0,
],
[
'bezeichnung' => 'Reisekosten',
'menge' => 1,
'einheit' => 'Pauschal',
'einzelpreis' => 50.00,
'ust_satz' => 19.0,
],
],
]);
\Steuerkram\Models\Invoice::finalize($originalId);
$original = \Steuerkram\Models\Invoice::find($originalId);
$this->assertNotEmpty($original['rechnungs_nr'], 'Original hat Rechnungsnummer');
$this->assertEquals('Finalisiert', $original['status'], 'Original ist finalisiert');
$this->assertEquals(250.00, round((float)$original['betrag_netto'], 2), 'Original Nettobetrag korrekt');
// Stornorechnung erstellen
$stornoId = \Steuerkram\Models\Invoice::createCancellation($originalId);
$this->assertGreaterThan(0, $stornoId, 'createCancellation gibt gültige ID zurück');
// Stornorechnung prüfen
$storno = \Steuerkram\Models\Invoice::find($stornoId);
$this->assertNotEmpty($storno, 'Stornorechnung gefunden');
$this->assertEquals($originalId, (int)$storno['storno_of_id'], 'storno_of_id zeigt auf Original');
$this->assertEquals('Finalisiert', $storno['status'], 'Stornorechnung ist sofort finalisiert');
$this->assertNotEmpty($storno['rechnungs_nr'], 'Stornorechnung hat Rechnungsnummer');
$this->assert($original['rechnungs_nr'] !== $storno['rechnungs_nr'], 'Rechnungsnummern sind verschieden');
// Beträge sind negiert
$this->assertEquals(-250.00, round((float)$storno['betrag_netto'], 2), 'Nettobetrag negiert');
$this->assertEquals(-47.50, round((float)$storno['betrag_ust'], 2), 'USt-Betrag negiert');
$this->assertEquals(-297.50, round((float)$storno['betrag_brutto'], 2), 'Bruttobetrag negiert');
// Originalrechnung ist storniert
$originalNach = \Steuerkram\Models\Invoice::find($originalId);
$this->assertEquals('Storniert', $originalNach['status'], 'Originalrechnung hat Status Storniert');
// Positionen der Stornorechnung sind negiert
$stornoLines = \Steuerkram\Models\Invoice::lines($stornoId);
$this->assertEquals(2, count($stornoLines), 'Stornorechnung hat 2 Positionen');
foreach ($stornoLines as $line) {
if (($line['type'] ?? 'position') === 'position') {
$this->assert((float)$line['menge'] > 0, 'Menge bleibt positiv');
$this->assert((float)$line['einzelpreis'] < 0, 'Einzelpreis ist negativ');
}
}
// Stornobuchungen müssen angelegt worden sein (Original hatte beleg_id nach finalize)
$originalMitBeleg = \Steuerkram\Models\Invoice::find($originalId);
if (!empty($originalMitBeleg['beleg_id'])) {
$bookings = \Steuerkram\Database::fetchAll(
'SELECT id FROM bookings WHERE beleg_id = ? AND is_reversal = 1',
[(int)$originalMitBeleg['beleg_id']]
);
$this->assertGreaterThan(0, count($bookings), 'Stornobuchungen wurden angelegt');
}
// Erneutes Stornieren der Stornorechnung muss fehlschlagen
$exception = false;
try {
\Steuerkram\Models\Invoice::createCancellation($stornoId);
} catch (\RuntimeException $e) {
$exception = true;
}
$this->assert($exception, 'Stornorechnung kann nicht erneut storniert werden');
}
}