Fix #9: IST-Versteuerung – Zahldatum statt Buchungsdatum für Belege und Ausgangsrechnungen

This commit is contained in:
fds2610 2026-03-27 20:15:32 +01:00
parent 60815f7179
commit 6e0f93c3b1
6 changed files with 434 additions and 66 deletions

View file

@ -86,7 +86,8 @@ $router->get('/invoices/{id}/edit', fn($p) => (new InvoiceController())->edit($p
$router->post('/invoices/{id}', fn($p) => (new InvoiceController())->update($p));
$router->post('/invoices/{id}/finalize', fn($p) => (new InvoiceController())->finalize($p));
$router->get('/invoices/{id}/pdf', fn($p) => (new InvoiceController())->pdf($p));
$router->post('/invoices/{id}/book', fn($p) => (new InvoiceController())->book($p));
$router->get('/invoices/{id}/book', fn($p) => (new InvoiceController())->book($p));
$router->post('/invoices/{id}/bookings', fn($p) => (new InvoiceController())->storeBooking($p));
$router->post('/invoices/{id}/mark-paid', fn($p) => (new InvoiceController())->markPaid($p));
$router->post('/invoices/{id}/cancel', fn($p) => (new InvoiceController())->cancel($p));
$router->post('/invoices/{id}/lines', fn($p) => (new InvoiceController())->addLine($p));

View file

@ -8,6 +8,7 @@ use Steuerkram\Models\Invoice;
use Steuerkram\Models\Customer;
use Steuerkram\Models\Beleg;
use Steuerkram\Models\Booking;
use Steuerkram\Models\Account;
use Steuerkram\Models\Setting;
use Steuerkram\Services\PdfInvoiceService;
use Steuerkram\View;
@ -172,9 +173,9 @@ class InvoiceController
\Steuerkram\Database::beginTransaction();
$nr = Invoice::finalize($id);
$invoice = Invoice::find($id);
$belegNr = $this->createBelegAndBookings($invoice);
$belegNr = $this->createBeleg($invoice);
\Steuerkram\Database::commit();
View::redirect("/invoices/{$id}", "Rechnung {$nr} wurde finalisiert und gebucht (Beleg {$belegNr}).");
View::redirect("/invoices/{$id}", "Rechnung {$nr} wurde finalisiert (Beleg {$belegNr}). Bitte Zahlungseingang erfassen.");
} catch (\PDOException $e) {
\Steuerkram\Database::rollBack();
error_log('[Steuerkram] PDO-Fehler in InvoiceController::finalize(): ' . $e->getMessage());
@ -207,27 +208,110 @@ class InvoiceController
View::redirect("/invoices/{$id}", 'Rechnung muss zuerst finalisiert werden.', 'error');
return;
}
if (!empty($invoice['beleg_id'])) {
View::redirect("/invoices/{$id}", 'Rechnung wurde bereits gebucht.', 'warning');
if (empty($invoice['beleg_id'])) {
View::redirect("/invoices/{$id}", 'Kein Beleg zugeordnet.', 'error');
return;
}
$bookings = Booking::forBeleg((int)$invoice['beleg_id']);
$accounts = Account::all(['aktiv' => 1]);
$formErrors = $_GET['_error'] ?? [];
$formData = [];
$view = new View();
$view->render('layout/base.php', [
'pageTitle' => 'Zahlungseingang – ' . ($invoice['rechnungs_nr'] ?? 'Rechnung'),
'content' => $this->renderView('invoices/book', [
'invoice' => $invoice,
'bookings' => $bookings,
'accounts' => $accounts,
'formErrors' => $formErrors,
'formData' => $formData,
]),
]);
}
public function storeBooking(array $params): void
{
$id = (int)$params['id'];
$invoice = Invoice::find($id);
if (!$invoice) { http_response_code(404); return; }
$belegId = (int)($invoice['beleg_id'] ?? 0);
if (!$belegId) {
View::redirect("/invoices/{$id}", 'Kein Beleg zugeordnet.', 'error');
return;
}
$data = $_POST;
$errors = [];
if (empty($data['kategorie_konto_id'])) $errors['kategorie_konto_id'] = 'Pflichtfeld';
if (empty($data['zahlungskonto_id'])) $errors['zahlungskonto_id'] = 'Pflichtfeld';
if (empty($data['buchungs_datum'])) $errors['buchungs_datum'] = 'Pflichtfeld';
if (!isset($data['betrag_netto']) || $data['betrag_netto'] === '')
$errors['betrag_netto'] = 'Pflichtfeld';
if (empty($data['buchungstext'])) $errors['buchungstext'] = 'Pflichtfeld';
if (empty($errors) && $data['kategorie_konto_id'] === $data['zahlungskonto_id']) {
$errors['zahlungskonto_id'] = 'Kategorie- und Zahlungskonto dürfen nicht identisch sein.';
}
if ($errors) {
$bookings = Booking::forBeleg($belegId);
$accounts = Account::all(['aktiv' => 1]);
$view = new View();
$view->render('layout/base.php', [
'pageTitle' => 'Zahlungseingang – ' . ($invoice['rechnungs_nr'] ?? 'Rechnung'),
'content' => $this->renderView('invoices/book', [
'invoice' => $invoice,
'bookings' => $bookings,
'accounts' => $accounts,
'formErrors' => $errors,
'formData' => $data,
]),
]);
return;
}
try {
\Steuerkram\Database::beginTransaction();
$belegNr = $this->createBelegAndBookings($invoice);
\Steuerkram\Database::commit();
View::redirect("/invoices/{$id}", 'Rechnung erfolgreich gebucht (Beleg ' . $belegNr . ').');
$data['beleg_id'] = $belegId;
$data['buchungstyp'] = 'Einnahme';
Booking::create($data);
View::redirect("/invoices/{$id}/book", 'Buchung erfasst.');
} catch (\PDOException $e) {
\Steuerkram\Database::rollBack();
error_log('[Steuerkram] PDO-Fehler in InvoiceController::book(): ' . $e->getMessage());
View::redirect("/invoices/{$id}", 'Datenbankfehler beim Buchen der Rechnung.', 'error');
error_log('[Steuerkram] PDO-Fehler in InvoiceController::storeBooking(): ' . $e->getMessage());
$bookings = Booking::forBeleg($belegId);
$accounts = Account::all(['aktiv' => 1]);
$view = new View();
$view->render('layout/base.php', [
'pageTitle' => 'Zahlungseingang – ' . ($invoice['rechnungs_nr'] ?? 'Rechnung'),
'content' => $this->renderView('invoices/book', [
'invoice' => $invoice,
'bookings' => $bookings,
'accounts' => $accounts,
'formErrors' => ['_exception' => 'Datenbankfehler beim Speichern der Buchung.'],
'formData' => $data,
]),
]);
} catch (\Exception $e) {
\Steuerkram\Database::rollBack();
View::redirect("/invoices/{$id}", $e->getMessage(), 'error');
$bookings = Booking::forBeleg($belegId);
$accounts = Account::all(['aktiv' => 1]);
$view = new View();
$view->render('layout/base.php', [
'pageTitle' => 'Zahlungseingang – ' . ($invoice['rechnungs_nr'] ?? 'Rechnung'),
'content' => $this->renderView('invoices/book', [
'invoice' => $invoice,
'bookings' => $bookings,
'accounts' => $accounts,
'formErrors' => ['_exception' => $e->getMessage()],
'formData' => $data,
]),
]);
}
}
private function createBelegAndBookings(array $invoice): string
private function createBeleg(array $invoice): string
{
$id = (int)$invoice['id'];
@ -235,7 +319,7 @@ class InvoiceController
$belegId = Beleg::create([
'beleg_nr' => $belegNr,
'filename' => $invoice['rechnungs_nr'] . '.pdf',
'storage_path' => '', // no physical file for outgoing invoices
'storage_path' => '',
'mime_type' => 'application/pdf',
'file_hash' => hash('sha256', $invoice['rechnungs_nr']),
'beleg_datum' => $invoice['rechnungs_datum'],
@ -246,52 +330,6 @@ class InvoiceController
'notes' => 'Automatisch erstellt aus Rechnung ' . $invoice['rechnungs_nr'],
]);
$zahlungskonto = \Steuerkram\Database::fetchOne(
'SELECT id FROM accounts WHERE kontonummer = ?', ['1400']
);
if (!$zahlungskonto) {
throw new \RuntimeException('Forderungskonto 1400 nicht gefunden.');
}
$erloesKonten = [];
foreach (\Steuerkram\Database::fetchAll(
"SELECT id, kontonummer FROM accounts WHERE kontonummer IN ('8000','8200','8400')"
) as $row) {
$erloesKonten[$row['kontonummer']] = $row['id'];
}
$lines = Invoice::lines($id);
$gruppenNetto = [];
foreach ($lines as $line) {
$satz = (float)$line['ust_satz'];
$netto = round((float)$line['menge'] * (float)$line['einzelpreis'], 2);
$gruppenNetto[$satz] = ($gruppenNetto[$satz] ?? 0.0) + $netto;
}
foreach ($gruppenNetto as $satz => $netto) {
if (abs($satz - 19) < 0.01) {
$kategorieId = $erloesKonten['8000'] ?? null;
} elseif (abs($satz - 7) < 0.01) {
$kategorieId = $erloesKonten['8200'] ?? null;
} else {
$kategorieId = $erloesKonten['8400'] ?? null;
}
if (!$kategorieId) {
throw new \RuntimeException("Erlöskonto für USt-Satz {$satz}% nicht gefunden.");
}
Booking::create([
'beleg_id' => $belegId,
'buchungs_datum' => $invoice['rechnungs_datum'],
'buchungstyp' => 'Einnahme',
'kategorie_konto_id' => $kategorieId,
'zahlungskonto_id' => $zahlungskonto['id'],
'betrag_netto' => round($netto, 2),
'ust_satz' => $satz,
'buchungstext' => 'Ausgangsrechnung ' . $invoice['rechnungs_nr'],
]);
}
\Steuerkram\Database::execute(
'UPDATE invoices SET beleg_id=? WHERE id=?', [$belegId, $id]
);

View file

@ -301,7 +301,7 @@ $suppliers = $suppliers ?? [];
<div class="form-grid form-grid-2">
<div class="form-group">
<label>Buchungsdatum *</label>
<label>Zahldatum *</label>
<input type="date" name="buchungs_datum"
value="<?= $h($formData['buchungs_datum'] ?? date('Y-m-d')) ?>" required
class="<?= isset($formErrors['buchungs_datum']) ? 'input-error' : '' ?>">

219
src/Views/invoices/book.php Normal file
View file

@ -0,0 +1,219 @@
<?php
use Steuerkram\View;
$h = fn($v) => View::h($v);
$fmt = fn($v) => View::formatMoney($v);
$customerName = !empty($invoice['firma'])
? $invoice['firma']
: trim(($invoice['anrede'] ?? '') . ' ' . ($invoice['vorname'] ?? '') . ' ' . $invoice['nachname']);
$formErrors = $formErrors ?? [];
$formData = $formData ?? [];
$kontenGruppen = [];
foreach ($accounts as $acc) {
$kontenGruppen[$acc['kontenart']][] = $acc;
}
?>
<!-- Rechnungs-Header -->
<div class="card mb-2">
<div class="card-body" style="display:flex;gap:2rem;align-items:center;flex-wrap:wrap;padding:.75rem 1.25rem">
<div>
<span class="text-muted text-sm">Rechnung</span><br>
<strong class="font-mono"><?= $h($invoice['rechnungs_nr'] ?? '–') ?></strong>
</div>
<div>
<span class="text-muted text-sm">Kunde</span><br>
<?= $h($customerName) ?>
</div>
<div>
<span class="text-muted text-sm">Rechnungsdatum</span><br>
<?= View::formatDate($invoice['rechnungs_datum']) ?>
</div>
<div>
<span class="text-muted text-sm">Betrag</span><br>
<strong><?= $fmt($invoice['betrag_brutto']) ?></strong>
</div>
<div>
<span class="text-muted text-sm">Status</span><br>
<?php
$badgeClass = match($invoice['status']) {
'Bezahlt' => 'badge-green',
'Storniert' => 'badge-red',
'Gesendet' => 'badge-yellow',
default => 'badge-gray',
};
?>
<span class="badge <?= $badgeClass ?>"><?= $h($invoice['status']) ?></span>
</div>
<div style="margin-left:auto;display:flex;gap:.5rem">
<a href="/invoices/<?= $h($invoice['id']) ?>" class="btn btn-secondary btn-sm">Zur Rechnung</a>
<?php if (!empty($invoice['beleg_id'])): ?>
<a href="/belege/<?= $h($invoice['beleg_id']) ?>" class="btn btn-secondary btn-sm">Beleg öffnen</a>
<?php endif; ?>
</div>
</div>
</div>
<!-- Vorhandene Buchungen -->
<div class="card mb-2">
<div class="card-header">
<div class="card-title">Buchungszeilen (<?= count($bookings) ?>)</div>
</div>
<div class="table-wrap">
<table>
<thead>
<tr>
<th>Buchungsnr.</th>
<th>Zahldatum</th>
<th>Kategorie</th>
<th>Zahlungskonto</th>
<th class="td-right">Netto</th>
<th class="td-right">USt</th>
<th class="td-right">Brutto</th>
<th>Text</th>
</tr>
</thead>
<tbody>
<?php foreach ($bookings as $bk):
$isReversed = !empty($bk['reversed_by']);
$isReversal = (bool)$bk['is_reversal'];
?>
<tr style="<?= ($isReversed || $isReversal) ? 'opacity:.55;background:#fff5f5' : '' ?>">
<td class="td-mono text-sm"><?= $h($bk['buchungs_nr']) ?></td>
<td class="td-muted"><?= View::formatDate($bk['buchungs_datum']) ?></td>
<td class="td-mono text-sm"><?= $h($bk['kategorie_nr']) ?><br><span class="td-muted"><?= $h($bk['kategorie_name']) ?></span></td>
<td class="td-mono text-sm"><?= $h($bk['zahlungs_nr']) ?><br><span class="td-muted"><?= $h($bk['zahlungs_name']) ?></span></td>
<td class="td-right td-mono"><?= $fmt($bk['betrag_netto']) ?></td>
<td class="td-right td-mono"><?= number_format((float)$bk['ust_satz'], 0) ?> %</td>
<td class="td-right td-mono"><?= $fmt($bk['betrag_brutto']) ?></td>
<td class="text-sm">
<?= $h($bk['buchungstext']) ?>
<?php if ($isReversal): ?>
<span class="badge badge-red">Storno</span>
<?php elseif ($isReversed): ?>
<span class="badge badge-red">Storniert</span>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
<?php if (empty($bookings)): ?>
<tr><td colspan="8" class="text-center text-muted" style="padding:1rem">Noch keine Buchungen.</td></tr>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
<!-- Neue Buchungszeile erfassen -->
<?php if ($invoice['status'] !== 'Storniert'): ?>
<div class="card mb-2">
<div class="card-header">
<div class="card-title">Neue Buchungszeile</div>
</div>
<div class="card-body">
<?php if (!empty($formErrors)): ?>
<div class="flash flash-error" style="margin-bottom:1rem">
<?php if (isset($formErrors['_exception'])): ?>
Fehler: <?= $h($formErrors['_exception']) ?>
<?php else: ?>
<?= implode(' &nbsp;|&nbsp; ', array_map(fn($k,$v) => "<strong>$k:</strong> $v", array_keys($formErrors), $formErrors)) ?>
<?php endif; ?>
</div>
<?php endif; ?>
<form method="post" action="/invoices/<?= $h($invoice['id']) ?>/bookings">
<div class="form-grid form-grid-2">
<div class="form-group">
<label>Zahldatum *</label>
<input type="date" name="buchungs_datum"
value="<?= $h($formData['buchungs_datum'] ?? date('Y-m-d')) ?>" required
class="<?= isset($formErrors['buchungs_datum']) ? 'input-error' : '' ?>">
</div>
<div class="form-group">
<label>USt-Satz (%)</label>
<select name="ust_satz">
<?php foreach (['0','7','19'] as $s): ?>
<option value="<?= $s ?>"<?= ($formData['ust_satz'] ?? '19') === $s ? ' selected' : '' ?>><?= $s ?> %</option>
<?php endforeach; ?>
</select>
</div>
</div>
<!-- Kategorie-Konto (nur Betriebseinnahmen) -->
<div class="form-group">
<label for="kategorie_konto_id">Kategorie *</label>
<input type="search" placeholder="Kontonr. oder Bezeichnung filtern…" class="konto-filter"
data-target="kategorie_konto_id" autocomplete="off" style="margin-bottom:.3rem">
<select id="kategorie_konto_id" name="kategorie_konto_id" required size="5">
<option value="">– Konto wählen –</option>
<?php if (!empty($kontenGruppen['Betriebseinnahme'])): ?>
<optgroup label="── Betriebseinnahme ──">
<?php foreach ($kontenGruppen['Betriebseinnahme'] as $acc): ?>
<option value="<?= $h($acc['id']) ?>"
data-search="<?= $h($acc['kontonummer'] . ' ' . $acc['bezeichnung']) ?>"
<?= ($formData['kategorie_konto_id'] ?? '') == $acc['id'] ? 'selected' : '' ?>>
<?= $h($acc['kontonummer']) ?> – <?= $h($acc['bezeichnung']) ?>
</option>
<?php endforeach; ?>
</optgroup>
<?php endif; ?>
</select>
</div>
<!-- Zahlungskonto (nur Zahlungskonten) -->
<div class="form-group">
<label for="zahlungskonto_id">Zahlungskonto *</label>
<input type="search" placeholder="Kontonr. oder Bezeichnung filtern…" class="konto-filter"
data-target="zahlungskonto_id" autocomplete="off" style="margin-bottom:.3rem">
<select id="zahlungskonto_id" name="zahlungskonto_id" required size="4">
<option value="">– Konto wählen –</option>
<?php if (!empty($kontenGruppen['Zahlungskonto'])): ?>
<optgroup label="── Zahlungskonto ──">
<?php foreach ($kontenGruppen['Zahlungskonto'] as $acc): ?>
<option value="<?= $h($acc['id']) ?>"
data-search="<?= $h($acc['kontonummer'] . ' ' . $acc['bezeichnung']) ?>"
<?= ($formData['zahlungskonto_id'] ?? '') == $acc['id'] ? 'selected' : '' ?>>
<?= $h($acc['kontonummer']) ?> – <?= $h($acc['bezeichnung']) ?>
</option>
<?php endforeach; ?>
</optgroup>
<?php endif; ?>
</select>
</div>
<div class="form-grid form-grid-2">
<div class="form-group">
<label>Nettobetrag (€) *</label>
<input type="number" name="betrag_netto" step="0.01" min="0.01" required
placeholder="0.00"
value="<?= $h($formData['betrag_netto'] ?? '') ?>"
class="<?= isset($formErrors['betrag_netto']) ? 'input-error' : '' ?>">
</div>
<div class="form-group">
<label>Buchungstext *</label>
<input type="text" name="buchungstext" required
value="<?= $h($formData['buchungstext'] ?? ('Ausgangsrechnung ' . ($invoice['rechnungs_nr'] ?? ''))) ?>"
class="<?= isset($formErrors['buchungstext']) ? 'input-error' : '' ?>">
</div>
</div>
<button type="submit" class="btn btn-primary">Buchung erfassen</button>
</form>
</div>
</div>
<?php endif; ?>
<!-- Als bezahlt markieren -->
<?php if ($invoice['status'] === 'Gesendet' && !empty($bookings)): ?>
<div class="card">
<div class="card-body">
<p class="text-muted text-sm mb-2">Alle Zahlungseingänge erfasst? Dann die Rechnung als bezahlt markieren.</p>
<form method="post" action="/invoices/<?= $h($invoice['id']) ?>/mark-paid"
data-confirm="Rechnung als bezahlt markieren?">
<button type="submit" class="btn btn-success">Rechnung als bezahlt markieren</button>
</form>
</div>
</div>
<?php endif; ?>

View file

@ -40,9 +40,7 @@ $badgeClass = match($invoice['status']) {
<a href="/invoices/<?= $h($invoice['id']) ?>/pdf" class="btn btn-primary">PDF herunterladen</a>
<?php endif; ?>
<?php if ($invoice['status'] === 'Gesendet'): ?>
<form method="post" action="/invoices/<?= $h($invoice['id']) ?>/mark-paid">
<button type="submit" class="btn btn-success">Als bezahlt markieren</button>
</form>
<a href="/invoices/<?= $h($invoice['id']) ?>/book" class="btn btn-success">Zahlungseingang erfassen</a>
<?php endif; ?>
<?php if (in_array($invoice['status'], ['Entwurf','Gesendet'])): ?>
<form method="post" action="/invoices/<?= $h($invoice['id']) ?>/cancel"

View file

@ -0,0 +1,112 @@
<?php
declare(strict_types=1);
/**
* Test 10: IST-Versteuerung – Zahldatum unabhängig vom Rechnungsdatum.
*
* Prüft, dass:
* 1. Finalisieren einer Ausgangsrechnung keine Buchungen erzeugt.
* 2. Buchungen separat mit einem abweichenden Zahldatum erfasst werden können.
* 3. Die Buchung im Journal mit dem korrekten Zahldatum erscheint.
*/
class IstVersteuerungTest extends TestCase
{
protected string $testName = 'Test 10: IST-Versteuerung – Zahldatum';
public function run(): void
{
$rechnungsDatum = date('Y-m-d');
$zahlDatum = date('Y-m-d', strtotime('+7 days'));
// Konten prüfen
$zahlungsId = $this->findAccountId('1200'); // Bank
$kategorieId = $this->findAccountId('8000'); // Erlöse 19 %
if (!$zahlungsId || !$kategorieId) {
$zahlungsId = $zahlungsId ?? $this->findAccountId('1400');
$kategorieId = $kategorieId ?? $this->findAccountId('8400');
}
$this->assert($zahlungsId > 0, 'Zahlungskonto vorhanden');
$this->assert($kategorieId > 0, 'Einnahmekategorie vorhanden');
// Kunde anlegen
$customerId = \Steuerkram\Models\Customer::create([
'nachname' => 'IstTestKunde',
'firma' => 'IST GmbH',
'strasse' => 'Musterstr. 1',
'plz' => '10115',
'ort' => 'Berlin',
'payment_days' => 14,
]);
// Rechnung anlegen
$invoiceId = \Steuerkram\Models\Invoice::create([
'customer_id' => $customerId,
'betreff' => 'Test IST-Versteuerung',
'rechnungs_datum' => $rechnungsDatum,
'leistungszeitraum_von' => date('Y-m-01'),
'leistungszeitraum_bis' => date('Y-m-t'),
'lines' => [
[
'bezeichnung' => 'Beratung',
'menge' => 2,
'einheit' => 'Std.',
'einzelpreis' => 500.00,
'ust_satz' => 19.0,
],
],
]);
$this->assertGreaterThan(0, $invoiceId, 'Rechnung angelegt');
// Finalisieren
$nr = \Steuerkram\Models\Invoice::finalize($invoiceId);
$this->assertNotEmpty($nr, 'Rechnungsnummer vergeben');
$invoice = \Steuerkram\Models\Invoice::find($invoiceId);
$this->assertEquals('Gesendet', $invoice['status'] ?? null, 'Status nach Finalisierung: Gesendet');
// Beleg anlegen (wie in InvoiceController::createBeleg)
$belegNr = \Steuerkram\Models\Beleg::generateBelegNr();
$belegId = \Steuerkram\Models\Beleg::create([
'beleg_nr' => $belegNr,
'filename' => $nr . '.pdf',
'storage_path' => '',
'mime_type' => 'application/pdf',
'file_hash' => hash('sha256', $nr),
'beleg_datum' => $rechnungsDatum,
'eingangs_datum' => $rechnungsDatum,
'rechnungs_nr' => $nr,
'betrag_brutto' => $invoice['betrag_brutto'],
'belegtyp' => 'Ausgangsrechnung',
]);
\Steuerkram\Database::execute('UPDATE invoices SET beleg_id=? WHERE id=?', [$belegId, $invoiceId]);
// Noch keine Buchungen vorhanden (finalize erzeugt keine mehr)
$buchungenNachFinalisierung = \Steuerkram\Models\Booking::forBeleg($belegId);
$this->assertEquals(0, count($buchungenNachFinalisierung), 'Nach Finalisierung: keine Buchungen vorhanden');
// Buchung separat mit Zahldatum erfassen
$buchungId = \Steuerkram\Models\Booking::create([
'beleg_id' => $belegId,
'buchungs_datum' => $zahlDatum,
'buchungstyp' => 'Einnahme',
'kategorie_konto_id' => $kategorieId,
'zahlungskonto_id' => $zahlungsId,
'betrag_netto' => 1000.00,
'ust_satz' => 19.0,
'buchungstext' => 'Ausgangsrechnung ' . $nr,
]);
$this->assertGreaterThan(0, $buchungId, 'Buchung mit Zahldatum erfasst');
// Buchung hat das Zahldatum – nicht das Rechnungsdatum
$buchungenNachZahlung = \Steuerkram\Models\Booking::forBeleg($belegId);
$this->assertEquals(1, count($buchungenNachZahlung), 'Genau eine Buchung vorhanden');
$bk = $buchungenNachZahlung[0];
$this->assertEquals($zahlDatum, $bk['buchungs_datum'], 'Buchungsdatum ist das Zahldatum');
$this->assertNotEmpty($bk['buchungs_datum'] !== $rechnungsDatum || $zahlDatum === $rechnungsDatum,
'Zahldatum wurde korrekt gespeichert');
}
}