diff --git a/src/Models/Booking.php b/src/Models/Booking.php
index b04cec5..b34f265 100644
--- a/src/Models/Booking.php
+++ b/src/Models/Booking.php
@@ -163,10 +163,10 @@ class Booking
$reversalData['buchungstyp'],
$reversalData['kategorie_konto_id'],
$reversalData['zahlungskonto_id'],
- $orig['betrag_netto'],
+ -1 * (float)$orig['betrag_netto'],
$orig['ust_satz'],
- $orig['betrag_ust'],
- $orig['betrag_brutto'],
+ -1 * (float)$orig['betrag_ust'],
+ -1 * (float)$orig['betrag_brutto'],
$reversalData['buchungstext'],
$fy['id'],
]
diff --git a/src/Services/EuerCalculator.php b/src/Services/EuerCalculator.php
index 5ea807c..86ccc16 100644
--- a/src/Services/EuerCalculator.php
+++ b/src/Services/EuerCalculator.php
@@ -35,7 +35,6 @@ class EuerCalculator
JOIN accounts a ON bk.kategorie_konto_id = a.id
JOIN fiscal_years fy ON bk.fiscal_year_id = fy.id
WHERE fy.year = ?
- AND bk.is_reversal = 0
AND a.kontenart IN (\'Betriebseinnahme\', \'Betriebsausgabe\')
GROUP BY a.id, a.kontonummer, a.bezeichnung, a.kontenart, a.euer_zeile
HAVING buchungsanzahl > 0
@@ -81,7 +80,7 @@ class EuerCalculator
return Database::fetchAll(
'SELECT bk.id, bk.buchungs_datum, bk.buchungstyp, bk.ust_satz,
bk.betrag_netto, bk.betrag_ust, bk.betrag_brutto, bk.buchungstext,
- bk.beleg_id, bl.beleg_nr,
+ bk.is_reversal, bk.beleg_id, bl.beleg_nr,
k.kontonummer AS kategorie_nr, k.bezeichnung AS kategorie_name,
z.kontonummer AS zahlungs_nr, z.bezeichnung AS zahlungs_name
FROM bookings bk
@@ -89,7 +88,7 @@ class EuerCalculator
LEFT JOIN belege bl ON bk.beleg_id = bl.id
JOIN accounts k ON bk.kategorie_konto_id = k.id
JOIN accounts z ON bk.zahlungskonto_id = z.id
- WHERE fy.year = ? AND bk.kategorie_konto_id = ? AND bk.is_reversal = 0
+ WHERE fy.year = ? AND bk.kategorie_konto_id = ?
ORDER BY bk.buchungs_datum, bk.id',
[$year, $kontoId]
);
diff --git a/src/Services/UstvaCalculator.php b/src/Services/UstvaCalculator.php
index a54820b..eece441 100644
--- a/src/Services/UstvaCalculator.php
+++ b/src/Services/UstvaCalculator.php
@@ -61,7 +61,6 @@ class UstvaCalculator
JOIN fiscal_years fy ON bk.fiscal_year_id = fy.id
JOIN accounts a ON a.id = bk.kategorie_konto_id
WHERE fy.year = ? AND MONTH(bk.buchungs_datum) = ?
- AND bk.is_reversal = 0
AND a.ustva_kz IS NOT NULL AND a.ustva_kz != \'\'
GROUP BY a.ustva_kz',
[$year, $month]
@@ -75,8 +74,8 @@ class UstvaCalculator
];
}
- $n = fn(string $kz): float => abs($byKz[$kz]['netto'] ?? 0.0);
- $u = fn(string $kz): float => abs($byKz[$kz]['ust'] ?? 0.0);
+ $n = fn(string $kz): float => (float)($byKz[$kz]['netto'] ?? 0.0);
+ $u = fn(string $kz): float => (float)($byKz[$kz]['ust'] ?? 0.0);
$kz81 = $n('81'); $kz83 = $u('81');
$kz86 = $n('86'); $kz84 = $u('86');
@@ -124,7 +123,7 @@ class UstvaCalculator
return Database::fetchAll(
'SELECT bk.id, bk.buchungs_datum, bk.buchungstyp, bk.steuerart, bk.ust_satz,
bk.betrag_netto, bk.betrag_ust, bk.betrag_brutto, bk.buchungstext,
- bk.beleg_id, bl.beleg_nr,
+ bk.is_reversal, bk.beleg_id, bl.beleg_nr,
k.kontonummer AS kategorie_nr, k.bezeichnung AS kategorie_name,
z.kontonummer AS zahlungs_nr, z.bezeichnung AS zahlungs_name
FROM bookings bk
@@ -133,7 +132,6 @@ class UstvaCalculator
JOIN accounts k ON bk.kategorie_konto_id = k.id
JOIN accounts z ON bk.zahlungskonto_id = z.id
WHERE fy.year = ? AND MONTH(bk.buchungs_datum) = ?
- AND bk.is_reversal = 0
AND k.ustva_kz IN (' . $placeholders . ')
ORDER BY bk.buchungs_datum, bk.id',
$params
diff --git a/src/Views/tax/euer_konto_bookings.php b/src/Views/tax/euer_konto_bookings.php
index b028a69..f5f45b5 100644
--- a/src/Views/tax/euer_konto_bookings.php
+++ b/src/Views/tax/euer_konto_bookings.php
@@ -46,7 +46,7 @@ $fmt = fn($v) => number_format((float)$v, 2, ',', '.') . ' €';
$sumUst += $ust;
$sumBrutto += $brutto;
?>
-
+
>
| = $h($b['buchungs_datum']) ?> |
diff --git a/src/Views/tax/ustva_kz_bookings.php b/src/Views/tax/ustva_kz_bookings.php
index a0cb5e6..47ae919 100644
--- a/src/Views/tax/ustva_kz_bookings.php
+++ b/src/Views/tax/ustva_kz_bookings.php
@@ -20,8 +20,9 @@ $ustBeitragKz69 = function(array $b): float {
};
// Ausgaben werden als negative Beträge angezeigt (Geld fließt ab).
+// Stornobuchungen haben bereits negative Beträge in der DB – kein abs().
$signedBetrag = fn(array $b, string $field): float =>
- $b['buchungstyp'] === 'Ausgabe' ? -abs((float)$b[$field]) : abs((float)$b[$field]);
+ $b['buchungstyp'] === 'Ausgabe' ? -(float)$b[$field] : (float)$b[$field];
?>
@@ -65,7 +66,7 @@ $signedBetrag = fn(array $b, string $field): float =>
$sumUst += $ust;
$sumBrutto += $brutto;
?>
- |
+
>
| = $h($b['buchungs_datum']) ?> |
diff --git a/tests/18_StornoBuchungTest.php b/tests/18_StornoBuchungTest.php
new file mode 100644
index 0000000..0575033
--- /dev/null
+++ b/tests/18_StornoBuchungTest.php
@@ -0,0 +1,104 @@
+findAccountId('1200'); // Bank
+ $ausgabeId = $this->findAccountId('4920'); // Telefon (ustva_kz=61)
+
+ if (!$zahlungsId || !$ausgabeId) {
+ $this->assert(false, 'Benötigte Konten nicht gefunden – SKR03-Seed vorhanden?');
+ return;
+ }
+
+ // ── Beleg anlegen ──────────────────────────────────────────────
+ $belegId = \Steuerkram\Models\Beleg::create([
+ 'beleg_nr' => 'STORNO-TEST-' . uniqid(),
+ 'filename' => 'storno-test.pdf',
+ 'storage_path' => '',
+ 'mime_type' => 'application/pdf',
+ 'file_hash' => hash('sha256', 'storno-test-' . uniqid()),
+ 'beleg_datum' => $datum,
+ 'eingangs_datum' => $datum,
+ 'belegtyp' => 'Ausgangsrechnung',
+ ]);
+
+ // ── Ausgabe-Buchung anlegen (Netto=100, USt 19%=19, Brutto=119) ─
+ $bookingId = \Steuerkram\Models\Booking::create([
+ 'beleg_id' => $belegId,
+ 'buchungs_datum' => $datum,
+ 'buchungstyp' => 'Ausgabe',
+ 'kategorie_konto_id' => $ausgabeId,
+ 'zahlungskonto_id' => $zahlungsId,
+ 'betrag_netto' => 100.00,
+ 'ust_satz' => 19.0,
+ 'buchungstext' => 'Test-Ausgabe für Storno',
+ ]);
+
+ $original = \Steuerkram\Models\Booking::find($bookingId);
+ $this->assertEquals(100.00, round((float)$original['betrag_netto'], 2), 'Originalbuchung: betrag_netto = 100');
+ $this->assertEquals(19.00, round((float)$original['betrag_ust'], 2), 'Originalbuchung: betrag_ust = 19');
+ $this->assertEquals(119.00, round((float)$original['betrag_brutto'], 2),'Originalbuchung: betrag_brutto = 119');
+
+ // ── Stornieren ────────────────────────────────────────────────
+ $reversalId = \Steuerkram\Models\Booking::reverse($bookingId, 'Testfall Storno');
+ $reversal = \Steuerkram\Models\Booking::find($reversalId);
+
+ $this->assertEquals(1, (int)$reversal['is_reversal'], 'Stornobuchung: is_reversal = 1');
+ $this->assertEquals(-100.00, round((float)$reversal['betrag_netto'], 2), 'Stornobuchung: betrag_netto = -100');
+ $this->assertEquals(-19.00, round((float)$reversal['betrag_ust'], 2), 'Stornobuchung: betrag_ust = -19');
+ $this->assertEquals(-119.00, round((float)$reversal['betrag_brutto'], 2), 'Stornobuchung: betrag_brutto = -119');
+
+ // ── EÜR: storniertes Konto hat Netto-Summe = 0 ───────────────
+ $euerCalc = new \Steuerkram\Services\EuerCalculator();
+ $euer = $euerCalc->calculate($year);
+
+ $kontoNetto = null;
+ foreach ($euer['ausgaben'] as $row) {
+ if ((int)$row['id'] === (int)$ausgabeId) {
+ $kontoNetto = (float)$row['netto'];
+ break;
+ }
+ }
+ // Wenn das Konto nicht mehr auftaucht (alle Buchungen = 0), ist das ebenfalls korrekt.
+ if ($kontoNetto !== null) {
+ $this->assertEquals(0.0, round($kontoNetto, 2), 'EÜR: Storno + Original = 0 auf Ausgabe-Konto');
+ } else {
+ $this->assert(true, 'EÜR: Konto taucht bei Summe=0 nicht auf (HAVING buchungsanzahl > 0 greift)');
+ }
+
+ // ── UStVA: Vorsteuer-KZ hebt sich auf ─────────────────────────
+ // Kz 61 enthält Vorsteuer aus dem Ausgabe-Konto (ustva_kz='61').
+ // Original: +19, Storno: -19 → Summe = 0 (oder weniger als andere Tests einbringen).
+ // Da weitere Tests parallel laufen können, prüfen wir nur, dass die Storno-Buchung
+ // als eigenständige Zeile in fetchBookingsForKz erscheint.
+ $ustvaCalc = new \Steuerkram\Services\UstvaCalculator();
+ $bookings = $ustvaCalc->fetchBookingsForKz($year, $month, '61');
+
+ $reversalFound = false;
+ foreach ($bookings as $b) {
+ if ((int)$b['id'] === $reversalId) {
+ $reversalFound = true;
+ $this->assertEquals(1, (int)$b['is_reversal'], 'UStVA-Drilldown: Stornobuchung is_reversal = 1');
+ $this->assertEquals(-19.00, round((float)$b['betrag_ust'], 2), 'UStVA-Drilldown: Storno betrag_ust = -19');
+ break;
+ }
+ }
+ $this->assert($reversalFound, 'UStVA-Drilldown Kz 61: Stornobuchung erscheint in Detailansicht');
+ }
+}
|