Fix: UStVA Vorsteuer-Kz-Aufteilung (Migration 022) + Tests korrigiert
This commit is contained in:
parent
aa36a16c5a
commit
e1f356d0cf
8 changed files with 195 additions and 111 deletions
48
BERICHTE.md
48
BERICHTE.md
|
|
@ -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.
|
||||||
|
|
|
||||||
10
Changes.md
10
Changes.md
|
|
@ -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
|
||||||
|
|
|
||||||
17
migrations/022_vorsteuer_kz_split.sql
Normal file
17
migrations/022_vorsteuer_kz_split.sql
Normal 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';
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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([
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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([
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue