145 lines
7 KiB
PHP
145 lines
7 KiB
PHP
<?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]
|
||
);
|
||
}
|
||
}
|