Fix: UStVA Vorsteuer-Kz-Aufteilung (Migration 022) + Tests korrigiert

This commit is contained in:
fds2610-bot 2026-04-10 20:33:14 +02:00
parent aa36a16c5a
commit e1f356d0cf
8 changed files with 195 additions and 111 deletions

View file

@ -41,36 +41,29 @@ Die EÜR basiert auf dem Feld `accounts.kontenart` sowie `accounts.euer_zeile` d
**Datei:** `src/Services/UstvaCalculator.php` **Datei:** `src/Services/UstvaCalculator.php`
Die UStVA basiert ausschließlich auf dem Feld `accounts.ustva_kz` des Kategorie-Kontos einer Buchung. Die Felder `buchungstyp` und `steuerart` werden nicht ausgewertet. Stornobuchungen (`is_reversal = 1`) werden ausgeschlossen. Die UStVA kombiniert zwei Quellen (Migration 022):
### Berechnungslogik **Quelle 1 – kontobasiert** (`accounts.ustva_kz`): reguläre Umsätze und Vorsteuer. Buchungen mit EU-Steuerarten (s.u.) werden hier ausgeschlossen.
Pro Monat wird eine einzige Aggregationsabfrage ausgeführt, die `betrag_netto` und `betrag_ust` nach `ustva_kz` des Kategorie-Kontos gruppiert: **Quelle 2 – steuerartbasiert** (`bookings.steuerart`): EU-Sonderfälle (ig. Lieferung, Ausfuhr, ig. Erwerb). Diese Buchungen landen unabhängig vom Kategorie-Konto in den richtigen Kennzahlen.
```sql
SELECT a.ustva_kz, SUM(betrag_netto), SUM(betrag_ust)
FROM bookings JOIN accounts a ON a.id = kategorie_konto_id
WHERE ... AND a.ustva_kz IS NOT NULL
GROUP BY a.ustva_kz
```
### Kontozuordnung und Kennzahlen ### Kontozuordnung und Kennzahlen
| Kz | Bezeichnung | Quelle | Relevante Konten (Beispiele) | | Kz | Bezeichnung | Quelle | Erläuterung |
|---|---|---|---| |---|---|---|---|
| **81** | Steuerpfl. Umsätze 19 % | `SUM(betrag_netto)` WHERE `ustva_kz='81'` | 8000, 8400, 8510, 8570, 8800, 8820, 8922 | | **81** | Steuerpfl. Umsätze 19 % | `SUM(betrag_netto)` WHERE `ustva_kz='81'` | 8400, 8570, 8720, 8801, 8820, 8922 |
| **83** | USt auf steuerpfl. Umsätze 19 % | `SUM(betrag_ust)` WHERE `ustva_kz='81'` | s.o. | | **83** | USt auf steuerpfl. Umsätze 19 % | `SUM(betrag_ust)` WHERE `ustva_kz='81'` | s.o. |
| **86** | Steuerpfl. Umsätze 7 % | `SUM(betrag_netto)` WHERE `ustva_kz='86'` | 8300 | | **86** | Steuerpfl. Umsätze 7 % | `SUM(betrag_netto)` WHERE `ustva_kz='86'` | 8300, 8710, 8921 |
| **84** | USt auf steuerpfl. Umsätze 7 % | `SUM(betrag_ust)` WHERE `ustva_kz='86'` | s.o. | | **84** | USt auf steuerpfl. Umsätze 7 % | `SUM(betrag_ust)` WHERE `ustva_kz='86'` | s.o. |
| **35** | Steuerfreie Umsätze | `SUM(betrag_netto)` WHERE `ustva_kz='35'` | 8100, 8105, 8110, 8160 | | **35** | Steuerfreie Umsätze | `SUM(betrag_netto)` WHERE `ustva_kz='35'` | 8100, 8105, 8110 |
| **41** | Steuerfreie ig. Lieferungen | `SUM(betrag_netto)` WHERE `ustva_kz='41'` | 8120, 8125, 8130 | | **41** | Steuerfreie ig. Lieferungen | kontobasiert (`ustva_kz='41'`) **+** `steuerart='ig_lieferung'` | 8125; oder Buchung mit steuerart |
| **44** | Steuerfreie Ausfuhren | `SUM(betrag_netto)` WHERE `ustva_kz='44'` | 8150 | | **44** | Steuerfreie Ausfuhren | kontobasiert (`ustva_kz='44'`) **+** `steuerart='export'` | 8120; oder Buchung mit steuerart |
| **61** | Vorsteuer 19 % | `SUM(betrag_ust)` WHERE `ustva_kz='61'` | alle Betriebsausgabe-Konten mit `steuercode='VSt19'` (z.B. 3100, 4200ff, 4900ff) | | **61** | Vorsteuer 19 % | `SUM(betrag_ust)` WHERE `ustva_kz='61'` | alle Betriebsausgabe-Konten mit `steuercode='VSt19'` (Migration 022: früher kz=66) |
| **62** | Vorsteuer 7 % | `SUM(betrag_ust)` WHERE `ustva_kz='62'` | alle Betriebsausgabe-Konten mit `steuercode='VSt7'` (z.B. 3110, 4610, 4680) | | **62** | Vorsteuer 7 % | `SUM(betrag_ust)` WHERE `ustva_kz='62'` | alle Betriebsausgabe-Konten mit `steuercode='VSt7'` (Migration 022: früher kz=66) |
| **89** | Ig. Erwerb 19 % – Bemessungsgrundlage | `SUM(betrag_netto)` WHERE `ustva_kz='89'` | 3425 | | **89** | Ig. Erwerb 19 % – Bemessungsgrundlage | `SUM(betrag_netto)` WHERE `steuerart='ig_erwerb_19'` | kein spezielles Konto nötig |
| **91** | USt auf ig. Erwerb 19 % | `SUM(betrag_ust)` WHERE `ustva_kz='89'` | 3425 | | **91** | USt auf ig. Erwerb 19 % | `Kz 89 × 0,19` | berechnet |
| **93** | Ig. Erwerb 7 % – Bemessungsgrundlage | `SUM(betrag_netto)` WHERE `ustva_kz='93'` | 3420 | | **93** | Ig. Erwerb 7 % – Bemessungsgrundlage | `SUM(betrag_netto)` WHERE `steuerart='ig_erwerb_7'` | kein spezielles Konto nötig |
| **94** | USt auf ig. Erwerb 7 % | `SUM(betrag_ust)` WHERE `ustva_kz='93'` | 3420 | | **94** | USt auf ig. Erwerb 7 % | `Kz 93 × 0,07` | berechnet |
### Abgeleitete Kennzahlen ### Abgeleitete Kennzahlen
@ -80,11 +73,10 @@ GROUP BY a.ustva_kz
| **66** | Vorsteuerbeträge gesamt | Kz 61 + Kz 62 + Kz 67 | | **66** | Vorsteuerbeträge gesamt | Kz 61 + Kz 62 + Kz 67 |
| **69** | Verbleibende USt-Vorauszahlung / Erstattung | (Kz 83 + Kz 84 + Kz 91 + Kz 94) − Kz 66 | | **69** | Verbleibende USt-Vorauszahlung / Erstattung | (Kz 83 + Kz 84 + Kz 91 + Kz 94) − Kz 66 |
### Neue Konten in Klasse 3 (Migration 010) ### Migration 022 – Vorsteuer-Kz-Aufteilung
| Konto | Bezeichnung | ustva_kz | Vor Migration 022 trugen alle Vorsteuer-Konten `ustva_kz='66'`, was dazu führte, dass Kz 61, 62 und 66 stets 0 ergaben. Migration 022 setzt:
|---|---|---| - `steuercode='VSt19'` → `ustva_kz='61'`
| 3420 | Innergemeinschaftliche Erwerbe 7 % Vorsteuer | 93 | - `steuercode='VSt7'` → `ustva_kz='62'`
| 3425 | Innergemeinschaftliche Erwerbe 19 % Vorsteuer | 89 |
Buchungen auf ig. Erwerb müssen auf Konto 3420 bzw. 3425 als Kategorie-Konto erfolgen. Kz 66 wird weiterhin rechnerisch als `Kz 61 + Kz 62 + Kz 67` aggregiert.

View file

@ -2,6 +2,16 @@
--- ---
## 2026-04-10 – UStVA Vorsteuer-Kz-Aufteilung (Migration 022)
### Fix
- Migration 022: Vorsteuer-Konten von `ustva_kz='66'` auf `'61'` (VSt19) bzw. `'62'` (VSt7) aufgeteilt – Kz 61, 62 und 66 wurden zuvor stets als 0 ausgewiesen
- `UstvaCalculator`: zweite Abfrage für steuerart-basierte EU-Kennzahlen (ig. Lieferung → Kz 41, Ausfuhr → Kz 44, ig. Erwerb → Kz 89/91/93/94); verhindert Doppelzählung gegenüber kontobasierter Berechnung
- `fetchBookingsForKz`: UNION-Abfrage aus Konto-Kz und steuerart für korrekten Drill-down
- Tests 4, 5, 6, 9: Kontonummern auf tatsächliche SKR03-Nummern korrigiert (8000→8400, 1400→1200)
---
## 2026-04-10 – Upload-Limit auf 20 MB erhöht ## 2026-04-10 – Upload-Limit auf 20 MB erhöht
### Geändert ### Geändert

View file

@ -0,0 +1,17 @@
-- Migration 022: Vorsteuer-Konten ustva_kz von '66' auf '61' (VSt19) bzw. '62' (VSt7) aufteilen.
--
-- Hintergrund: Der UstvaCalculator berechnet Kz 66 (Vorsteuer gesamt) als Summe
-- aus Kz 61 + Kz 62 + Kz 67. Alle Vorsteuer-Konten hatten bisher ustva_kz='66',
-- was dazu führte, dass Kz 61, 62 und damit auch 66 stets 0 ergaben.
-- Nach dieser Migration tragen Konten mit VSt19-Steuercode zu Kz 61 bei,
-- Konten mit VSt7 zu Kz 62. Kz 66 wird weiterhin rechnerisch aggregiert.
UPDATE accounts
SET ustva_kz = '61'
WHERE ustva_kz = '66'
AND steuercode = 'VSt19';
UPDATE accounts
SET ustva_kz = '62'
WHERE ustva_kz = '66'
AND steuercode = 'VSt7';

