Merge pull request 'Fix #210: Gezahlte Vorsteuer als Betriebsausgabe in der EÜR (Zeile 57 / Kz 185)' (#211) from fds2610-bot/Steuerkram:main into main
Reviewed-on: https://codeberg.org/fds2610/Steuerkram/pulls/211
This commit is contained in:
commit
be4d54112d
11 changed files with 256 additions and 33 deletions
|
|
@ -52,7 +52,8 @@ seitenübergreifenden Mustern (z. B. `data-modal-open`/`data-modal-close`,
|
|||
## Tests
|
||||
**Regel: Jedes neue Feature bekommt einen eigenen Test.**
|
||||
|
||||
Tests liegen in `tests/` und folgen dem Namensschema `NN_FunktionsnameTest.php`.
|
||||
Tests liegen in `tests/` und folgen dem Namensschema `NNN_FunktionsnameTest.php`
|
||||
(zwei- oder dreistellige Nummer; 01–99 sind vergeben, neue Tests beginnen bei 100).
|
||||
Jede Testklasse erweitert `TestCase` und implementiert `run()`.
|
||||
|
||||
```bash
|
||||
|
|
@ -66,9 +67,9 @@ unterhalb von `data/` Dateien an, die Apache/PHP später nicht mehr beschreiben
|
|||
kann – ein echter Beleg-Upload scheitert dann mit HTTP 500.
|
||||
|
||||
### Neuen Test erstellen
|
||||
1. Datei `tests/NN_MeinFeatureTest.php` anlegen (NN = nächste freie Nummer)
|
||||
1. Datei `tests/NNN_MeinFeatureTest.php` anlegen (NNN = nächste freie Nummer)
|
||||
2. Klasse `MeinFeatureTest extends TestCase`
|
||||
3. `protected string $testName = 'Test NN: Beschreibung';`
|
||||
3. `protected string $testName = 'Test NNN: Beschreibung';`
|
||||
4. Methode `public function run(): void` mit Assertions implementieren
|
||||
5. Transaktions-Isolation ist automatisch (setUp/tearDown in TestCase)
|
||||
6. Dateien und Verzeichnisse **immer** über `TestCase` anlegen – der Rollback
|
||||
|
|
|
|||
|
|
@ -2,6 +2,15 @@
|
|||
|
||||
---
|
||||
|
||||
## 2026-09-20 – Gezahlte Vorsteuer als Betriebsausgabe in der EÜR (EÜR-Zeile 57 / Kz 185)
|
||||
|
||||
- **`migrations/095_vorsteuerkonten_betriebsausgabe.sql`** (neu): Die Konten 1571/1574/1576 (abziehbare Vorsteuer) lagen als `Zahlungskonto` im Kontenrahmen und wurden vom `EuerCalculator` daher ignoriert – die auf ihnen gebuchte Vorsteuer tauchte in der EÜR nirgends auf. Da die vereinnahmte USt seit Migration 076 als Betriebseinnahme (Zeile 17) und die ans Finanzamt gezahlte USt seit Migration 077 als Betriebsausgabe (Zeile 58) geführt werden, war die Bruttomethode unvollständig und der ausgewiesene Gewinn um die gezahlte Vorsteuer zu hoch. Alle drei Konten sind jetzt `Betriebsausgabe` mit `euer_zeile = '57'`. Auch 1574 (Vorsteuer aus ig. Erwerb) muss mit: sein Gegenkonto 1774 ist seit Migration 076 Betriebseinnahme, bei Reverse-Charge zählte also nur die Erwerbsteuer als Einnahme – die Buchung war entgegen der Absicht nicht ergebnisneutral.
|
||||
- **`docs/berechnungslogik.md`:** ergänzt, dass die Summenbildung allein über `kontenart` läuft und `euer_zeile` nur beim Wert `'74'` ausgewertet wird; alle übrigen Zeilennummern sind dokumentarisch.
|
||||
- **`src/Views/accounts/form.php`:** Die EÜR-Zeilen 57 und 58 tragen jetzt zusätzlich ihre ELSTER-Kennzahl im Label (`57 (Kz 185) – …`, `58 (Kz 186) – …`); „185" war bislang nirgends auffindbar, obwohl die amtliche Anlage EÜR die Position so benennt. Außerdem wurde die gespeicherte EÜR-Zeile nie vorausgewählt: PHP wandelt numerische Array-Keys in `int` um, der strikte Vergleich mit dem `string` aus der Datenbank schlug deshalb immer fehl.
|
||||
- **`tests/100_VorsteuerEuerZeileTest.php`** (neu): prüft Kontenstamm, Auswahlliste und dass eine Ausgabe über 1.000 € mit 19 % die Betriebsausgaben um 1.190 € (Netto + Vorsteuer) erhöht.
|
||||
- **`tests/80_DashboardEuerAbgleichTest.php`:** Erwartungen an die neue Zuordnung angepasst – die Vorsteuer zählt jetzt in Kachel und EÜR gleichermaßen als Betriebsausgabe.
|
||||
- **`tests/run.php`:** Die Testsuite hat die 99 aufgebraucht; der Dateiname-Glob akzeptiert jetzt auch dreistellige Nummern und sortiert numerisch statt alphabetisch.
|
||||
|
||||
## 2026-09-20 – Fehlende Admin-Prüfung bei GET /settings (#206)
|
||||
|
||||
- **`src/Controllers/SettingsController.php`:** `show()` prüfte keine Rolle, sodass jeder eingeloggte Nicht-Admin IBAN/BIC, Steuernummer, Nextcloud- und SMTP-Daten einsehen konnte. `Auth::requireAdmin()` ergänzt.
|
||||
|
|
|
|||
|
|
@ -13,6 +13,13 @@ Die EÜR basiert auf dem Feld `accounts.kontenart` sowie `accounts.euer_zeile` d
|
|||
|
||||
### Spaltenauswahl nach Kontenart und euer_zeile
|
||||
|
||||
Maßgeblich für die Summenbildung ist allein `kontenart`. `euer_zeile` wird von
|
||||
`EuerCalculator` nur an einer Stelle ausgewertet – beim Wert `'74'`, der eine
|
||||
Betriebsausgabe in den Abschnitt „nicht abzugsfähig" umlenkt. Alle übrigen
|
||||
Zeilennummern sind dokumentarisch: sie erscheinen in der Kontenliste und
|
||||
benennen die Position der amtlichen Anlage EÜR, ändern aber nichts an der
|
||||
Berechnung.
|
||||
|
||||
| kontenart | euer_zeile | EÜR-Abschnitt |
|
||||
|---|---|---|
|
||||
| `Betriebseinnahme` | beliebig | Betriebseinnahmen |
|
||||
|
|
@ -20,7 +27,23 @@ Die EÜR basiert auf dem Feld `accounts.kontenart` sowie `accounts.euer_zeile` d
|
|||
| `Betriebsausgabe` | `'74'` | Nicht abzugsfähige Betriebsausgaben – eigener Abschnitt, **nicht** in `total_ausgaben` oder `gewinn` |
|
||||
| `Anlagevermögen` | beliebig | nicht in EÜR (die Anschaffung wirkt nur über die AfA) |
|
||||
| `Privat` | beliebig | nicht in EÜR (Privatentnahmen/-einlagen beeinflussen den Gewinn nicht) |
|
||||
| `Zahlungskonto`, `Verbindlichkeit` | beliebig | nicht in EÜR (auch Vorsteuerkonten wie 1571/1576) |
|
||||
| `Zahlungskonto`, `Verbindlichkeit` | beliebig | nicht in EÜR |
|
||||
|
||||
Die EÜR wird nach der **Bruttomethode** gerechnet: Die Umsatzsteuer läuft als Einnahme bzw. Ausgabe mit. Dafür sind drei Kontengruppen umklassifiziert:
|
||||
|
||||
| Konten | kontenart | euer_zeile | Bedeutung |
|
||||
|---|---|---|---|
|
||||
| 1770, 1771, 1774, 1776 | `Betriebseinnahme` | `17` | Vereinnahmte Umsatzsteuer (Migration 076) |
|
||||
| 1571, 1574, 1576 | `Betriebsausgabe` | `57` | Gezahlte Vorsteuer, ELSTER-Kennzahl 185 (Migration 095) |
|
||||
| 1780, 1781 | `Betriebsausgabe` | `58` | An das Finanzamt gezahlte USt (Migration 077) |
|
||||
|
||||
Bei Reverse-Charge (`steuerart = 'ig_erwerb_19'`) tragen die beiden Kindzeilen auf
|
||||
1574 (Vorsteuer) und 1774 (Erwerbsteuer) denselben Betrag; als Betriebsausgabe und
|
||||
Betriebseinnahme heben sie sich im Gewinn auf. Deshalb müssen beide Konten
|
||||
klassifiziert sein – bliebe eines davon `Zahlungskonto`, wäre die Buchung nicht
|
||||
mehr ergebnisneutral.
|
||||
|
||||
Die Vorsteuerbeträge stammen aus den VSt-Buchungszeilen, die `Booking::create()` als Kindzeile (`parent_booking_id`) zu jeder Ausgabe mit 7 % oder 19 % anlegt.
|
||||
|
||||
**EÜR-Zeile 74** entspricht den „Nicht abziehbaren Betriebsausgaben nach § 4 Abs. 5 EStG" (z.B. nicht abzugsfähige Bewirtungskosten auf Konten 4654, 4656, 4657; nicht abzugsfähige Geschenke auf 4635, 4636; nicht abzugsfähiger Arbeitszimmeranteil auf 4289; seit Migration 075). Diese Konten werden im EÜR-Bericht unterhalb der Betriebsausgaben in einem separaten Abschnitt angezeigt.
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ Vollständige Kontenliste: [kontenrahmen.md](kontenrahmen.md)
|
|||
|
||||
Fünf Kacheln: Umsatz, Ausgaben, Gewinn/Verlust (jeweils Jahr, Vormonat und laufender Monat), offene Rechnungen und ungebuchte Belege. Darunter eine Ablagefläche für Belege per Drag & Drop sowie die zehn zuletzt erfassten Buchungen.
|
||||
|
||||
- **Zuordnung wie in der EÜR**: Umsatz, Ausgaben und Ergebnis richten sich nach der Kontenart des Kategorie-Kontos, nicht nach dem Buchungstyp. AfA-Buchungen zählen als Betriebsausgabe; Vorsteuer-, Anlage- und Privatkonten bleiben außen vor, nicht abzugsfähige Ausgaben (EÜR-Zeile 74) mindern das Ergebnis nicht. Die Kacheln stimmen damit mit der EÜR desselben Jahres überein.
|
||||
- **Zuordnung wie in der EÜR**: Umsatz, Ausgaben und Ergebnis richten sich nach der Kontenart des Kategorie-Kontos, nicht nach dem Buchungstyp. AfA-Buchungen zählen als Betriebsausgabe; die gezahlte Vorsteuer ebenfalls (EÜR-Zeile 57, Kz 185 – die EÜR rechnet nach der Bruttomethode), Anlage- und Privatkonten bleiben außen vor, nicht abzugsfähige Ausgaben (EÜR-Zeile 74) mindern das Ergebnis nicht. Die Kacheln stimmen damit mit der EÜR desselben Jahres überein.
|
||||
- **Detailansicht je Zahl** (`/dashboard/buchungen/{einnahmen|ausgaben|ergebnis}/{jahr}[/{monat}]`): Jede der neun Zahlen der ersten drei Kacheln führt auf die Einzelbuchungen, aus denen sie besteht – mit Belegverweis, Konten und Summenzeile. In der Ergebnisansicht werden Betriebsausgaben mit negativem Vorzeichen ausgewiesen, damit die Spaltensumme dem Kachelwert entspricht.
|
||||
|
||||
---
|
||||
|
|
@ -124,7 +124,7 @@ Zuordnungsregeln: [berechnungslogik.md](berechnungslogik.md)
|
|||
## EÜR (Einnahmen-Überschuss-Rechnung)
|
||||
|
||||
- Jahresauswertung: Einnahmen, Ausgaben, Gewinn je Kategorie-Konto
|
||||
- Nicht abzugsfähige Betriebsausgaben (§ 4 Abs. 5 EStG, EÜR-Zeile 51) in separatem Abschnitt
|
||||
- Nicht abzugsfähige Betriebsausgaben (§ 4 Abs. 5 EStG, EÜR-Zeile 74) in separatem Abschnitt
|
||||
- PDF-Export
|
||||
- Drill-down: Klick auf ein Konto zeigt alle zugehörigen Buchungszeilen
|
||||
|
||||
|
|
|
|||
|
|
@ -54,15 +54,22 @@ Der kanonische Seed liegt in `migrations/018_seed_kontenrahmen_skr03_final.sql`.
|
|||
| 1240 | Bank 5 | Zahlungskonto |
|
||||
| 1390 | Verrechnungskonto Ist-Versteuerung | Zahlungskonto |
|
||||
|
||||
### Umsatzsteuerkonten
|
||||
### Umsatzsteuer- und Vorsteuerkonten
|
||||
|
||||
| Kto. | Bezeichnung | Kontenart |
|
||||
|---|---|---|
|
||||
| 1770 | Umsatzsteuer | Zahlungskonto |
|
||||
| 1771 | Umsatzsteuer 7 % | Zahlungskonto |
|
||||
| 1776 | Umsatzsteuer 19 % | Zahlungskonto |
|
||||
| 1780 | Umsatzsteuer-Vorauszahlungen lfd. Jahr | Zahlungskonto |
|
||||
| 1781 | Umsatzsteuer-Vorauszahlungen 1/11 | Zahlungskonto |
|
||||
Die EÜR rechnet nach der Bruttomethode, deshalb sind diese Konten als
|
||||
Betriebseinnahme bzw. Betriebsausgabe klassifiziert (Migrationen 076, 077, 095).
|
||||
|
||||
| Kto. | Bezeichnung | Kontenart | EÜR-Zeile |
|
||||
|---|---|---|---|
|
||||
| 1571 | Abziehbare Vorsteuer 7 % | Betriebsausgabe | 57 (Kz 185) |
|
||||
| 1574 | Abziehbare Vorsteuer ig. Erwerb 19 % | Betriebsausgabe | 57 (Kz 185) |
|
||||
| 1576 | Abziehbare Vorsteuer 19 % (Eingangsrechnungen) | Betriebsausgabe | 57 (Kz 185) |
|
||||
| 1770 | Umsatzsteuer | Betriebseinnahme | 17 |
|
||||
| 1771 | Umsatzsteuer 7 % | Betriebseinnahme | 17 |
|
||||
| 1774 | Umsatzsteuer ig. Erwerb 19 % | Betriebseinnahme | 17 |
|
||||
| 1776 | Umsatzsteuer 19 % | Betriebseinnahme | 17 |
|
||||
| 1780 | Umsatzsteuer-Vorauszahlungen lfd. Jahr | Betriebsausgabe | 58 (Kz 186) |
|
||||
| 1781 | Umsatzsteuer-Vorauszahlungen 1/11 | Betriebsausgabe | 58 (Kz 186) |
|
||||
|
||||
### Privatkonten
|
||||
|
||||
|
|
|
|||
24
migrations/095_vorsteuerkonten_betriebsausgabe.sql
Normal file
24
migrations/095_vorsteuerkonten_betriebsausgabe.sql
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
-- 095_vorsteuerkonten_betriebsausgabe.sql
|
||||
-- Vorsteuerkonten als Betriebsausgabe klassifizieren und EÜR-Zeile 57
|
||||
-- zuweisen (Anlage EÜR, Kennzahl 185: "Gezahlte und nach § 15 UStG
|
||||
-- abziehbare Vorsteuerbeträge").
|
||||
--
|
||||
-- Hintergrund: Die EÜR wird nach der Bruttomethode gerechnet. Migration 076
|
||||
-- hat die vereinnahmte Umsatzsteuer als Betriebseinnahme (Zeile 17) erfasst,
|
||||
-- Migration 077 die an das Finanzamt gezahlte Umsatzsteuer als Betriebsausgabe
|
||||
-- (Zeile 58). Die gezahlte Vorsteuer fehlte bisher – sie lag auf Konten der
|
||||
-- Kontenart "Zahlungskonto" und wurde vom EuerCalculator ignoriert, wodurch
|
||||
-- der ausgewiesene Gewinn um die Vorsteuer zu hoch war.
|
||||
--
|
||||
-- Betroffen sind alle drei Vorsteuerkonten: 1571/1576 (VSt-Buchungszeilen aus
|
||||
-- Migration 045) und 1574 (Vorsteuer aus innergemeinschaftlichem Erwerb). Auch
|
||||
-- 1574 muss mit, weil das Gegenkonto 1774 (Erwerbsumsatzsteuer) seit
|
||||
-- Migration 076 Betriebseinnahme ist: bleibt 1574 ein Zahlungskonto, zählt bei
|
||||
-- Reverse-Charge-Buchungen nur die Erwerbsteuer als Einnahme und der Gewinn ist
|
||||
-- um diesen Betrag zu hoch. Als Betriebsausgabe heben sich die beiden
|
||||
-- Kindzeilen – sie tragen denselben Betrag – wie vorgesehen auf.
|
||||
|
||||
UPDATE accounts
|
||||
SET kontenart = 'Betriebsausgabe',
|
||||
euer_zeile = '57'
|
||||
WHERE kontonummer IN ('1571', '1574', '1576');
|
||||
|
|
@ -116,8 +116,8 @@
|
|||
'54' => '54 – Werbekosten (Inserate, Werbespots, Plakate)',
|
||||
'55' => '55 – Schuldzinsen zur Finanzierung von Anschaffungs-/Herstellungskosten AV',
|
||||
'56' => '56 – Übrige Schuldzinsen',
|
||||
'57' => '57 – Gezahlte und nach § 15 UStG abziehbare Vorsteuerbeträge',
|
||||
'58' => '58 – An das Finanzamt gezahlte/verrechnete Umsatzsteuer',
|
||||
'57' => '57 (Kz 185) – Gezahlte und nach § 15 UStG abziehbare Vorsteuerbeträge',
|
||||
'58' => '58 (Kz 186) – An das Finanzamt gezahlte/verrechnete Umsatzsteuer',
|
||||
'60' => '60 – Übrige unbeschränkt abziehbare Betriebsausgaben',
|
||||
// Beschränkt abziehbare Betriebsausgaben (Zeilen 62–67)
|
||||
'62' => '62 – Geschenke (abziehbarer Anteil)',
|
||||
|
|
@ -137,12 +137,12 @@
|
|||
];
|
||||
foreach ($euerZeilen as $val => $label):
|
||||
?>
|
||||
<option value="<?= $val ?>"<?= ($account['euer_zeile'] ?? '') === $val ? ' selected' : '' ?>>
|
||||
<option value="<?= $val ?>"<?= ($account['euer_zeile'] ?? '') === (string)$val ? ' selected' : '' ?>>
|
||||
<?= $h($label) ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<div class="form-hint">Zeilen entsprechen der amtlichen Anlage EÜR 2025</div>
|
||||
<div class="form-hint">Zeilen entsprechen der amtlichen Anlage EÜR 2025; „Kz“ ist die ELSTER-Kennzahl</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
1-260821
|
||||
1-260920
|
||||
|
|
|
|||
157
tests/100_VorsteuerEuerZeileTest.php
Normal file
157
tests/100_VorsteuerEuerZeileTest.php
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Test 100: Gezahlte Vorsteuer als Betriebsausgabe in der EÜR.
|
||||
*
|
||||
* Die EÜR wird nach der Bruttomethode gerechnet: vereinnahmte USt ist
|
||||
* Betriebseinnahme (Zeile 17), an das Finanzamt gezahlte USt ist
|
||||
* Betriebsausgabe (Zeile 58) – und die gezahlte Vorsteuer gehört als
|
||||
* Betriebsausgabe in Zeile 57 (ELSTER-Kennzahl 185, Migration 095).
|
||||
*
|
||||
* Maßgeblich für die Summenbildung ist dabei `kontenart`; `euer_zeile` wertet
|
||||
* der EuerCalculator nur beim Wert '74' aus. Der Test prüft trotzdem beides,
|
||||
* weil die Zeilennummer die Position der amtlichen Anlage EÜR dokumentiert.
|
||||
*/
|
||||
class VorsteuerEuerZeileTest extends TestCase
|
||||
{
|
||||
protected string $testName = 'Test 100: Vorsteuer in EÜR-Zeile 57 (Kz 185)';
|
||||
|
||||
public function run(): void
|
||||
{
|
||||
// ── 1. Kontenstamm: Vorsteuerkonten sind Betriebsausgabe, Zeile 57 ──
|
||||
foreach (['1571', '1574', '1576'] as $nr) {
|
||||
$kto = \Steuerkram\Database::fetchOne(
|
||||
'SELECT kontenart, euer_zeile FROM accounts WHERE kontonummer = ?',
|
||||
[$nr]
|
||||
);
|
||||
$this->assertNotEmpty($kto, "Vorsteuerkonto $nr existiert");
|
||||
$this->assertEquals('Betriebsausgabe', $kto['kontenart'] ?? null, "Konto $nr ist Betriebsausgabe");
|
||||
$this->assertEquals('57', $kto['euer_zeile'] ?? null, "Konto $nr hat euer_zeile=57");
|
||||
}
|
||||
|
||||
// ── 2. Auswahlliste im Kontenformular führt Zeile 57 mit Kennzahl 185 ──
|
||||
$form = (string)file_get_contents(__DIR__ . '/../src/Views/accounts/form.php');
|
||||
$this->assert(
|
||||
str_contains($form, "'57' => '57 (Kz 185)"),
|
||||
'Kontenformular bietet EÜR-Zeile 57 mit ELSTER-Kennzahl 185 an'
|
||||
);
|
||||
$this->assert(
|
||||
str_contains($form, "(string)\$val ? ' selected' : ''"),
|
||||
'Gespeicherte EÜR-Zeile wird im Formular vorausgewählt (String-Vergleich)'
|
||||
);
|
||||
|
||||
// ── 3. Vorsteuer einer Ausgabe landet in der EÜR-Ausgabenspalte ──────
|
||||
$datum = date('Y-m-d');
|
||||
$jahr = (int)date('Y');
|
||||
$bankId = $this->findAccountId('1200');
|
||||
$kostenId = $this->findAccountId('4600');
|
||||
|
||||
if (!$bankId || !$kostenId) {
|
||||
$this->assert(false, 'Benötigte Konten (1200, 4600) nicht gefunden');
|
||||
return;
|
||||
}
|
||||
|
||||
$vorher = (new \Steuerkram\Services\EuerCalculator())->calculate($jahr);
|
||||
|
||||
$beleg = \Steuerkram\Models\Beleg::create([
|
||||
'beleg_nr' => 'TEST-VST185-' . uniqid(),
|
||||
'filename' => 'test.pdf',
|
||||
'storage_path' => '',
|
||||
'mime_type' => 'application/pdf',
|
||||
'file_hash' => hash('sha256', 'vst185' . uniqid()),
|
||||
'beleg_datum' => $datum,
|
||||
'belegtyp' => 'Eingangsrechnung',
|
||||
]);
|
||||
\Steuerkram\Models\Booking::create([
|
||||
'beleg_id' => $beleg,
|
||||
'buchungs_datum' => $datum,
|
||||
'buchungstyp' => 'Ausgabe',
|
||||
'kategorie_konto_id' => $kostenId,
|
||||
'zahlungskonto_id' => $bankId,
|
||||
'betrag_netto' => 1000.00,
|
||||
'ust_satz' => 19.0,
|
||||
'buchungstext' => 'Test Ausgabe mit Vorsteuer',
|
||||
]);
|
||||
|
||||
$nachher = (new \Steuerkram\Services\EuerCalculator())->calculate($jahr);
|
||||
|
||||
$ausKonten = array_column($nachher['ausgaben'], 'kontonummer');
|
||||
$this->assert(
|
||||
in_array('1576', $ausKonten, true),
|
||||
'Konto 1576 erscheint als Betriebsausgabe in der EÜR'
|
||||
);
|
||||
|
||||
// Netto 1000 + Vorsteuer 190 = 1190 mehr Betriebsausgaben
|
||||
$delta = round((float)$nachher['total_ausgaben'] - (float)$vorher['total_ausgaben'], 2);
|
||||
$this->assertEquals(1190.00, $delta, 'Betriebsausgaben steigen um Netto + Vorsteuer');
|
||||
|
||||
// Vorsteuer darf nicht im Abschnitt "nicht abzugsfähig" landen
|
||||
$naKonten = array_column($nachher['nicht_abzugsfaehig'], 'kontonummer');
|
||||
$this->assert(
|
||||
!in_array('1576', $naKonten, true),
|
||||
'Konto 1576 erscheint nicht unter den nicht abzugsfähigen Ausgaben'
|
||||
);
|
||||
|
||||
// ── 4. Reverse-Charge bleibt ergebnisneutral ────────────────────────
|
||||
// Die Kindzeilen auf 1574 (Vorsteuer) und 1774 (Erwerbsteuer) tragen
|
||||
// denselben Betrag: als Betriebsausgabe bzw. Betriebseinnahme heben sie
|
||||
// sich auf. Bliebe 1574 ein Zahlungskonto, wäre der Gewinn zu hoch.
|
||||
$ige = \Steuerkram\Database::fetchOne(
|
||||
"SELECT kontenart, euer_zeile FROM accounts WHERE kontonummer = '1774'"
|
||||
);
|
||||
$this->assertEquals('Betriebseinnahme', $ige['kontenart'] ?? null, 'Konto 1774 ist Betriebseinnahme');
|
||||
|
||||
$vorIge = (new \Steuerkram\Services\EuerCalculator())->calculate($jahr);
|
||||
|
||||
$belegIge = \Steuerkram\Models\Beleg::create([
|
||||
'beleg_nr' => 'TEST-IGE185-' . uniqid(),
|
||||
'filename' => 'test.pdf',
|
||||
'storage_path' => '',
|
||||
'mime_type' => 'application/pdf',
|
||||
'file_hash' => hash('sha256', 'ige185' . uniqid()),
|
||||
'beleg_datum' => $datum,
|
||||
'belegtyp' => 'Eingangsrechnung',
|
||||
]);
|
||||
\Steuerkram\Models\Booking::create([
|
||||
'beleg_id' => $belegIge,
|
||||
'buchungs_datum' => $datum,
|
||||
'buchungstyp' => 'Ausgabe',
|
||||
'kategorie_konto_id' => $kostenId,
|
||||
'zahlungskonto_id' => $bankId,
|
||||
'betrag_netto' => 500.00,
|
||||
'ust_satz' => 0.0,
|
||||
'steuerart' => 'ig_erwerb_19',
|
||||
'buchungstext' => 'Test ig. Erwerb',
|
||||
]);
|
||||
|
||||
$nachIge = (new \Steuerkram\Services\EuerCalculator())->calculate($jahr);
|
||||
|
||||
// Nur die Netto-Ausgabe von 500 € darf den Gewinn mindern, die 95 €
|
||||
// Erwerbsteuer stehen auf beiden Seiten.
|
||||
$this->assertEquals(
|
||||
round((float)$vorIge['gewinn'] - 500.00, 2),
|
||||
round((float)$nachIge['gewinn'], 2),
|
||||
'Erwerbsteuer und Vorsteuer aus ig. Erwerb heben sich im Gewinn auf'
|
||||
);
|
||||
$this->assertEquals(
|
||||
round((float)$vorIge['total_einnahmen'] + 95.00, 2),
|
||||
round((float)$nachIge['total_einnahmen'], 2),
|
||||
'Erwerbsteuer (1774) erhöht die Betriebseinnahmen um 95 €'
|
||||
);
|
||||
$this->assertEquals(
|
||||
round((float)$vorIge['total_ausgaben'] + 595.00, 2),
|
||||
round((float)$nachIge['total_ausgaben'], 2),
|
||||
'Netto + Vorsteuer ig. Erwerb (1574) erhöhen die Betriebsausgaben um 595 €'
|
||||
);
|
||||
|
||||
// ── 5. Dashboard-Kacheln (summary) rechnen identisch ────────────────
|
||||
$summary = (new \Steuerkram\Services\EuerCalculator())->summary($jahr);
|
||||
$this->assertEquals(
|
||||
(float)$nachIge['total_ausgaben'],
|
||||
(float)$summary['ausgaben'],
|
||||
'summary() und calculate() liefern dieselben Betriebsausgaben'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -9,11 +9,12 @@ use Steuerkram\Services\EuerCalculator;
|
|||
* Test 80: Dashboard-Kacheln stimmen mit der EÜR überein (Issue #167)
|
||||
*
|
||||
* Das Dashboard summierte bisher nach bookings.buchungstyp, die EÜR nach
|
||||
* accounts.kontenart. Dadurch zählten Vorsteuer- und Anlagenbuchungen als
|
||||
* Betriebsausgabe, AfA-Buchungen fielen ganz heraus und nicht abzugsfähige
|
||||
* Kosten minderten das angezeigte Ergebnis. Geprüft wird, dass beide
|
||||
* Auswertungen jetzt dieselbe Zuordnung verwenden und dass die Detailliste
|
||||
* hinter einer Kachel exakt auf deren Summe kommt.
|
||||
* accounts.kontenart. Dadurch zählten Anlagenbuchungen als Betriebsausgabe,
|
||||
* AfA-Buchungen fielen ganz heraus und nicht abzugsfähige Kosten minderten
|
||||
* das angezeigte Ergebnis. Geprüft wird, dass beide Auswertungen jetzt
|
||||
* dieselbe Zuordnung verwenden und dass die Detailliste hinter einer Kachel
|
||||
* exakt auf deren Summe kommt. Die gezahlte Vorsteuer ist seit Migration 095
|
||||
* Betriebsausgabe (EÜR-Zeile 57) und zählt in beiden Auswertungen mit.
|
||||
*/
|
||||
class DashboardEuerAbgleichTest extends TestCase
|
||||
{
|
||||
|
|
@ -27,7 +28,7 @@ class DashboardEuerAbgleichTest extends TestCase
|
|||
$erloese = $this->findAccountId('8400'); // Betriebseinnahme
|
||||
$buerobedarf = $this->findAccountId('4930'); // Betriebsausgabe
|
||||
$bewirtung = $this->findAccountId('4654'); // Betriebsausgabe, euer_zeile 74
|
||||
$vorsteuer = $this->findAccountId('1576'); // Zahlungskonto
|
||||
$vorsteuer = $this->findAccountId('1576'); // Betriebsausgabe, euer_zeile 57
|
||||
$bank = $this->findAccountId('1200'); // Zahlungskonto
|
||||
|
||||
$this->assertNotEmpty($erloese, 'Erlöskonto 8400 vorhanden');
|
||||
|
|
@ -48,7 +49,7 @@ class DashboardEuerAbgleichTest extends TestCase
|
|||
$this->createBooking($belegId, $datum, 'Ausgabe', $buerobedarf, $bank, 200.00);
|
||||
// AfA 100,00 – fiel im alten Dashboard komplett unter den Tisch
|
||||
$this->createBooking($belegId, $datum, 'AfA', $buerobedarf, $bank, 100.00);
|
||||
// Vorsteuer 38,00 – wurde im alten Dashboard als Ausgabe mitgezählt
|
||||
// Vorsteuer 38,00 – Betriebsausgabe nach EÜR-Zeile 57 (Kz 185)
|
||||
$this->createBooking($belegId, $datum, 'Ausgabe', $vorsteuer, $bank, 38.00);
|
||||
// Nicht abzugsfähige Bewirtung 30,00 – mindert das Ergebnis nicht
|
||||
$this->createBooking($belegId, $datum, 'Ausgabe', $bewirtung, $bank, 30.00);
|
||||
|
|
@ -61,9 +62,9 @@ class DashboardEuerAbgleichTest extends TestCase
|
|||
'Einnahmen steigen um genau den Erlös'
|
||||
);
|
||||
$this->assertEquals(
|
||||
round($vorher['ausgaben'] + 300.00, 2),
|
||||
round($vorher['ausgaben'] + 338.00, 2),
|
||||
$nachher['ausgaben'],
|
||||
'Ausgaben enthalten Aufwand und AfA, aber weder Vorsteuer noch nicht abzugsfähige Kosten'
|
||||
'Ausgaben enthalten Aufwand, AfA und Vorsteuer, aber keine nicht abzugsfähigen Kosten'
|
||||
);
|
||||
$this->assertEquals(
|
||||
round($vorher['nicht_abzugsfaehig'] + 30.00, 2),
|
||||
|
|
@ -71,7 +72,7 @@ class DashboardEuerAbgleichTest extends TestCase
|
|||
'Nicht abzugsfähige Kosten werden getrennt ausgewiesen'
|
||||
);
|
||||
$this->assertEquals(
|
||||
round($vorher['ergebnis'] + 700.00, 2),
|
||||
round($vorher['ergebnis'] + 662.00, 2),
|
||||
$nachher['ergebnis'],
|
||||
'Ergebnis = Einnahmen − Ausgaben'
|
||||
);
|
||||
|
|
@ -98,9 +99,9 @@ class DashboardEuerAbgleichTest extends TestCase
|
|||
);
|
||||
}
|
||||
|
||||
// Keine Vorsteuer- und keine nicht abzugsfähige Buchung in der Ausgabenliste
|
||||
// Vorsteuer gehört in die Ausgabenliste, nicht abzugsfähige Kosten nicht
|
||||
$kontonummern = array_column($calc->fetchBookingsForBucket($jahr, null, 'ausgaben'), 'kategorie_nr');
|
||||
$this->assert(!in_array('1576', $kontonummern, true), 'Vorsteuerkonto fehlt in der Ausgabenliste');
|
||||
$this->assert(in_array('1576', $kontonummern, true), 'Vorsteuerkonto steht in der Ausgabenliste');
|
||||
$this->assert(!in_array('4654', $kontonummern, true), 'Nicht abzugsfähige Bewirtung fehlt in der Ausgabenliste');
|
||||
|
||||
// Monatsfilter greift
|
||||
|
|
|
|||
|
|
@ -81,8 +81,9 @@ function neuePfade(array $vorher, array $nachher): array
|
|||
}
|
||||
|
||||
// Alle Test-Dateien laden
|
||||
$testFiles = glob(__DIR__ . '/[0-9][0-9]_*.php');
|
||||
sort($testFiles);
|
||||
$testFiles = glob(__DIR__ . '/[0-9][0-9]*_*.php');
|
||||
// Numerisch sortieren, damit 100_ hinter 99_ einsortiert wird
|
||||
usort($testFiles, fn(string $a, string $b): int => (int)basename($a) <=> (int)basename($b));
|
||||
|
||||
$testClasses = [];
|
||||
foreach ($testFiles as $file) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue