mirror of
http://100.103.83.12:3003/fegger/odoo-at-payroll.git
synced 2026-09-17 16:56:42 +00:00
523 lines
25 KiB
Python
523 lines
25 KiB
Python
# Part of the odoo-at-payroll project. License: LGPL-3.
|
||
"""Sozialversicherungs-Kern (GP4/AP4): DG-Beiträge (SVDG), Geringfügigkeit
|
||
(§ 5 Abs 2 ASVG iVm § 810 Abs 3 — 2026-Aufwertung ausgestellt), §-53a-
|
||
UV der Geringfügigen, beitragsfreie Bezüge nach § 49 Abs 3 ASVG mit
|
||
Jahres-Kumulativen, altersabhängige Satzausschaltungen (§ 1 Abs 2 lit e
|
||
AlVG, § 51 Abs 6/7 ASVG, § 12 Abs 2 IESG), WF-Satzvektor Bundesland ×
|
||
Jahr (Wien 1,5 % ab 1.1.2026), ALV-DN-Staffel (§ 2a AMPFG) und
|
||
Beitragsmonats-Allokation (Anspruchsprinzip, historisch versionierte
|
||
Sätze/HBG je Beitragsmonat).
|
||
|
||
Verifizierte Grundlagen (2026-09-11, s. RECHTSQUELLEN-Privat Abschn. 4
|
||
und docs/rechenfaelle/sozialversicherung.md):
|
||
- ÖGK-Beitragstarifsystem „Normalbetriebe Allgemeiner Teil", gültig ab
|
||
01.07.2026 (DN 18,07 % · DG 20,98 % · Wien DN 18,32/DG 21,23 %);
|
||
- TASY-LSWH-Export 2026-03-24 (GFG 551,10 · DAG-Grenze 826,65 ·
|
||
§-19a-Pauschalbeitrag 83,49 €/Monat — Primärquellen-Entscheidung);
|
||
- „Beitragsrechtliche Werte 2026" (sozialversicherung.at): GFG-Aufwertung
|
||
2026 ausgesetzt (§ 810 Abs 3 ASVG); AV-Staffel bis 2.225/2.427/2.630 €
|
||
0/1/2 % DN; WF Wien 0,75/0,75 ab 1.1.2026;
|
||
- ASVG §§ 51 Abs 6 (UV-Entfall 60. LJ/Lehrlinge), 51 Abs 7 (PV in der
|
||
Bonusphase hälftig, DG und DN), 53a Abs 1/3/5 (UV 1,1 % der allgemeinen
|
||
Beitragsgrundlage; Pauschalbeitrag 14,12 %, auch von SZ);
|
||
IESG § 12 Abs 1 Z 4 iVm Abs 2 (Entfall ab Folgemonat nach 63);
|
||
AlVG § 1 Abs 2 lit e (AV-Entfall, ab 2027 inkl. Korridorpension).
|
||
|
||
Laufzeit-Datenfluss wie GP3: Regeln reichen Zwischensummen über
|
||
``result_rules`` an die Compute-Methoden; abgeschlossene Läufe werden
|
||
über line_ids gelesen. Der Beitragsmonat wird je Regel geführt
|
||
(``l10n_at_beitragsmonat``) und auf die Lohnkontolinie durchgereicht.
|
||
"""
|
||
from datetime import date, timedelta
|
||
|
||
from odoo import api, fields, models
|
||
from odoo.tools.safe_eval import safe_eval
|
||
|
||
|
||
class HrSalaryRule(models.Model):
|
||
_inherit = 'hr.salary.rule'
|
||
|
||
l10n_at_sv_art = fields.Selection(
|
||
[('folgt', 'Folgt AT LSt-Bemessungsart'),
|
||
('laufend', 'Laufender SV-Bezug'),
|
||
('sonstig', 'Sonstiger SV-Bezug'),
|
||
('keine', 'Kein SV-Bezug')],
|
||
string='AT SV-Bemessungsart',
|
||
default='folgt',
|
||
help='Steuert ausschließlich die SV-Zuordnung. Standard „Folgt“ '
|
||
'übernimmt die AT LSt-Bemessungsart und ist damit '
|
||
'rückwärtskompatibel. Abweichung für Drittlohn oder '
|
||
'abweichende LSt-/SV-Zuordnung, z. B. Bonusmeilen oder '
|
||
'Hardware-Verkauf.')
|
||
l10n_at_sv49_frei = fields.Selection(
|
||
[('keine', 'Keine §-49-Abs-3-Befreiung'),
|
||
('mahlzeit', 'Mahlzeiten-Gutschein (§ 49 Abs 3 Z 12; 8 €/AT)'),
|
||
('rabatt', 'Mitarbeiterrabatt (§ 49 Abs 3 Z 29; 1.000 €/J)'),
|
||
('betriebsveranstaltung', 'Betriebsveranstaltung (Z 17; 365 €/J)'),
|
||
('jubilaeum', 'Jubiläums-Sachzuwendung (Z 17; 186 €/J)'),
|
||
('zukunftssicherung', 'Zukunftssicherung (Z 18 lit a; 300 €/J)'),
|
||
('kinderbetreuung', 'Kinderbetreuungszuschuss (Z 11 lit d; 2.000 €/Kind/J)'),
|
||
('homeoffice', 'Homeoffice-Pauschale (Z 31; 3 €/Tag, max 100 Tage)')],
|
||
string='AT § 49 Abs 3 (beitragsfrei)',
|
||
default='keine',
|
||
help='Beitragsfreie Bezugsbestandteile: der beitragsfreie Anteil '
|
||
'wird je Bezugsmonat von der SV-Beitragsgrundlage abgezogen; '
|
||
'Grenzen als Jahres-Kumulative je Dienstnehmer '
|
||
'(at_sv49_grenzen). Überschreiten = beitragspflichtiger '
|
||
'Sachbezug (Umwidmung). Menge = Arbeitstage (Z 12/Z 31).')
|
||
l10n_at_s67_abs = fields.Selection(
|
||
[('keine', 'Keine §-67-Abs-3/6-Begünstigung'),
|
||
('abs3', 'Abfertigung (§ 67 Abs 3 EStG)'),
|
||
('abs6', 'Abfindung (§ 67 Abs 6 EStG)')],
|
||
string='AT § 67 Abs 3/6',
|
||
default='keine',
|
||
help='Markiert Bezüge, die beim FLAF-Dienstgeberbeitrag (§ 41 '
|
||
'Abs 4 FLAG), Zuschlag zum DG-Beitrag (WKG) und der '
|
||
'Kommunalsteuer (§ 5 Abs 2 KommStG) aus der Bemessungsgrund'
|
||
'lage ausgenommen sind.')
|
||
l10n_at_beitragsmonat = fields.Date(
|
||
string='AT Beitragsmonat (Anspruchsprinzip)',
|
||
help='Monat, in dem der Anspruch auf diesen Bezug fällig wurde '
|
||
'(§ 17 ASVG). Leer = Abrechnungsmonat. Für Nachzahlungs-/'
|
||
'Korrekturlinien setzen: SV-Sätze, HBG und GFG werden '
|
||
'historisch versioniert je Beitragsmonat aufgelöst '
|
||
'(Lohnsteuer rollt kumulativ in den Zuflussmonat, GP3).')
|
||
|
||
|
||
class HrPayslipLine(models.Model):
|
||
_inherit = 'hr.payslip.line'
|
||
|
||
l10n_at_beitragsmonat = fields.Date(
|
||
string='AT Beitragsmonat',
|
||
compute='_compute_l10n_at_beitragsmonat',
|
||
store=True,
|
||
readonly=False,
|
||
index=True,
|
||
help='Beitragsmonat der Linie (Anspruchsprinzip); default '
|
||
'Abrechnungsmonat, über die Regel übersteuerbar.')
|
||
|
||
@api.depends('salary_rule_id.l10n_at_beitragsmonat', 'date_from')
|
||
def _compute_l10n_at_beitragsmonat(self):
|
||
for line in self:
|
||
line.l10n_at_beitragsmonat = (
|
||
line.salary_rule_id.l10n_at_beitragsmonat or line.date_from)
|
||
|
||
|
||
class HrVersion(models.Model):
|
||
_inherit = 'hr.version'
|
||
|
||
l10n_at_lehrling = fields.Boolean(
|
||
string='AT Lehrling',
|
||
help='Keine AK-Umlage (§ 61 AKG) und kein UV-Beitrag (§ 51 Abs 6 '
|
||
'ASVG — aus Mitteln der Unfallversicherung); GFG gilt nicht '
|
||
'(§ 5 Abs 3 ASVG).')
|
||
l10n_at_alv_entfall_ab = fields.Date(
|
||
string='AT ALV-Entfall ab (§ 1 Abs 2 lit e AlVG)',
|
||
help='AV-Beitrag DN+DG und IESG-Zuschlag entfallen ab diesem '
|
||
'Beitragsmonat (63. Lebensjahr oder erfüllte Alterspensions-'
|
||
'Anwartschaft; ab 1.1.2027 inkl. Korridorpension, '
|
||
'NOR40280219; § 12 Abs 2 IESG). Stichtagsregel am Vertrag.')
|
||
l10n_at_uv_entfall_ab = fields.Date(
|
||
string='AT UV-Entfall ab (§ 51 Abs 6 ASVG)',
|
||
help='Unfallversicherungsbeitrag entfällt ab diesem '
|
||
'Beitragsmonat (Vollendung des 60. Lebensjahres); die '
|
||
'Person bleibt unfallversichert.')
|
||
l10n_at_pv_bonusphase_ab = fields.Date(
|
||
string='AT PV-Bonusphase ab (§ 51 Abs 7 ASVG)',
|
||
help='Pensionsversicherungsbeitrag je zur Hälfte reduziert für '
|
||
'DG und DN (Bonusphase/Pensionsanspruch; ÖGK-Abschlag '
|
||
'A 15; Pensionskonto-Gutschrift bleibt ungekürzt — Auswertung '
|
||
'GP8).')
|
||
l10n_at_lnkp_frei_ab = fields.Date(
|
||
string='AT LNK-frei ab (§ 41 Abs 4 lit f FLAG)',
|
||
help='Arbeitslöhne ab diesem Kalendermonat sind vom FLAF-'
|
||
'Dienstgeberbeitrag und vom Zuschlag zum Dienstgeberbeitrag '
|
||
'(WKG) ausgenommen (Folgemonat nach Vollendung des 60. '
|
||
'Lebensjahres). Kommunalsteuer: keine Befreiung.')
|
||
|
||
|
||
class ResCompany(models.Model):
|
||
_inherit = 'res.company'
|
||
|
||
l10n_at_bundesland = fields.Selection(
|
||
[('burgenland', 'Burgenland'), ('kaernten', 'Kärnten'),
|
||
('niederoesterreich', 'Niederösterreich'),
|
||
('oberoesterreich', 'Oberösterreich'), ('salzburg', 'Salzburg'),
|
||
('steiermark', 'Steiermark'), ('tirol', 'Tirol'),
|
||
('vorarlberg', 'Vorarlberg'), ('wien', 'Wien')],
|
||
string='AT Bundesland (Betriebsstätte)',
|
||
help='Betriebsstätten-Bundesland für Bundesland-Satzvektoren '
|
||
'(z. B. DZ-Landeskammeranteil; WF-Wien zusätzlich über das '
|
||
'Company-Flag l10n_at_wf_wien steuerbar).')
|
||
l10n_at_wk_mitglied = fields.Boolean(
|
||
string='AT WKO-Mitgliedschaft (DZ-pflichtig)',
|
||
help='Zuschlag zum Dienstgeberbeitrag (§§ 122/126 WKG) nur für '
|
||
'Mitglieder der Wirtschaftskammerorganisation (§ 2 WKG).')
|
||
|
||
def _l10n_at_dag(self, monat_start):
|
||
"""Dienstgeberabgabe geringfügig Beschäftigter (§ 1 DAG) für den
|
||
Kalendermonat: 19,4 % der Lohnsumme (Beitragsgrundlagen ohne SZ)
|
||
aller geringfügig Beschäftigten, wenn die Lohnsumme das 1,5-fache
|
||
der monatlichen GFG überschreitet und mindestens zwei
|
||
Geringfügige beschäftigt sind (lb-bes-06; TASY MINGRDAGP1 2026:
|
||
826,65 €; Ausweis/Veranlagung GP8)."""
|
||
monat_ende = (monat_start + timedelta(days=32)).replace(day=1) \
|
||
- timedelta(days=1)
|
||
slips = self.env['hr.payslip'].search([
|
||
('company_id', '=', self.id),
|
||
('date_from', '<=', monat_ende),
|
||
('date_to', '>=', monat_start),
|
||
('state', 'not in', ('draft', 'cancel')),
|
||
('l10n_at_geringfuegig', '=', True),
|
||
])
|
||
lohnsumme = sum(
|
||
slip._l10n_at_sv_basis_roh() for slip in slips)
|
||
anzahl = len({s.employee_id.id for s in slips})
|
||
params = self.env['hr.rule.parameter']._get_parameter_from_code(
|
||
'at_dag', monat_start, raise_if_not_found=False) or {}
|
||
gfg = float(self.env['hr.rule.parameter']._get_parameter_from_code(
|
||
'at_sv_gfg_monatlich', monat_start,
|
||
raise_if_not_found=False) or 0.0)
|
||
schwelle = float(params.get('schwelle_faktor', 0.0)) * gfg
|
||
abgabe = 0.0
|
||
if anzahl >= 2 and lohnsumme > schwelle:
|
||
abgabe = lohnsumme * float(params.get('satz', 0.0)) / 100.0
|
||
return {'anzahl': anzahl, 'lohnsumme': lohnsumme,
|
||
'schwelle': schwelle, 'abgabe': abgabe}
|
||
|
||
|
||
class HrPayslip(models.Model):
|
||
_inherit = 'hr.payslip'
|
||
|
||
l10n_at_geringfuegig = fields.Boolean(
|
||
string='AT Geringfügig beschäftigt',
|
||
compute='_compute_l10n_at_geringfuegig',
|
||
store=True,
|
||
help='Laufendes Entgelt (§-49-bereinigt, hochgerechnet bei '
|
||
'untermonatiger Beschäftigung) überschreitet die GFG nicht '
|
||
'(§ 5 Abs 2 ASVG). DAG-Monitoring-Grundlage (§ 1 DAG).')
|
||
|
||
@api.depends('line_ids', 'date_from', 'date_to', 'version_id')
|
||
def _compute_l10n_at_geringfuegig(self):
|
||
for slip in self:
|
||
try:
|
||
slip.l10n_at_geringfuegig = slip._l10n_at_geringfuegig()
|
||
except Exception:
|
||
# Alter Lauf ohne GFG-Parameterzeile: keine Bewertung.
|
||
slip.l10n_at_geringfuegig = False
|
||
|
||
# ------------------------------------------------------------------
|
||
# Geringfügigkeit (§ 5 Abs 2 ASVG; SZ zählen nicht — lb-bes-06)
|
||
# ------------------------------------------------------------------
|
||
def _l10n_at_geringfuegig(self, result_rules=None):
|
||
"""Hochgerechnetes laufendes Entgelt ≤ GFG (2026: 551,10 € —
|
||
Aufwertung gem. § 810 Abs 3 ASVG ausgesetzt). Lehrlinge sind
|
||
nie geringfügig (§ 5 Abs 3 ASVG). Verhältnisse kürzer als ein
|
||
Naturalmonat: Gesamtentgelt direkt gegen die Monatsgrenze."""
|
||
self.ensure_one()
|
||
version = self.version_id
|
||
if version.l10n_at_lehrling:
|
||
return False
|
||
roh = self._l10n_at_sv_basis_roh(result_rules)
|
||
if roh <= 0.0:
|
||
# Ohne laufenden Bezug keine Statusbewertung (z. B.
|
||
# reiner SZ-Lauf): Normalbehandlung der SZ-Beiträge.
|
||
return False
|
||
if (version.contract_date_start and version.contract_date_end
|
||
and (version.contract_date_end - version.contract_date_start)
|
||
.days < 29):
|
||
vergleich = roh
|
||
else:
|
||
tage = self._l10n_at_lohnsteuertage()
|
||
vergleich = roh * 30.0 / max(tage, 1) if tage < 30 else roh
|
||
gfg = float(self.env['hr.rule.parameter']
|
||
._get_parameter_from_code(
|
||
'at_sv_gfg_monatlich', self.date_to,
|
||
raise_if_not_found=False) or 0.0)
|
||
return vergleich <= gfg
|
||
|
||
# ------------------------------------------------------------------
|
||
# Beitragsfreie Bezüge § 49 Abs 3 ASVG (Jahres-Kumulative je DN)
|
||
# ------------------------------------------------------------------
|
||
def _l10n_at_sv49_ytd(self, kategorie, menge=False):
|
||
"""Jahresverbrauch einer §-49-Kategorie aus früheren validierten
|
||
Läufen (refundbewusst über _l10n_at_ytd_slaeufe)."""
|
||
self.ensure_one()
|
||
total = 0.0
|
||
for slip in self._l10n_at_ytd_slaeufe():
|
||
linien = slip.line_ids.filtered(
|
||
lambda l: l.salary_rule_id.l10n_at_sv49_frei == kategorie)
|
||
if menge:
|
||
total += sum(linien.mapped('quantity'))
|
||
else:
|
||
total += sum(linien.mapped('total'))
|
||
return total
|
||
|
||
def _l10n_at_sv49_freie_laufen(self, result_rules=None):
|
||
"""Beitragsfreie Anteile laufender Bezüge dieses LZP: je Kategorie
|
||
Tages-/Jahresgrenzen (at_sv49_grenzen) minus Jahresverbrauch;
|
||
Überschreiten = beitragspflichtig (Umwidmung, § 49 Abs 3)."""
|
||
self.ensure_one()
|
||
grenzen = self.env['hr.rule.parameter']._get_parameter_from_code(
|
||
'at_sv49_grenzen', self.date_to, raise_if_not_found=False)
|
||
if not grenzen:
|
||
return 0.0
|
||
frei_gesamt = 0.0
|
||
for rule in self.struct_id.rule_ids:
|
||
kategorie = rule.l10n_at_sv49_frei
|
||
if (not kategorie or kategorie == 'keine'
|
||
or self._l10n_at_sv_art(rule) != 'laufend'):
|
||
continue
|
||
werte = None
|
||
if result_rules is not None:
|
||
if rule.code in result_rules:
|
||
werte = result_rules[rule.code]
|
||
else:
|
||
linie = self.line_ids.filtered(lambda l: l.code == rule.code)
|
||
if linie:
|
||
werte = {'total': sum(linie.mapped('total')),
|
||
'quantity': sum(linie.mapped('quantity'))}
|
||
if not werte:
|
||
continue
|
||
total = werte['total']
|
||
menge = werte.get('quantity', 0.0)
|
||
if kategorie == 'mahlzeit':
|
||
frei = min(
|
||
total, float(grenzen.get('mahlzeit_tag')) * menge)
|
||
elif kategorie == 'homeoffice':
|
||
rest_tage = max(
|
||
float(grenzen.get('homeoffice_tage'))
|
||
- self._l10n_at_sv49_ytd('homeoffice', menge=True), 0.0)
|
||
frei = min(
|
||
total,
|
||
float(grenzen.get('homeoffice_tag'))
|
||
* min(menge, rest_tage))
|
||
else:
|
||
pool = {'rabatt': 'rabatt_jahr',
|
||
'betriebsveranstaltung': 'betriebsveranstaltung_jahr',
|
||
'jubilaeum': 'jubilaeum_jahr',
|
||
'zukunftssicherung': 'zukunftssicherung_jahr',
|
||
'kinderbetreuung': 'kinderbetreuung_jahr'}.get(
|
||
kategorie)
|
||
rest = max(
|
||
float(grenzen.get(pool))
|
||
- self._l10n_at_sv49_ytd(kategorie), 0.0)
|
||
frei = min(total, rest)
|
||
frei_gesamt += max(frei, 0.0)
|
||
return frei_gesamt
|
||
|
||
# ------------------------------------------------------------------
|
||
# Laufende Beitragsgrundlage — je Beitragsmonat (Anspruchsprinzip)
|
||
# ------------------------------------------------------------------
|
||
def _l10n_at_sv_art(self, rule):
|
||
"""Effektive SV-Art einer Regel; Standard folgt der LSt-Art."""
|
||
self.ensure_one()
|
||
return (rule.l10n_at_bemessung if rule.l10n_at_sv_art == 'folgt'
|
||
else rule.l10n_at_sv_art)
|
||
|
||
def _l10n_at_sv_brutto(self, art, result_rules=None):
|
||
"""SV-Brutto je Art; abweichende SV-Zuordnung bleibt von der
|
||
LSt-Bemessungsart getrennt (Drittlohn/Sonderfälle)."""
|
||
self.ensure_one()
|
||
total = 0.0
|
||
for rule in self.struct_id.rule_ids:
|
||
if self._l10n_at_sv_art(rule) != art:
|
||
continue
|
||
if result_rules is not None:
|
||
if rule.code in result_rules:
|
||
total += result_rules[rule.code].get('total', 0.0)
|
||
else:
|
||
total += sum(self.line_ids.filtered(
|
||
lambda l: l.code == rule.code).mapped('total'))
|
||
return total
|
||
|
||
def _l10n_at_sv_basis_roh(self, result_rules=None):
|
||
"""Laufendes SV-Brutto minus beitragsfreie §-49-Anteile
|
||
(vor HBG-Cap; Geringfügigkeits-/Staffel-Basis)."""
|
||
self.ensure_one()
|
||
roh = self._l10n_at_sv_brutto('laufend', result_rules) \
|
||
- self._l10n_at_sv49_freie_laufen(result_rules)
|
||
return max(roh, 0.0)
|
||
|
||
def _l10n_at_sv_basis_laufen_monate(self, result_rules=None):
|
||
"""[(Beitragsmonat, HBG-gecappte Basis), …]: laufender Arbeitslohn
|
||
(§-49-bereinigt), gruppiert nach Regel-Beitragsmonat; HBG je
|
||
Beitragsmonat historisch aufgelöst (at_sv_hbg_monatlich)."""
|
||
self.ensure_one()
|
||
gruppen = {}
|
||
for rule in self.struct_id.rule_ids:
|
||
if self._l10n_at_sv_art(rule) != 'laufend':
|
||
continue
|
||
werte = None
|
||
if result_rules is not None:
|
||
if rule.code in result_rules:
|
||
werte = result_rules[rule.code]
|
||
else:
|
||
linie = self.line_ids.filtered(lambda l: l.code == rule.code)
|
||
if linie:
|
||
werte = {'total': sum(linie.mapped('total'))}
|
||
if not werte:
|
||
continue
|
||
monat = (rule.l10n_at_beitragsmonat or self.date_from) \
|
||
.replace(day=1)
|
||
gruppen[monat] = gruppen.get(monat, 0.0) + werte['total']
|
||
standard = self.date_from.replace(day=1)
|
||
if standard in gruppen:
|
||
gruppen[standard] -= self._l10n_at_sv49_freie_laufen(
|
||
result_rules)
|
||
ergebnis = []
|
||
for monat, basis in sorted(gruppen.items()):
|
||
hbg = float(self._l10n_at_param('at_sv_hbg_monatlich', monat))
|
||
ergebnis.append((monat, min(max(basis, 0.0), hbg)))
|
||
return ergebnis
|
||
|
||
def _l10n_at_svdn_basis_laufen(self, result_rules=None):
|
||
"""SV-Beitragsgrundlage laufend: Summe der je Beitragsmonat
|
||
HBG-gecappten, §-49-bereinigten Basen (at_sv_*)."""
|
||
self.ensure_one()
|
||
return sum(basis for _monat, basis
|
||
in self._l10n_at_sv_basis_laufen_monate(result_rules))
|
||
|
||
# ------------------------------------------------------------------
|
||
# Effektive Beitragssätze (Alter/Stichtäge, Wien-WF, ALV-DN-Staffel)
|
||
# ------------------------------------------------------------------
|
||
def _l10n_at_sv_satz(self, seite, komponente, monat=None,
|
||
staffel_basis=None):
|
||
"""Effektiver Satz (vH) je Seite ('dn'/'dg') und Komponente
|
||
(kv/pv/av/uv/ak/wf/iesg) zum Beitragsmonat, unter Berücksich-
|
||
tigung der Stichtags-Regeln am Vertrag und der ALV-DN-Staffel
|
||
(§ 2a AMPFG: reduziert nur den DN-Anteil; Grenzen nicht zu
|
||
aliquotieren; gilt auch für SZ-Beiträge)."""
|
||
self.ensure_one()
|
||
version = self.version_id
|
||
monat = monat or self.date_from
|
||
if komponente == 'wf':
|
||
vektor = self.env['hr.rule.parameter']._get_parameter_from_code(
|
||
'at_sv_wf_vektor', monat, raise_if_not_found=False)
|
||
satz = None
|
||
if vektor:
|
||
wien = (self.company_id.l10n_at_wf_wien
|
||
or self.company_id.l10n_at_bundesland == 'wien')
|
||
eintrag = vektor.get('wien' if wien else 'default') \
|
||
or vektor.get('default')
|
||
if eintrag:
|
||
satz = float(eintrag.get(seite, 0.0))
|
||
if satz is None:
|
||
satz = float(self.env['hr.rule.parameter']
|
||
._get_parameter_from_code(
|
||
'at_sv_wf_%s' % seite, monat,
|
||
raise_if_not_found=False) or 0.0)
|
||
return satz
|
||
satz = float(self._l10n_at_param(
|
||
'at_sv_%s_%s' % (komponente, seite), monat))
|
||
entfall_alv = (version.l10n_at_alv_entfall_ab
|
||
and monat >= version.l10n_at_alv_entfall_ab)
|
||
if komponente in ('av', 'iesg') and entfall_alv:
|
||
# AV DN+DG entfallen (AlVG § 1 Abs 2 lit e) — der IESG-
|
||
# Zuschlag folgt dem AV-Beitrag (§ 12 Abs 1 Z 4 iVm
|
||
# § 12 Abs 2 IESG).
|
||
return 0.0
|
||
if komponente == 'uv':
|
||
if version.l10n_at_lehrling \
|
||
or (version.l10n_at_uv_entfall_ab
|
||
and monat >= version.l10n_at_uv_entfall_ab):
|
||
return 0.0
|
||
if komponente == 'ak' and version.l10n_at_lehrling:
|
||
return 0.0
|
||
if komponente == 'iesg' and version.l10n_at_lehrling:
|
||
# § 12 Abs 2 IESG: für Lehrlinge gesamte Lehrzeit kein Zuschlag
|
||
return 0.0
|
||
if komponente == 'pv' and (
|
||
version.l10n_at_pv_bonusphase_ab
|
||
and monat >= version.l10n_at_pv_bonusphase_ab):
|
||
faktor = float(self.env['hr.rule.parameter']
|
||
._get_parameter_from_code(
|
||
'at_pv_bonus_faktor', monat,
|
||
raise_if_not_found=False) or 0.5)
|
||
satz *= faktor
|
||
if komponente == 'av' and seite == 'dn':
|
||
staffel = self.env['hr.rule.parameter']._get_parameter_from_code(
|
||
'at_sv_av_staffel_ampfg', monat, raise_if_not_found=False)
|
||
if staffel and staffel.get('tiers'):
|
||
basis = staffel_basis if staffel_basis is not None \
|
||
else self._l10n_at_sv_basis_roh()
|
||
if basis > 0:
|
||
for _von, bis, dn_satz in staffel['tiers']:
|
||
if basis <= float(bis):
|
||
return float(dn_satz)
|
||
# über der obersten Stufe: voller DN-Satz
|
||
return float(staffel.get('dn_voll', satz))
|
||
return satz
|
||
|
||
# ------------------------------------------------------------------
|
||
# Beiträge laufend (DN- wie DG-seitig; geringfügig = 0)
|
||
# ------------------------------------------------------------------
|
||
def _l10n_at_sv_beitrag(self, seite, komponente, result_rules=None):
|
||
"""Beitragssumme je Beitragsmonat: Basis × historischen Satz;
|
||
geringfügige Beschäftigung → 0 (stattdessen §-53a-UV, s. u.)."""
|
||
self.ensure_one()
|
||
if self._l10n_at_geringfuegig(result_rules):
|
||
return 0.0
|
||
total = 0.0
|
||
for monat, basis in self._l10n_at_sv_basis_laufen_monate(
|
||
result_rules):
|
||
satz = self._l10n_at_sv_satz(seite, komponente, monat, basis)
|
||
total += basis * satz / 100.0
|
||
return total
|
||
|
||
def _l10n_at_svdn(self, komponente, result_rules=None):
|
||
"""DN-SV-Komponente laufend: kv/pv/av/ak/wf — AK (§ 61 AKG) und
|
||
WF nur laufend, HBGL-Cap; Wien über WF-Satzvektor."""
|
||
self.ensure_one()
|
||
return self._l10n_at_sv_beitrag('dn', komponente, result_rules)
|
||
|
||
def _l10n_at_svdg(self, komponente, result_rules=None):
|
||
"""DG-SV-Komponente laufend: kv/pv/av/uv/wf/iesg (AK = 0);
|
||
ÖGK-Tarif „Normalbetriebe" 2026: DG-Gesamt 20,98 % (Wien 21,23)."""
|
||
self.ensure_one()
|
||
return self._l10n_at_sv_beitrag('dg', komponente, result_rules)
|
||
|
||
def _l10n_at_svdg_gesamt(self, result_rules=None):
|
||
self.ensure_one()
|
||
return sum(self._l10n_at_svdg(k, result_rules)
|
||
for k in ('kv', 'pv', 'av', 'uv', 'wf', 'iesg'))
|
||
|
||
# ------------------------------------------------------------------
|
||
# Beiträge auf sonstigen Bezügen (§ 54 ASVG; 60faches HBG je Jahr)
|
||
# ------------------------------------------------------------------
|
||
def _l10n_at_svdg_sz(self, komponente, result_rules=None):
|
||
"""DG-Beitrag auf sonstigen Bezügen: KV/PV/AV/UV und IESG-Zuschlag
|
||
(folgt dem AV-Beitrag) — ohne AK/WF (§ 62 Z 3/Z 5; lb-son-03:
|
||
DN 17,07 %; DG 20,48 % inkl. UV 1,10 + IESG 0,10)."""
|
||
self.ensure_one()
|
||
if self._l10n_at_geringfuegig(result_rules):
|
||
return 0.0
|
||
basis = self._l10n_at_svdn_basis_sz(result_rules)
|
||
satz = self._l10n_at_sv_satz(
|
||
'dg', komponente, staffel_basis=self._l10n_at_sv_basis_roh(
|
||
result_rules))
|
||
return basis * satz / 100.0
|
||
|
||
def _l10n_at_svdg_sz_gesamt(self, result_rules=None):
|
||
self.ensure_one()
|
||
return sum(self._l10n_at_svdg_sz(k, result_rules)
|
||
for k in ('kv', 'pv', 'av', 'uv', 'iesg'))
|
||
|
||
# ------------------------------------------------------------------
|
||
# § 53a ASVG — Geringfügig Beschäftigte
|
||
# ------------------------------------------------------------------
|
||
def _l10n_at_svdg_uv_geringfuegig(self, result_rules=None):
|
||
"""DG-UV geringfügig Beschäftigter: 1,1 % der allgemeinen
|
||
Beitragsgrundlage (§ 53a Abs 1 ASVG) als Monatspauschale
|
||
(at_sv_geringfuegig['beitrag_uv']; aBG 2026 = 83,49 ÷ 14,12 % =
|
||
591,29 → 6,50 € — abgeleitet, s. Rechenfälle). Entfällt bei
|
||
UV-Stichtag (60. LJ) bzw. Lehrlingen (§ 51 Abs 6 ASVG)."""
|
||
self.ensure_one()
|
||
if not self._l10n_at_geringfuegig(result_rules):
|
||
return 0.0
|
||
if self._l10n_at_sv_satz('dg', 'uv') <= 0.0:
|
||
return 0.0
|
||
params = self.env['hr.rule.parameter']._get_parameter_from_code(
|
||
'at_sv_geringfuegig', self.date_to, raise_if_not_found=False) \
|
||
or {}
|
||
return float(params.get('beitrag_uv', 0.0)) |