View file

@ -9,18 +9,24 @@ use Steuerkram\Database;
/** /**
* Berechnet alle ELSTER-Kennzahlen (Kz) für die Umsatzsteuervoranmeldung. * Berechnet alle ELSTER-Kennzahlen (Kz) für die Umsatzsteuervoranmeldung.
* *
* Maßgeblich ist ausschließlich das Feld accounts.ustva_kz des Kategorie-Kontos. * Zwei Quellen fließen in die Berechnung ein:
* *
* Kontozuordnung: * 1. Kontobasiert (accounts.ustva_kz des Kategorie-Kontos):
* ustva_kz = '81' → Kz 81 (betrag_netto), Kz 83 (betrag_ust) * ustva_kz = '81' → Kz 81 (betrag_netto), Kz 83 (betrag_ust)
* ustva_kz = '86' → Kz 86 (betrag_netto), Kz 84 (betrag_ust) * ustva_kz = '86' → Kz 86 (betrag_netto), Kz 84 (betrag_ust)
* ustva_kz = '35' → Kz 35 (betrag_netto) * ustva_kz = '35' → Kz 35 (betrag_netto)
* ustva_kz = '41' → Kz 41 (betrag_netto) * ustva_kz = '41' → Kz 41 (betrag_netto) [steuerfreie ig. Lieferungen via Konto]
* ustva_kz = '44' → Kz 44 (betrag_netto) * ustva_kz = '44' → Kz 44 (betrag_netto) [steuerfreie Ausfuhren via Konto]
* ustva_kz = '61' → Kz 61 (betrag_ust) [Betriebsausgabe-Konten mit VSt19] * ustva_kz = '61' → Kz 61 (betrag_ust) [Vorsteuer 19 %, VSt19-Konten]
* ustva_kz = '62' → Kz 62 (betrag_ust) [Betriebsausgabe-Konten mit VSt7] * ustva_kz = '62' → Kz 62 (betrag_ust) [Vorsteuer 7 %, VSt7-Konten]
* ustva_kz = '89' → Kz 89 (betrag_netto), Kz 91 (betrag_ust) [ig. Erwerb 19 %] *
* ustva_kz = '93' → Kz 93 (betrag_netto), Kz 94 (betrag_ust) [ig. Erwerb 7 %] * 2. Steuerart-basiert (bookings.steuerart, unabhängig vom Konto):
* steuerart = 'ig_lieferung' → Kz 41 (betrag_netto)
* steuerart = 'export' → Kz 44 (betrag_netto)
* steuerart = 'ig_erwerb_19' → Kz 89 (betrag_netto), Kz 91 (netto × 0,19)
* steuerart = 'ig_erwerb_7' → Kz 93 (betrag_netto), Kz 94 (netto × 0,07)
* Buchungen mit diesen steuerart-Werten bleiben aus der kontobasierten Berechnung
* ausgeschlossen, um Doppelzählung zu vermeiden.
* *
* Abgeleitete Kennzahlen: * Abgeleitete Kennzahlen:
* Kz 67 = Kz 91 + Kz 94 (Vorsteuer aus ig. Erwerb) * Kz 67 = Kz 91 + Kz 94 (Vorsteuer aus ig. Erwerb)
@ -29,8 +35,12 @@ use Steuerkram\Database;
*/ */
class UstvaCalculator class UstvaCalculator
{ {
/** Steuerart-Werte die AUSSCHLIESSLICH steuerart-basiert berechnet werden. */
private const EU_STEUERARTEN = ['ig_lieferung', 'export', 'ig_erwerb_19', 'ig_erwerb_7'];
/** /**
* Welche accounts.ustva_kz-Werte liefern Buchungen für die Drill-down-Ansicht einer Kennzahl. * Welche accounts.ustva_kz-Werte liefern Buchungen für die Drill-down-Ansicht.
* Nur für den kontobasierten Teil; EU-Steuerarten kommen aus KZ_STEUERART_MAP.
*/ */
private const KZ_ACCOUNT_MAP = [ private const KZ_ACCOUNT_MAP = [
'81' => ['81'], '81' => ['81'],
@ -42,18 +52,28 @@ class UstvaCalculator
'44' => ['44'], '44' => ['44'],
'61' => ['61'], '61' => ['61'],
'62' => ['62'], '62' => ['62'],
'89' => ['89'], ];
'91' => ['89'],
'93' => ['93'], /**
'94' => ['93'], * Welche steuerart-Werte liefern Buchungen für eine Kennzahl.
'67' => ['89', '93'], */
'66' => ['61', '62', '89', '93'], private const KZ_STEUERART_MAP = [
'69' => ['81', '86', '35', '41', '44', '61', '62', '89', '93'], '41' => ['ig_lieferung'],
'44' => ['export'],
'89' => ['ig_erwerb_19'],
'91' => ['ig_erwerb_19'],
'93' => ['ig_erwerb_7'],
'94' => ['ig_erwerb_7'],
'67' => ['ig_erwerb_19', 'ig_erwerb_7'],
'66' => ['ig_erwerb_19', 'ig_erwerb_7'],
'69' => ['ig_lieferung', 'export', 'ig_erwerb_19', 'ig_erwerb_7'],
]; ];
public function calculate(int $year, int $month): array public function calculate(int $year, int $month): array
{ {
$rows = Database::fetchAll( // ── 1. Kontobasierte Kennzahlen (EU-Steuerarten ausgeschlossen) ──────
$euPlaceholders = implode(',', array_fill(0, count(self::EU_STEUERARTEN), '?'));
$accountRows = Database::fetchAll(
'SELECT a.ustva_kz, 'SELECT a.ustva_kz,
SUM(bk.betrag_netto) AS summe_netto, SUM(bk.betrag_netto) AS summe_netto,
SUM(bk.betrag_ust) AS summe_ust SUM(bk.betrag_ust) AS summe_ust
@ -62,28 +82,49 @@ class UstvaCalculator
JOIN accounts a ON a.id = bk.kategorie_konto_id JOIN accounts a ON a.id = bk.kategorie_konto_id
WHERE fy.year = ? AND MONTH(bk.buchungs_datum) = ? WHERE fy.year = ? AND MONTH(bk.buchungs_datum) = ?
AND a.ustva_kz IS NOT NULL AND a.ustva_kz != \'\' AND a.ustva_kz IS NOT NULL AND a.ustva_kz != \'\'
AND (bk.steuerart IS NULL OR bk.steuerart NOT IN (' . $euPlaceholders . '))
GROUP BY a.ustva_kz', GROUP BY a.ustva_kz',
[$year, $month] array_merge([$year, $month], self::EU_STEUERARTEN)
); );
$byKz = []; $byKz = [];
foreach ($rows as $row) { foreach ($accountRows as $row) {
$byKz[$row['ustva_kz']] = [ $byKz[$row['ustva_kz']] = [
'netto' => (float)$row['summe_netto'], 'netto' => (float)$row['summe_netto'],
'ust' => (float)$row['summe_ust'], 'ust' => (float)$row['summe_ust'],
]; ];
} }
// ── 2. Steuerart-basierte Kennzahlen ─────────────────────────────────
$stRows = Database::fetchAll(
'SELECT bk.steuerart, SUM(bk.betrag_netto) AS summe_netto
FROM bookings bk
JOIN fiscal_years fy ON bk.fiscal_year_id = fy.id
WHERE fy.year = ? AND MONTH(bk.buchungs_datum) = ?
AND bk.steuerart IN (' . $euPlaceholders . ')
GROUP BY bk.steuerart',
array_merge([$year, $month], self::EU_STEUERARTEN)
);
$bySteuerart = [];
foreach ($stRows as $row) {
$bySteuerart[$row['steuerart']] = (float)$row['summe_netto'];
}
// ── 3. Kennzahlen zusammensetzen ─────────────────────────────────────
$n = fn(string $kz): float => (float)($byKz[$kz]['netto'] ?? 0.0); $n = fn(string $kz): float => (float)($byKz[$kz]['netto'] ?? 0.0);
$u = fn(string $kz): float => (float)($byKz[$kz]['ust'] ?? 0.0); $u = fn(string $kz): float => (float)($byKz[$kz]['ust'] ?? 0.0);
$st = fn(string $art): float => (float)($bySteuerart[$art] ?? 0.0);
$kz81 = $n('81'); $kz83 = $u('81'); $kz81 = $n('81'); $kz83 = $u('81');
$kz86 = $n('86'); $kz84 = $u('86'); $kz86 = $n('86'); $kz84 = $u('86');
$kz35 = $n('35'); $kz35 = $n('35');
$kz41 = $n('41'); $kz41 = $n('41') + $st('ig_lieferung');
$kz44 = $n('44'); $kz44 = $n('44') + $st('export');
$kz89 = $n('89'); $kz91 = $u('89'); $kz89 = $st('ig_erwerb_19');
$kz93 = $n('93'); $kz94 = $u('93'); $kz91 = round($kz89 * 0.19, 2);
$kz93 = $st('ig_erwerb_7');
$kz94 = round($kz93 * 0.07, 2);
$kz61 = $u('61'); $kz61 = $u('61');
$kz62 = $u('62'); $kz62 = $u('62');
$kz67 = $kz91 + $kz94; $kz67 = $kz91 + $kz94;
@ -110,18 +151,20 @@ class UstvaCalculator
]; ];
} }
/**
* Liefert alle Buchungszeilen, die zu einer Kennzahl beitragen (Drill-down).
* Kombiniert kontobasierte und steuerart-basierte Quellen per UNION.
*/
public function fetchBookingsForKz(int $year, int $month, string $kz): array public function fetchBookingsForKz(int $year, int $month, string $kz): array
{ {
$accountKzList = self::KZ_ACCOUNT_MAP[$kz] ?? null; $accountKzList = self::KZ_ACCOUNT_MAP[$kz] ?? [];
if ($accountKzList === null) { $steuerartList = self::KZ_STEUERART_MAP[$kz] ?? [];
if (empty($accountKzList) && empty($steuerartList)) {
return []; return [];
} }
$placeholders = implode(',', array_fill(0, count($accountKzList), '?')); $select = 'SELECT bk.id, bk.buchungs_datum, bk.buchungstyp, bk.steuerart, bk.ust_satz,
$params = array_merge([$year, $month], $accountKzList);
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.betrag_netto, bk.betrag_ust, bk.betrag_brutto, bk.buchungstext,
bk.is_reversal, 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, k.kontonummer AS kategorie_nr, k.bezeichnung AS kategorie_name,
@ -131,10 +174,32 @@ class UstvaCalculator
LEFT JOIN belege bl ON bk.beleg_id = bl.id LEFT JOIN belege bl ON bk.beleg_id = bl.id
JOIN accounts k ON bk.kategorie_konto_id = k.id JOIN accounts k ON bk.kategorie_konto_id = k.id
JOIN accounts z ON bk.zahlungskonto_id = z.id JOIN accounts z ON bk.zahlungskonto_id = z.id
WHERE fy.year = ? AND MONTH(bk.buchungs_datum) = ? WHERE fy.year = ? AND MONTH(bk.buchungs_datum) = ?';
AND k.ustva_kz IN (' . $placeholders . ')
ORDER BY bk.buchungs_datum, bk.id', $parts = [];
$params $params = [];
if (!empty($accountKzList)) {
$ph = implode(',', array_fill(0, count($accountKzList), '?'));
$euPh = implode(',', array_fill(0, count(self::EU_STEUERARTEN), '?'));
$parts[] = $select
. ' AND k.ustva_kz IN (' . $ph . ')'
. ' AND (bk.steuerart IS NULL OR bk.steuerart NOT IN (' . $euPh . '))';
$params = array_merge($params,
[$year, $month],
$accountKzList,
self::EU_STEUERARTEN
); );
} }
if (!empty($steuerartList)) {
$ph = implode(',', array_fill(0, count($steuerartList), '?'));
$parts[] = $select . ' AND bk.steuerart IN (' . $ph . ')';
$params = array_merge($params, [$year, $month], $steuerartList);
}
$sql = implode(' UNION ALL ', $parts) . ' ORDER BY buchungs_datum, id';
return Database::fetchAll($sql, $params);
}
} }

View file

@ -16,19 +16,19 @@ class InvoiceBuchungTest extends TestCase
public function run(): void public function run(): void
{ {
// Benötigte Konten prüfen (SKR03) // Benötigte Konten prüfen (SKR03)
$zahlungsId = $this->findAccountId('1400'); // Forderungen (Zahlungskonto) $zahlungsId = $this->findAccountId('1200'); // Bank (Zahlungskonto)
$kategorieId = $this->findAccountId('8000'); // Erlöse 19 % (Betriebseinnahme) $kategorieId = $this->findAccountId('8400'); // Erlöse 19 % USt (Betriebseinnahme)
if (!$zahlungsId) { if (!$zahlungsId) {
$this->assert(false, 'Forderungskonto 1400 nicht gefunden – SKR03-Seed vorhanden?'); $this->assert(false, 'Bankkonto 1200 nicht gefunden – SKR03-Seed vorhanden?');
return; return;
} }
if (!$kategorieId) { if (!$kategorieId) {
$this->assert(false, 'Erlöskonto 8000 nicht gefunden – SKR03-Seed vorhanden?'); $this->assert(false, 'Erlöskonto 8400 nicht gefunden – SKR03-Seed vorhanden?');
return; return;
} }
$this->assert(true, 'Benötigte Konten (1400, 8000) vorhanden'); $this->assert(true, 'Benötigte Konten (1200, 8400) vorhanden');
// Testkunden anlegen // Testkunden anlegen
$customerId = \Steuerkram\Models\Customer::create([ $customerId = \Steuerkram\Models\Customer::create([

View file

@ -19,15 +19,15 @@ class TaxSummenTest extends TestCase
$datum = date('Y-m-d'); $datum = date('Y-m-d');
// Benötigte Konten (SKR03) // Benötigte Konten (SKR03)
$zahlungsId = $this->findAccountId('1400'); // Forderungen (Zahlungskonto) $zahlungsId = $this->findAccountId('1200'); // Bank (Zahlungskonto)
$erloesId = $this->findAccountId('8000'); // Erlöse 19 % (Betriebseinnahme) $erloesId = $this->findAccountId('8400'); // Erlöse 19 % USt (Betriebseinnahme, kz=81)
$ausgabeId = $this->findAccountId('4920'); // Bürobedarf (Betriebsausgabe) $ausgabeId = $this->findAccountId('4920'); // Bürobedarf (Betriebsausgabe)
$bankId = $this->findAccountId('1200'); // Bank (Zahlungskonto) $bankId = $this->findAccountId('1200'); // Bank (Zahlungskonto)
// Fallback auf beliebige Konten der passenden Art // Fallback: explizit ein Konto mit ustva_kz=81 suchen, damit kz81/83 befüllt werden
if (!$erloesId) { if (!$erloesId) {
$row = \Steuerkram\Database::fetchOne( $row = \Steuerkram\Database::fetchOne(
"SELECT id FROM accounts WHERE kontenart = 'Betriebseinnahme' AND aktiv = 1 LIMIT 1" "SELECT id FROM accounts WHERE kontenart = 'Betriebseinnahme' AND ustva_kz = '81' AND aktiv = 1 LIMIT 1"
); );
$erloesId = $row ? (int)$row['id'] : null; $erloesId = $row ? (int)$row['id'] : null;
} }

View file

@ -42,12 +42,12 @@ class EuerBuchungsTest extends TestCase
); );
// Konten laden // Konten laden
$erloesId = $this->findAccountId('8000'); // Betriebseinnahme $erloesId = $this->findAccountId('8400'); // Erlöse 19 % USt (Betriebseinnahme)
$ausgabeId = $this->findAccountId('4600'); // Bürobedarf (Betriebsausgabe) $ausgabeId = $this->findAccountId('4600'); // Werbekosten (Betriebsausgabe)
$bankId = $this->findAccountId('1200'); // Bank (Zahlungskonto) $bankId = $this->findAccountId('1200'); // Bank (Zahlungskonto)
if (!$erloesId || !$ausgabeId || !$bankId) { if (!$erloesId || !$ausgabeId || !$bankId) {
$this->assert(false, 'Benötigte Konten (8000, 4600, 1200) nicht gefunden'); $this->assert(false, 'Benötigte Konten (8400, 4600, 1200) nicht gefunden');
return; return;
} }

View file

@ -20,14 +20,14 @@ class InvoiceToReportsTest extends TestCase
$datum = date('Y-m-d'); $datum = date('Y-m-d');
// ── Konten prüfen ──────────────────────────────────────────────────── // ── Konten prüfen ────────────────────────────────────────────────────
$zahlungsId = $this->findAccountId('1400'); // Forderungen $zahlungsId = $this->findAccountId('1200'); // Bank
$erloes19Id = $this->findAccountId('8000'); // Erlöse 19 % $erloes19Id = $this->findAccountId('8400'); // Erlöse 19 % USt
if (!$zahlungsId || !$erloes19Id) { if (!$zahlungsId || !$erloes19Id) {
$this->assert(false, 'Benötigte Konten (1400, 8000) nicht gefunden – SKR03-Seed vorhanden?'); $this->assert(false, 'Benötigte Konten (1200, 8400) nicht gefunden – SKR03-Seed vorhanden?');
return; return;
} }
$this->assert(true, 'Konten 1400 und 8000 vorhanden'); $this->assert(true, 'Konten 1200 und 8400 vorhanden');
// ── Kunde und Rechnung anlegen ──────────────────────────────────────── // ── Kunde und Rechnung anlegen ────────────────────────────────────────
$customerId = \Steuerkram\Models\Customer::create([ $customerId = \Steuerkram\Models\Customer::create([