rechnungsnummerWirdFreigegeben(); $this->aeltereRechnungsnummerBleibtVerbraucht(); $this->belegnummerWirdFreigegeben(); } // ── Rechnungen ────────────────────────────────────────────────────────── private function rechnungsnummerWirdFreigegeben(): void { $vorher = $this->invoiceNrNext(); $invoiceId = $this->createRechnung('Nummernkreis-Freigabe'); $nr = \Steuerkram\Models\Invoice::finalize($invoiceId); $this->assertNotEmpty($nr, 'Finalisieren vergibt eine Rechnungsnummer'); $this->assertEquals($vorher + 1, $this->invoiceNrNext(), 'Zähler steht nach dem Finalisieren eins weiter'); \Steuerkram\Models\Invoice::delete($invoiceId); $this->assertEquals( $vorher, $this->invoiceNrNext(), 'Nach dem Löschen steht der Zähler wieder auf dem Ausgangswert' ); // Die freigegebene Nummer muss erneut vergeben werden – sonst entstünde // trotz zurückgesetztem Zähler eine andere Nummer. $zweiteId = $this->createRechnung('Nummernkreis-Wiedervergabe'); $this->assertEquals($nr, \Steuerkram\Models\Invoice::finalize($zweiteId), 'Die freigegebene Nummer wird erneut vergeben'); \Steuerkram\Models\Invoice::delete($zweiteId); } private function aeltereRechnungsnummerBleibtVerbraucht(): void { $ersteId = $this->createRechnung('Nummernkreis-Alt'); \Steuerkram\Models\Invoice::finalize($ersteId); $zweiteId = $this->createRechnung('Nummernkreis-Neu'); $zweiteNr = \Steuerkram\Models\Invoice::finalize($zweiteId); $standMitBeiden = $this->invoiceNrNext(); // Die ältere der beiden Rechnungen löschen: ihre Nummer darf nicht // freigegeben werden, sie liegt vor der bereits vergebenen $zweiteNr. \Steuerkram\Models\Invoice::delete($ersteId); $this->assertEquals( $standMitBeiden, $this->invoiceNrNext(), 'Das Löschen einer älteren Rechnung lässt den Zähler unverändert' ); // Jetzt die jüngste löschen – deren Nummer wird freigegeben. \Steuerkram\Models\Invoice::delete($zweiteId); $this->assertEquals( $standMitBeiden - 1, $this->invoiceNrNext(), 'Das Löschen der jüngsten Rechnung gibt deren Nummer frei' ); $dritteId = $this->createRechnung('Nummernkreis-Nachrueck'); $this->assertEquals( $zweiteNr, \Steuerkram\Models\Invoice::finalize($dritteId), 'Die freigegebene Nummer der jüngsten Rechnung rückt nach' ); \Steuerkram\Models\Invoice::delete($dritteId); } // ── Belege ────────────────────────────────────────────────────────────── private function belegnummerWirdFreigegeben(): void { $vorher = $this->belegNrNext(); $ersterId = $this->createBeleg('Nummernkreis-Beleg-1'); $zweiterId = $this->createBeleg('Nummernkreis-Beleg-2'); $this->assertEquals($vorher + 2, $this->belegNrNext(), 'Zwei Belege verbrauchen zwei Nummern'); // Älterer Beleg: keine Freigabe. $this->assert(\Steuerkram\Models\Beleg::delete($ersterId), 'Älterer Beleg ist löschbar'); $this->assertEquals( $vorher + 2, $this->belegNrNext(), 'Das Löschen eines älteren Belegs lässt den Zähler unverändert' ); // Jüngster Beleg: Nummer wird frei. $this->assert(\Steuerkram\Models\Beleg::delete($zweiterId), 'Jüngster Beleg ist löschbar'); $this->assertEquals( $vorher + 1, $this->belegNrNext(), 'Das Löschen des jüngsten Belegs gibt dessen Nummer frei' ); } // ── Hilfsfunktionen ───────────────────────────────────────────────────── private function invoiceNrNext(): int { $row = \Steuerkram\Database::fetchOne('SELECT invoice_nr_next FROM company_settings WHERE id=1'); return (int)($row['invoice_nr_next'] ?? 0); } private function belegNrNext(): int { $row = \Steuerkram\Database::fetchOne('SELECT beleg_nr_next FROM company_settings WHERE id=1'); return (int)($row['beleg_nr_next'] ?? 0); } private function createRechnung(string $marker): int { $customerId = \Steuerkram\Models\Customer::create([ 'nachname' => 'Nummernkreis', 'firma' => $marker . ' GmbH', 'strasse' => 'Zählerweg 1', 'plz' => '12345', 'ort' => 'Nummernstadt', 'land' => 'DE', 'payment_days' => 14, ]); return \Steuerkram\Models\Invoice::create([ 'customer_id' => $customerId, 'betreff' => $marker, 'rechnungs_datum' => date('Y-m-d'), 'lines' => [ ['bezeichnung' => 'Leistung', 'menge' => 1, 'einheit' => 'Std.', 'einzelpreis' => 100.00, 'ust_satz' => 19.0], ], ]); } /** Legt einen Beleg ohne Datei an (storage_path zeigt bewusst ins Leere). */ private function createBeleg(string $marker): int { $belegNr = \Steuerkram\Models\Beleg::generateBelegNr(); return \Steuerkram\Models\Beleg::create([ 'beleg_nr' => $belegNr, 'filename' => $marker . '.pdf', 'storage_path' => date('Y') . '/' . $marker . '/original.pdf', 'mime_type' => 'application/pdf', 'file_hash' => hash('sha256', $belegNr . $marker), 'belegtyp' => 'Eingangsrechnung', 'beleg_datum' => date('Y-m-d'), 'notes' => $marker, ]); } }