192 lines
7.7 KiB
PHP
192 lines
7.7 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
/**
|
||
* Test 7: UStVA-Kennzahlen für Auslandsumsätze und ig. Erwerb.
|
||
*
|
||
* Prüft, dass steuerart-Buchungen korrekt in die UStVA-Kennzahlen
|
||
* 41, 43, 89, 93 und 61 einfließen.
|
||
*/
|
||
class UstvaAuslandTest extends TestCase
|
||
{
|
||
protected string $testName = 'Test 7: UStVA Auslandsumsätze und ig. Erwerb';
|
||
|
||
public function run(): void
|
||
{
|
||
$year = (int)date('Y');
|
||
$month = (int)date('n');
|
||
$datum = date('Y-m-d');
|
||
|
||
// Konten aus SKR03
|
||
$erloesId = $this->findKonto('Betriebseinnahme');
|
||
$ausgabeId = $this->findKonto('Betriebsausgabe');
|
||
$zahlungId = $this->findKonto('Zahlungskonto');
|
||
|
||
if (!$erloesId || !$ausgabeId || !$zahlungId) {
|
||
$this->assert(false, 'Benötigte Konten nicht gefunden – SKR03-Seed vorhanden?');
|
||
return;
|
||
}
|
||
$this->assert(true, 'Konten vorhanden');
|
||
|
||
// Zweites Zahlungskonto für Ausgabe-Buchungen
|
||
$bankId = \Steuerkram\Database::fetchOne(
|
||
"SELECT id FROM accounts WHERE kontenart = 'Zahlungskonto' AND aktiv = 1 AND id != ? LIMIT 1",
|
||
[$zahlungId]
|
||
);
|
||
$bankId = $bankId ? (int)$bankId['id'] : $zahlungId;
|
||
|
||
// Baseline Kz83 VOR den Test-Buchungen (reale Buchungen im laufenden Monat)
|
||
$calcPre = new \Steuerkram\Services\UstvaCalculator();
|
||
$kzPre = $calcPre->calculate($year, $month);
|
||
$kz83Pre = (float)$kzPre['83']['steuer'];
|
||
|
||
// ── 1. Steuerfreie ig. Lieferung → Kz 41 ──────────────────────────
|
||
$belegIgL = \Steuerkram\Models\Beleg::create([
|
||
'beleg_nr' => 'TEST-IGL-' . uniqid(),
|
||
'filename' => 'test-igl.pdf',
|
||
'storage_path' => '',
|
||
'mime_type' => 'application/pdf',
|
||
'file_hash' => hash('sha256', 'test-igl'),
|
||
'beleg_datum' => $datum,
|
||
'belegtyp' => 'Ausgangsrechnung',
|
||
]);
|
||
\Steuerkram\Models\Booking::create([
|
||
'beleg_id' => $belegIgL,
|
||
'buchungs_datum' => $datum,
|
||
'buchungstyp' => 'Einnahme',
|
||
'steuerart' => 'ig_lieferung',
|
||
'kategorie_konto_id' => $erloesId,
|
||
'zahlungskonto_id' => $zahlungId,
|
||
'betrag_netto' => 1000.00,
|
||
'ust_satz' => 0.0,
|
||
'buchungstext' => 'Test ig. Lieferung EU',
|
||
]);
|
||
|
||
// ── 2. Steuerfreie Ausfuhr → Kz 43 ───────────────────────────────
|
||
$belegExp = \Steuerkram\Models\Beleg::create([
|
||
'beleg_nr' => 'TEST-EXP-' . uniqid(),
|
||
'filename' => 'test-exp.pdf',
|
||
'storage_path' => '',
|
||
'mime_type' => 'application/pdf',
|
||
'file_hash' => hash('sha256', 'test-exp'),
|
||
'beleg_datum' => $datum,
|
||
'belegtyp' => 'Ausgangsrechnung',
|
||
]);
|
||
\Steuerkram\Models\Booking::create([
|
||
'beleg_id' => $belegExp,
|
||
'buchungs_datum' => $datum,
|
||
'buchungstyp' => 'Einnahme',
|
||
'steuerart' => 'export',
|
||
'kategorie_konto_id' => $erloesId,
|
||
'zahlungskonto_id' => $zahlungId,
|
||
'betrag_netto' => 2000.00,
|
||
'ust_satz' => 0.0,
|
||
'buchungstext' => 'Test Ausfuhr Drittland',
|
||
]);
|
||
|
||
// ── 3. Ig. Erwerb 19 % → Kz 89 (Bemessungsgrundlage), Kz 61 (Vorsteuer) ──
|
||
$belegIgE19 = \Steuerkram\Models\Beleg::create([
|
||
'beleg_nr' => 'TEST-IGE19-' . uniqid(),
|
||
'filename' => 'test-ige19.pdf',
|
||
'storage_path' => '',
|
||
'mime_type' => 'application/pdf',
|
||
'file_hash' => hash('sha256', 'test-ige19'),
|
||
'beleg_datum' => $datum,
|
||
'belegtyp' => 'Eingangsrechnung',
|
||
]);
|
||
\Steuerkram\Models\Booking::create([
|
||
'beleg_id' => $belegIgE19,
|
||
'buchungs_datum' => $datum,
|
||
'buchungstyp' => 'Ausgabe',
|
||
'steuerart' => 'ig_erwerb_19',
|
||
'kategorie_konto_id' => $ausgabeId,
|
||
'zahlungskonto_id' => $bankId,
|
||
'betrag_netto' => 500.00,
|
||
'ust_satz' => 0.0,
|
||
'buchungstext' => 'Test ig. Erwerb 19%',
|
||
]);
|
||
|
||
// ── 4. Ig. Erwerb 7 % → Kz 93 (Bemessungsgrundlage), Kz 61 (Vorsteuer) ───
|
||
$belegIgE7 = \Steuerkram\Models\Beleg::create([
|
||
'beleg_nr' => 'TEST-IGE7-' . uniqid(),
|
||
'filename' => 'test-ige7.pdf',
|
||
'storage_path' => '',
|
||
'mime_type' => 'application/pdf',
|
||
'file_hash' => hash('sha256', 'test-ige7'),
|
||
'beleg_datum' => $datum,
|
||
'belegtyp' => 'Eingangsrechnung',
|
||
]);
|
||
\Steuerkram\Models\Booking::create([
|
||
'beleg_id' => $belegIgE7,
|
||
'buchungs_datum' => $datum,
|
||
'buchungstyp' => 'Ausgabe',
|
||
'steuerart' => 'ig_erwerb_7',
|
||
'kategorie_konto_id' => $ausgabeId,
|
||
'zahlungskonto_id' => $bankId,
|
||
'betrag_netto' => 300.00,
|
||
'ust_satz' => 0.0,
|
||
'buchungstext' => 'Test ig. Erwerb 7%',
|
||
]);
|
||
|
||
// ── UStVA berechnen und Kennzahlen prüfen ─────────────────────────
|
||
$calc = new \Steuerkram\Services\UstvaCalculator();
|
||
$kz = $calc->calculate($year, $month);
|
||
|
||
// Kz 41: ig. Lieferung 1000 €
|
||
$this->assert(
|
||
(float)$kz['41']['betrag'] >= 1000.00,
|
||
'Kz 41 (ig. Lieferung): Betrag ≥ 1.000 €'
|
||
);
|
||
|
||
// Kz 43: Ausfuhr 2000 €
|
||
$this->assert(
|
||
(float)$kz['43']['betrag'] >= 2000.00,
|
||
'Kz 43 (Ausfuhr): Betrag ≥ 2.000 €'
|
||
);
|
||
|
||
// Kz 89: ig. Erwerb 19% Bemessungsgrundlage 500 €
|
||
$this->assert(
|
||
(float)$kz['89']['betrag'] >= 500.00,
|
||
'Kz 89 (ig. Erwerb 19% Bmgl.): Betrag ≥ 500 €'
|
||
);
|
||
|
||
// Kz 93: ig. Erwerb 7% Bemessungsgrundlage 300 €
|
||
$this->assert(
|
||
(float)$kz['93']['betrag'] >= 300.00,
|
||
'Kz 93 (ig. Erwerb 7% Bmgl.): Betrag ≥ 300 €'
|
||
);
|
||
|
||
// Kz 61: Vorsteuer aus ig. Erwerben (§ 3d) = 500*0.19 + 300*0.07 = 95 + 21 = 116 €
|
||
$erwartetKz61 = round(500.00 * 0.19, 2) + round(300.00 * 0.07, 2);
|
||
$this->assertEquals(
|
||
$erwartetKz61,
|
||
(float)$kz['61']['steuer'],
|
||
'Kz 61 (Vorsteuer ig. Erwerb § 3d): Steuer = 116,00 €'
|
||
);
|
||
|
||
// Kz 83 (Zahllast): ig. Erwerb/Lieferung/Ausfuhr sind USt-neutral (betrag_ust=0)
|
||
// → Zahllast darf sich gegenüber Baseline nicht verändert haben
|
||
$this->assertEquals(
|
||
round($kz83Pre, 2),
|
||
(float)$kz['83']['steuer'],
|
||
'Kz 83 (Zahllast) unverändert – alle EU-Buchungen sind USt-neutral'
|
||
);
|
||
|
||
// ig. Erwerb darf NICHT in Kz 48 landen (steuerfreie Umsätze ohne VSt-Abzug)
|
||
$this->assertEquals(
|
||
0.0,
|
||
(float)$kz['48']['betrag'],
|
||
'Kz 48 (steuerfreie Inlandsumsätze) = 0 (kein steuerfreier Inlandsumsatz gebucht)'
|
||
);
|
||
}
|
||
|
||
private function findKonto(string $kontenart): ?int
|
||
{
|
||
$row = \Steuerkram\Database::fetchOne(
|
||
"SELECT id FROM accounts WHERE kontenart = ? AND aktiv = 1 LIMIT 1",
|
||
[$kontenart]
|
||
);
|
||
return $row ? (int)$row['id'] : null;
|
||
}
|
||
}
|