Steuerkram/src/Services/UstvaCalculator.php

142 lines
6.5 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 ist ausschließlich das Feld accounts.ustva_kz des Kategorie-Kontos.
*
* Kontozuordnung:
* ustva_kz = '81' → Kz 81 (betrag_netto), Kz 83 (betrag_ust)
* ustva_kz = '86' → Kz 86 (betrag_netto), Kz 84 (betrag_ust)
* ustva_kz = '35' → Kz 35 (betrag_netto)
* ustva_kz = '41' → Kz 41 (betrag_netto)
* ustva_kz = '44' → Kz 44 (betrag_netto)
* ustva_kz = '61' → Kz 61 (betrag_ust) [Betriebsausgabe-Konten mit VSt19]
* ustva_kz = '62' → Kz 62 (betrag_ust) [Betriebsausgabe-Konten mit VSt7]
* ustva_kz = '89' → Kz 89 (betrag_netto), Kz 91 (betrag_ust) [ig. Erwerb 19 %]
* ustva_kz = '93' → Kz 93 (betrag_netto), Kz 94 (betrag_ust) [ig. Erwerb 7 %]
*
* Abgeleitete Kennzahlen:
* Kz 67 = Kz 91 + Kz 94 (Vorsteuer aus ig. Erwerb)
* Kz 66 = Kz 61 + Kz 62 + Kz 67 (Vorsteuer gesamt)
* Kz 69 = (Kz 83 + Kz 84 + Kz 91 + Kz 94) − Kz 66 (Zahllast)
*/
class UstvaCalculator
{
/**
* Welche accounts.ustva_kz-Werte liefern Buchungen für die Drill-down-Ansicht einer Kennzahl.
*/
private const KZ_ACCOUNT_MAP = [
'81' => ['81'],
'83' => ['81'],
'86' => ['86'],
'84' => ['86'],
'35' => ['35'],
'41' => ['41'],
'44' => ['44'],
'61' => ['61'],
'62' => ['62'],
'89' => ['89'],
'91' => ['89'],
'93' => ['93'],
'94' => ['93'],
'67' => ['89', '93'],
'66' => ['61', '62', '89', '93'],
'69' => ['81', '86', '35', '41', '44', '61', '62', '89', '93'],
];
public function calculate(int $year, int $month): array
{
$rows = Database::fetchAll(
'SELECT a.ustva_kz,
SUM(bk.betrag_netto) AS summe_netto,
SUM(bk.betrag_ust) AS summe_ust
FROM bookings bk
JOIN fiscal_years fy ON bk.fiscal_year_id = fy.id
JOIN accounts a ON a.id = bk.kategorie_konto_id
WHERE fy.year = ? AND MONTH(bk.buchungs_datum) = ?
AND bk.is_reversal = 0
AND a.ustva_kz IS NOT NULL AND a.ustva_kz != \'\'
GROUP BY a.ustva_kz',
[$year, $month]
);
$byKz = [];
foreach ($rows as $row) {
$byKz[$row['ustva_kz']] = [
'netto' => (float)$row['summe_netto'],
'ust' => (float)$row['summe_ust'],
];
}
$n = fn(string $kz): float => abs($byKz[$kz]['netto'] ?? 0.0);
$u = fn(string $kz): float => abs($byKz[$kz]['ust'] ?? 0.0);
$kz81 = $n('81'); $kz83 = $u('81');
$kz86 = $n('86'); $kz84 = $u('86');
$kz35 = $n('35');
$kz41 = $n('41');
$kz44 = $n('44');
$kz89 = $n('89'); $kz91 = $u('89');
$kz93 = $n('93'); $kz94 = $u('93');
$kz61 = $u('61');
$kz62 = $u('62');
$kz67 = $kz91 + $kz94;
$kz66 = $kz61 + $kz62 + $kz67;
$kz69 = ($kz83 + $kz84 + $kz91 + $kz94) - $kz66;
return [
'81' => ['name' => 'Steuerpfl. Umsätze 19 %', 'betrag' => round($kz81, 2), 'steuer' => null],
'83' => ['name' => 'USt auf steuerpfl. Umsätze 19 %', 'betrag' => null, 'steuer' => round($kz83, 2)],
'86' => ['name' => 'Steuerpfl. Umsätze 7 %', 'betrag' => round($kz86, 2), 'steuer' => null],
'84' => ['name' => 'USt auf steuerpfl. Umsätze 7 %', 'betrag' => null, 'steuer' => round($kz84, 2)],
'35' => ['name' => 'Steuerfreie Umsätze (ohne Vorsteuerabzug)', 'betrag' => round($kz35, 2), 'steuer' => null],
'41' => ['name' => 'Steuerfreie ig. Lieferungen (§ 4 Nr. 1b)', 'betrag' => round($kz41, 2), 'steuer' => null],
'44' => ['name' => 'Steuerfreie Ausfuhren (§ 4 Nr. 1a)', 'betrag' => round($kz44, 2), 'steuer' => null],
'89' => ['name' => 'Ig. Erwerb 19 % – Bemessungsgrundlage', 'betrag' => round($kz89, 2), 'steuer' => null],
'91' => ['name' => 'USt auf ig. Erwerb 19 %', 'betrag' => null, 'steuer' => round($kz91, 2)],
'93' => ['name' => 'Ig. Erwerb 7 % – Bemessungsgrundlage', 'betrag' => round($kz93, 2), 'steuer' => null],
'94' => ['name' => 'USt auf ig. Erwerb 7 %', 'betrag' => null, 'steuer' => round($kz94, 2)],
'61' => ['name' => 'Vorsteuer aus Eingangsrechnungen 19 %', 'betrag' => null, 'steuer' => round($kz61, 2)],
'62' => ['name' => 'Vorsteuer aus Eingangsrechnungen 7 %', 'betrag' => null, 'steuer' => round($kz62, 2)],
'67' => ['name' => 'Vorsteuer aus ig. Erwerb', 'betrag' => null, 'steuer' => round($kz67, 2)],
'66' => ['name' => 'Vorsteuerbeträge gesamt', 'betrag' => null, 'steuer' => round($kz66, 2)],
'69' => ['name' => 'Verbleibende USt-Vorauszahlung/Erstattung', 'betrag' => null, 'steuer' => round($kz69, 2)],
];
}
public function fetchBookingsForKz(int $year, int $month, string $kz): array
{
$accountKzList = self::KZ_ACCOUNT_MAP[$kz] ?? null;
if ($accountKzList === null) {
return [];
}
$placeholders = implode(',', array_fill(0, count($accountKzList), '?'));
$params = array_merge([$year, $month], $accountKzList);
return Database::fetchAll(
'SELECT bk.id, bk.buchungs_datum, bk.buchungstyp, bk.steuerart, bk.ust_satz,
bk.betrag_netto, bk.betrag_ust, bk.betrag_brutto, bk.buchungstext,
bk.beleg_id, bl.beleg_nr,
k.kontonummer AS kategorie_nr, k.bezeichnung AS kategorie_name,
z.kontonummer AS zahlungs_nr, z.bezeichnung AS zahlungs_name
FROM bookings bk
JOIN fiscal_years fy ON bk.fiscal_year_id = fy.id
LEFT JOIN belege bl ON bk.beleg_id = bl.id
JOIN accounts k ON bk.kategorie_konto_id = k.id
JOIN accounts z ON bk.zahlungskonto_id = z.id
WHERE fy.year = ? AND MONTH(bk.buchungs_datum) = ?
AND bk.is_reversal = 0
AND k.ustva_kz IN (' . $placeholders . ')
ORDER BY bk.buchungs_datum, bk.id',
$params
);
}
}