- Migration 046: ustva_kz='83'/'84' für Konten 1776/1771, Bestandsmigration für Einnahme-Buchungen - Booking::create(): USt-Kindzeile auf 1771/1776 für Einnahmen mit 7/19 % (analog VSt) - UstvaCalculator: Kz 83/84 kontobasiert via 1771/1776, KZ_ACCOUNT_MAP aktualisiert - Invoice::cancel(): nur Hauptbuchungen direkt stornieren (parent_booking_id IS NULL) - Tests 10, 27 angepasst; Test 29 neu
143 lines
6.6 KiB
PHP
143 lines
6.6 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
/**
|
|
* Test 29: USt-Buchungszeilen für Ausgangsrechnungen.
|
|
*
|
|
* Prüft, dass Einnahme-Buchungen mit USt 7 % / 19 % automatisch eine
|
|
* USt-Buchungszeile auf 1771 / 1776 erhalten, die UStVA Kz 83/84 daraus
|
|
* kontobasiert ableitet und ein Storno die USt-Zeilen mitaufhebt.
|
|
*/
|
|
class UmsatzsteuerkontenTest extends TestCase
|
|
{
|
|
protected string $testName = 'Test 29: USt-Buchungszeilen für Ausgangsrechnungen';
|
|
|
|
public function run(): void
|
|
{
|
|
$year = (int)date('Y');
|
|
$month = (int)date('n');
|
|
$datum = date('Y-m-d');
|
|
|
|
$zahlungsId = $this->findAccountId('1200');
|
|
$einnahmeId = $this->findAccountId('8400');
|
|
$konto1776 = \Steuerkram\Database::fetchOne(
|
|
"SELECT id FROM accounts WHERE kontonummer = '1776' AND aktiv = 1"
|
|
);
|
|
$konto1771 = \Steuerkram\Database::fetchOne(
|
|
"SELECT id FROM accounts WHERE kontonummer = '1771' AND aktiv = 1"
|
|
);
|
|
|
|
if (!$zahlungsId || !$einnahmeId || !$konto1776 || !$konto1771) {
|
|
$this->assert(false, 'Benötigte Konten nicht gefunden (1200, 8400, 1771, 1776)');
|
|
return;
|
|
}
|
|
$this->assert(true, 'Konten 1200, 8400, 1771, 1776 vorhanden');
|
|
|
|
// ── UStVA-Stand vor den Testbuchungen ─────────────────────────
|
|
$calc = new \Steuerkram\Services\UstvaCalculator();
|
|
$kzPre = $calc->calculate($year, $month);
|
|
|
|
// ── Beleg anlegen ──────────────────────────────────────────────
|
|
$belegId = \Steuerkram\Models\Beleg::create([
|
|
'beleg_nr' => 'UST-TEST-' . uniqid(),
|
|
'filename' => 'ust-test.pdf',
|
|
'storage_path' => '',
|
|
'mime_type' => 'application/pdf',
|
|
'file_hash' => hash('sha256', 'ust-test-' . uniqid()),
|
|
'beleg_datum' => $datum,
|
|
'belegtyp' => 'Ausgangsrechnung',
|
|
]);
|
|
|
|
// ── Einnahme-Buchung 19 % (Netto=500, USt=95) ─────────────────
|
|
$bookingId19 = \Steuerkram\Models\Booking::create([
|
|
'beleg_id' => $belegId,
|
|
'buchungs_datum' => $datum,
|
|
'buchungstyp' => 'Einnahme',
|
|
'kategorie_konto_id' => $einnahmeId,
|
|
'zahlungskonto_id' => $zahlungsId,
|
|
'betrag_netto' => 500.00,
|
|
'ust_satz' => 19.0,
|
|
'buchungstext' => 'Test USt 19%',
|
|
]);
|
|
|
|
// USt-Buchungszeile muss auf 1776 angelegt worden sein
|
|
$ustRow19 = \Steuerkram\Database::fetchOne(
|
|
'SELECT bk.*, a.kontonummer FROM bookings bk
|
|
JOIN accounts a ON bk.kategorie_konto_id = a.id
|
|
WHERE bk.parent_booking_id = ?',
|
|
[$bookingId19]
|
|
);
|
|
$this->assert($ustRow19 !== false, 'USt-Buchungszeile für 19%-Buchung angelegt');
|
|
$this->assertEquals('1776', $ustRow19['kontonummer'] ?? '', 'USt-Buchungszeile auf Konto 1776');
|
|
$this->assertEquals(95.00, round((float)($ustRow19['betrag_netto'] ?? 0), 2), 'USt betrag_netto = 95');
|
|
$this->assertEquals(0.00, round((float)($ustRow19['betrag_ust'] ?? 0), 2), 'USt betrag_ust = 0');
|
|
|
|
// ── Einnahme-Buchung 7 % (Netto=200, USt=14) ──────────────────
|
|
$bookingId7 = \Steuerkram\Models\Booking::create([
|
|
'beleg_id' => $belegId,
|
|
'buchungs_datum' => $datum,
|
|
'buchungstyp' => 'Einnahme',
|
|
'kategorie_konto_id' => $einnahmeId,
|
|
'zahlungskonto_id' => $zahlungsId,
|
|
'betrag_netto' => 200.00,
|
|
'ust_satz' => 7.0,
|
|
'buchungstext' => 'Test USt 7%',
|
|
]);
|
|
|
|
$ustRow7 = \Steuerkram\Database::fetchOne(
|
|
'SELECT bk.*, a.kontonummer FROM bookings bk
|
|
JOIN accounts a ON bk.kategorie_konto_id = a.id
|
|
WHERE bk.parent_booking_id = ?',
|
|
[$bookingId7]
|
|
);
|
|
$this->assert($ustRow7 !== false, 'USt-Buchungszeile für 7%-Buchung angelegt');
|
|
$this->assertEquals('1771', $ustRow7['kontonummer'] ?? '', 'USt-Buchungszeile auf Konto 1771');
|
|
$this->assertEquals(14.00, round((float)($ustRow7['betrag_netto'] ?? 0), 2), 'USt betrag_netto = 14');
|
|
|
|
// ── UStVA: Kz 83 und 84 aus Konten lesen ──────────────────────
|
|
$kzMit = $calc->calculate($year, $month);
|
|
|
|
$this->assert(
|
|
round((float)$kzMit['83']['steuer'] - (float)$kzPre['83']['steuer'], 2) >= 95.00,
|
|
'UStVA Kz 83 stieg durch 19%-Buchung um ≥ 95 €'
|
|
);
|
|
$this->assert(
|
|
round((float)$kzMit['84']['steuer'] - (float)$kzPre['84']['steuer'], 2) >= 14.00,
|
|
'UStVA Kz 84 stieg durch 7%-Buchung um ≥ 14 €'
|
|
);
|
|
|
|
// ── Drill-down Kz 83 enthält die USt-Buchungszeile ────────────
|
|
$drilldown = $calc->fetchBookingsForKz($year, $month, '83');
|
|
$ustFound = false;
|
|
foreach ($drilldown as $b) {
|
|
if ((int)$b['id'] === (int)$ustRow19['id']) {
|
|
$ustFound = true;
|
|
break;
|
|
}
|
|
}
|
|
$this->assert($ustFound, 'Drill-down Kz 83 enthält die USt-Buchungszeile');
|
|
|
|
// ── Storno: USt-Zeile muss mitaufgehoben werden ───────────────
|
|
$reversalId = \Steuerkram\Models\Booking::reverse($bookingId19, 'Testfall USt-Storno');
|
|
|
|
$ustStornoRow = \Steuerkram\Database::fetchOne(
|
|
'SELECT * FROM bookings WHERE parent_booking_id = ? AND is_reversal = 1',
|
|
[$reversalId]
|
|
);
|
|
$this->assert($ustStornoRow !== false, 'USt-Stornobuchung wurde angelegt');
|
|
$this->assertEquals(-95.00, round((float)($ustStornoRow['betrag_netto'] ?? 0), 2), 'USt-Storno betrag_netto = -95');
|
|
|
|
// Original-USt-Zeile muss als storniert markiert sein
|
|
$ustOriginal = \Steuerkram\Database::fetchOne(
|
|
'SELECT reversed_by FROM bookings WHERE id = ?',
|
|
[(int)$ustRow19['id']]
|
|
);
|
|
$this->assert(!empty($ustOriginal['reversed_by']), 'Original-USt-Buchungszeile hat reversed_by gesetzt');
|
|
|
|
// UStVA Kz 83 nach Storno: netto Änderung = nur noch 7%-Buchung sichtbar, 19% aufgehoben
|
|
$kzNach = $calc->calculate($year, $month);
|
|
$delta83 = round((float)$kzNach['83']['steuer'] - (float)$kzPre['83']['steuer'], 2);
|
|
$this->assertEquals(0.00, $delta83, 'Kz 83: Buchung+Storno heben sich gegenüber Ausgangslage auf');
|
|
}
|
|
}
|