Steuerkram/src/Services/UstvaCalculator.php

145 lines
7 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
declare(strict_types=1);
namespace Steuerkram\Services;
use Steuerkram\Database;
/**
* Berechnet alle ELSTER-Kennzahlen (Kz) für die Umsatzsteuervoranmeldung.
*
* Maßgeblich sind buchungstyp, steuerart, ust_satz und betrag_ust der Buchung.
*
* Buchungslogik:
* Umsatzsteuer (Kz 81/86): buchungstyp = 'Einnahme', steuerart = 'normal', ust_satz > 0
* Steuerfreie Umsätze (Kz 35): buchungstyp = 'Einnahme', steuerart = 'normal', ust_satz = 0
* Steuerfreie ig. Lieferungen (Kz 41): buchungstyp = 'Einnahme', steuerart = 'ig_lieferung'
* Ausfuhren (Kz 44): buchungstyp = 'Einnahme', steuerart = 'export'
* Vorsteuer (Kz 61/62): buchungstyp = 'Ausgabe', steuerart = 'normal', ust_satz > 0
* Ig. Erwerb 19 % (Kz 89/91): steuerart = 'ig_erwerb_19'
* Ig. Erwerb 7 % (Kz 93/94): steuerart = 'ig_erwerb_7'
* Vorsteuer aus ig. Erwerb (Kz 67): abgeleitet aus Kz 91 + Kz 94
*/
class UstvaCalculator
{
public function calculate(int $year, int $month): array
{
$kz = [
// Steuerpflichtige und steuerfreie Umsätze
'81' => ['name' => 'Steuerpfl. Umsätze 19 %', 'betrag' => 0.0, 'steuer' => 0.0],
'86' => ['name' => 'Steuerpfl. Umsätze 7 %', 'betrag' => 0.0, 'steuer' => 0.0],
'35' => ['name' => 'Steuerfreie Umsätze (ohne Vorsteuerabzug)', 'betrag' => 0.0, 'steuer' => null],
'41' => ['name' => 'Steuerfreie ig. Lieferungen (§ 4 Nr. 1b)', 'betrag' => 0.0, 'steuer' => null],
'44' => ['name' => 'Steuerfreie Ausfuhren (§ 4 Nr. 1a)', 'betrag' => 0.0, 'steuer' => null],
// USt auf Umsätze (abgeleitet)
'83' => ['name' => 'USt auf steuerpfl. Umsätze 19 %', 'betrag' => null, 'steuer' => 0.0],
'84' => ['name' => 'USt auf steuerpfl. Umsätze 7 %', 'betrag' => null, 'steuer' => 0.0],
// Innergemeinschaftlicher Erwerb
'89' => ['name' => 'Ig. Erwerb 19 % – Bemessungsgrundlage', 'betrag' => 0.0, 'steuer' => null],
'91' => ['name' => 'USt auf ig. Erwerb 19 %', 'betrag' => null, 'steuer' => 0.0],
'93' => ['name' => 'Ig. Erwerb 7 % – Bemessungsgrundlage', 'betrag' => 0.0, 'steuer' => null],
'94' => ['name' => 'USt auf ig. Erwerb 7 %', 'betrag' => null, 'steuer' => 0.0],
// Vorsteuer
'61' => ['name' => 'Vorsteuer aus Eingangsrechnungen 19 %', 'betrag' => null, 'steuer' => 0.0],
'62' => ['name' => 'Vorsteuer aus Eingangsrechnungen 7 %', 'betrag' => null, 'steuer' => 0.0],
'67' => ['name' => 'Vorsteuer aus ig. Erwerb', 'betrag' => null, 'steuer' => 0.0],
'66' => ['name' => 'Vorsteuerbeträge gesamt', 'betrag' => null, 'steuer' => 0.0],
// Ergebnis
'69' => ['name' => 'Verbleibende USt-Vorauszahlung/Erstattung', 'betrag' => null, 'steuer' => 0.0],
];
$bookings = $this->fetchBookings($year, $month);
foreach ($bookings as $b) {
$steuerart = $b['steuerart'] ?? 'normal';
$ustSatz = (float)$b['ust_satz'];
$betragUst = abs((float)$b['betrag_ust']);
$betragNetto = abs((float)$b['betrag_netto']);
$buchungstyp = $b['buchungstyp'];
// ── Einnahmen ───────────────────────────────────────────────
if ($buchungstyp === 'Einnahme') {
switch ($steuerart) {
case 'ig_lieferung':
$kz['41']['betrag'] += $betragNetto;
break;
case 'export':
$kz['44']['betrag'] += $betragNetto;
break;
default: // normal
if ($ustSatz > 0) {
if (abs($ustSatz - 19) < 0.01) {
$kz['81']['betrag'] += $betragNetto;
} elseif (abs($ustSatz - 7) < 0.01) {
$kz['86']['betrag'] += $betragNetto;
}
} else {
$kz['35']['betrag'] += $betragNetto;
}
}
}
// ── Ausgaben ────────────────────────────────────────────────
if ($buchungstyp === 'Ausgabe') {
switch ($steuerart) {
case 'ig_erwerb_19':
$kz['89']['betrag'] += $betragNetto;
break;
case 'ig_erwerb_7':
$kz['93']['betrag'] += $betragNetto;
break;
default: // normal
if ($ustSatz > 0 && $betragUst > 0) {
if (abs($ustSatz - 19) < 0.01) {
$kz['61']['steuer'] += $betragUst;
} elseif (abs($ustSatz - 7) < 0.01) {
$kz['62']['steuer'] += $betragUst;
}
}
}
}
}
// ── Abgeleitete Kennzahlen ───────────────────────────────────────
$kz['83']['steuer'] = round($kz['81']['betrag'] * 0.19, 2);
$kz['84']['steuer'] = round($kz['86']['betrag'] * 0.07, 2);
$kz['91']['steuer'] = round($kz['89']['betrag'] * 0.19, 2);
$kz['94']['steuer'] = round($kz['93']['betrag'] * 0.07, 2);
// Vorsteuer aus ig. Erwerb = Steuer auf ig. Erwerb (vollständig abziehbar)
$kz['67']['steuer'] = round($kz['91']['steuer'] + $kz['94']['steuer'], 2);
// Vorsteuer gesamt
$kz['66']['steuer'] = round(
$kz['61']['steuer'] + $kz['62']['steuer'] + $kz['67']['steuer'],
2
);
// Verbleibende Zahllast
$ustGesamt = $kz['83']['steuer'] + $kz['84']['steuer']
+ $kz['91']['steuer'] + $kz['94']['steuer'];
$kz['69']['steuer'] = round($ustGesamt - $kz['66']['steuer'], 2);
// Runden
foreach ($kz as &$field) {
if ($field['betrag'] !== null) $field['betrag'] = round($field['betrag'], 2);
if ($field['steuer'] !== null) $field['steuer'] = round($field['steuer'], 2);
}
return $kz;
}
private function fetchBookings(int $year, int $month): array
{
return Database::fetchAll(
'SELECT bk.buchungstyp, bk.steuerart, bk.ust_satz, bk.betrag_netto, bk.betrag_ust
FROM bookings bk
JOIN fiscal_years fy ON bk.fiscal_year_id = fy.id
WHERE fy.year = ? AND MONTH(bk.buchungs_datum) = ?
AND bk.is_reversal = 0',
[$year, $month]
);
}
}