165 lines
6.3 KiB
PHP
165 lines
6.3 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, 44, 89, 91 und 67 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');
|
||
|
||
$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;
|
||
|
||
$calc = new \Steuerkram\Services\UstvaCalculator();
|
||
$kzPre = $calc->calculate($year, $month);
|
||
|
||
// ── 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 44 ───────────────────────────────
|
||
$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/91/67 ─────────────────────────────
|
||
$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%',
|
||
]);
|
||
|
||
// ── UStVA berechnen und Delta prüfen ──────────────────────────────
|
||
$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 44: Ausfuhr 2000 €
|
||
$this->assert(
|
||
(float)$kz['44']['betrag'] >= 2000.00,
|
||
'Kz 44 (Ausfuhr): Betrag ≥ 2.000 €'
|
||
);
|
||
|
||
// Kz 89: ig. Erwerb 19% Bemessungsgrundlage +500 €
|
||
$this->assert(
|
||
round((float)$kz['89']['betrag'] - (float)$kzPre['89']['betrag'], 2) >= 500.00,
|
||
'Kz 89 (ig. Erwerb 19% Bmgl.): Delta ≥ 500 €'
|
||
);
|
||
|
||
// Kz 91: USt ig. Erwerb kontobasiert (1774) +95 €
|
||
$this->assertEquals(
|
||
95.00,
|
||
round((float)$kz['91']['steuer'] - (float)$kzPre['91']['steuer'], 2),
|
||
'Kz 91 (USt ig. Erwerb 19%): Delta = 95,00 €'
|
||
);
|
||
|
||
// Kz 67: VSt ig. Erwerb kontobasiert (1574) +95 €
|
||
$this->assertEquals(
|
||
95.00,
|
||
round((float)$kz['67']['steuer'] - (float)$kzPre['67']['steuer'], 2),
|
||
'Kz 67 (VSt ig. Erwerb): Delta = 95,00 €'
|
||
);
|
||
|
||
// ReverseCharge ist zahllastneutral: Kz 69 unverändert
|
||
$this->assertEquals(
|
||
0.00,
|
||
round((float)$kz['69']['steuer'] - (float)$kzPre['69']['steuer'], 2),
|
||
'Kz 69 (Zahllast) durch ig. Erwerb nicht verändert'
|
||
);
|
||
|
||
// ig. Erwerb darf nicht in Kz 35 landen
|
||
$this->assertEquals(
|
||
0.0,
|
||
(float)$kz['35']['betrag'],
|
||
'Kz 35 (steuerfreie Inlandsumsätze) = 0'
|
||
);
|
||
}
|
||
|
||
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;
|
||
}
|
||
}
|