Files
odoo-at-payroll/addons/l10n_at_hr_payroll_private/wizard/bv_endabrechnung.py
T
fegger 0ea7f54682 [IMP] l10n_at_hr_payroll_private: GP5 Abfertigung Neu (BVG-Regel, eigene BV-Grundlage, Anwartschaft/Report/Wizard)
Laufender Beitrag (§ 6 BMSVG): Regel BVG (Kategorie ATP_DG, ohne Nettoeffekt) auf EIGENER, ungecappter BV-Grundlage — §-49-bereinigt, ohne HBG-/Jahresdeckel (§ 6 Abs 5 iVm § 49 ASVG) und ohne §-67-Abs-3/6-Zeilen (§ 49 Abs 3 Z 7 ASVG); bewusst kein Rückgriff auf die SV-SZ-Basis des Kerns (Nutzer-Entscheidung eigene BV-Grundlage; deren Z-7-Ausschluss fehlt — GP4-Beobachtung bleibt offen dokumentiert).

Beitragsfreier erster Monat taggenau: Fenster [Eintritt, Eintritt + 1 Monat), Beitragspflicht ab Monatsjubiläum (15. 06. → 15. 07.; KB-Beispiel lb-vor-03: 4.500/30 × 17 = 2.550 — die ab-Folgetag-Lesart war ein Off-by-one und wurde verworfen); 30-Tage-Aliquotierung über die Lohnsteuertage (Teilmonate ohne Doppelabzug); Monatsende-Klippe (Eintritt 31. 01.): Fenster bis Monatsultimo, Pflicht ab Folgemonatserstem (ABGB-Fristrechnung); AV ≤ 1 Monat zur Gänze beitragsfrei.

Wiederzulassung binnen 12 Monaten → Beitragspflicht ab dem 1. Tag; Vor-AV beliebiger Dauer (auch < 1 Monat, Nutzer-Klarstellung) und schlägt auch die Kurz-AV-Freiheit des laufenden AV (OGH 9 ObA 30/16a; Suche über frühere hr.version mit contract_date_end im 12-Monats-Fenster). Sonderzahlungen werden nicht um das Fenster gekürzt, bleiben frei nur bei Beitragsmonat (voller Datumstag, taggenau) im Fenster. Geringfügig Beschäftigte und Lehrlinge sind erfasst — BVG läuft auch im §-53a-Fall.

Anwartschaft (§ 15: gutgebrachte Beiträge + Erträge, keine Monatsentgelt-Staffel) on-the-fly aus validierten Läufen; Anwartschaftsreport (QWeb auf hr.employee) und Endabrechnungs-Wizard (§§ 14-17: §-14-Abs-2-Verfügungsmatrix, < 36 Beitragsmonate sperrt immer, §-16-Fälligkeit am Ende des zweitfolgenden Monats, 6-Monats-Erklärungsfrist, 5 Verfügungsoptionen) — die Auszahlung erfolgt durch die MV-Kasse, nicht über den Payslip.

Stammdaten: Vertrags-Flag l10n_at_abfertigung_neu (§ 46) + Modell l10n.at.bv.kasse (Leitzahl für eSV/mBGM; Seed der 7 Kassen-Namen noupdate, lb-gsv-11 — Leitzahlen aus Mandats-Unterlagen) + Mitarbeiter-Felder l10n_at_bv_ertraege/l10n_at_bv_monate_extern (Kassen-Auszug).

Bewusst nicht: §-7-BMSVG-Fiktivfälle (dokumentierte Option), Geringfügigen-Jahreszahlung + 2,5 % (GP7), BV-Provider (Option).

Tests: test_bv_private (10 Fälle, RF-BV1-10); gesamt 84/84 grün (frischer -i-Lauf auf Wegwerf-DB). Rechenfälle docs/rechenfaelle/abfertigung.md. 19.0.5.0.0.
2026-09-12 08:22:13 +02:00

156 lines
7.4 KiB
Python

# Part of the odoo-at-payroll project. License: LGPL-3.
"""BV-Endabrechnungs-Wizard (GP5): Anwartschaftsausweis bei Austritt
mit §-14-Abs-2-Verfügbarkeitsprüfung und Fristenausweis (§§ 16 f.
BMSVG). Reiner Informations-/Dokumentationsprozess — die Auszahlung
der Anwartschaft erfolgt durch die MV-Kasse (§ 15: gutgebrachte
Beiträge + Erträge, KEINE Monatsentgelt-Staffel), nicht über die
Lohnverrechnung. Verfügungsmatrix (lb-end-03, Stand 2026-08):
Verfügungsausschluss bei AN-Kündigung (außer MSchG/VKG-Teilzeit-
Kündigung), verschuldeter Entlassung, unberechtigtem vorzeitigem
Austritt — und IMMER bei weniger als 36 Beitragsmonaten seit der
ersten Beitragszahlung bzw. letztmaligen Verfügung (alle
Beitragszeiten zählen, Vor-Arbeitgeber manuell erfassen)."""
from datetime import timedelta
from dateutil.relativedelta import relativedelta
from odoo import _, api, fields, models
ABS14_GRUPPEN = ('an_kuendigung', 'verschuldete_entlassung',
'unberechtigter_austritt')
class L10nAtBvEndabrechnungWizard(models.TransientModel):
_name = 'l10n.at.bv.endabrechnung.wizard'
_description = 'BV-Endabrechnung (Abfertigung Neu, §§ 14-17 BMSVG)'
employee_id = fields.Many2one(
'hr.employee', string='Dienstnehmer:in', required=True)
austrittsdatum = fields.Date(
string='Austritt (Ende des Arbeitsverhältnisses)',
help='Beginn der 6-Monats-Erklärungsfrist (§ 17 BMSVG): gibt der '
'Berechtigte binnen sechs Monaten keine '
'Verwendungserklärung ab, wird weiterveranlagt.')
beendigungsart = fields.Selection(
[('ag_kuendigung', 'Kündigung durch den Arbeitgeber (unschädlich)'),
('einvernehmlich', 'Einvernehmliche Auflösung (unschädlich)'),
('berechtigter_austritt',
'Berechtigter vorzeitiger Austritt (unschädlich)'),
('unverschuldete_entlassung',
'Unverschuldete/unrechtmäßige Entlassung (unschädlich)'),
('an_kuendigung', 'Kündigung durch den AN (§ 14 Abs 2 BMSVG)'),
('verschuldete_entlassung',
'Verschuldete Entlassung (§ 14 Abs 2 BMSVG)'),
('unberechtigter_austritt',
'Unberechtigter vorzeitiger Austritt (§ 14 Abs 2 BMSVG)'),
('tod', 'Tod (ungekürzt an unterhaltsberechtigte Erben)')],
string='Beendigungsart', required=True)
monate_extern = fields.Integer(
string='Beitragsmonate Vor-Arbeitgeber',
help='Beitragsmonate aus Vor-Beschäftigungen laut Auszug der '
'MV-Kasse (§ 14 Abs 2 BMSVG: alle Beitragszeiten über '
'Arbeitgeber hinweg summierbar). Vorbelegt aus dem '
'Mitarbeiter-Stamm.')
ertraege = fields.Monetary(
string='Erträge laut Kassen-Auszug',
currency_field='currency_id',
help='Veranlagungserträge laut Auszug der MV-Kasse; die '
'Anwartschaft = gutgebrachte Beiträge + Erträge (§ 15 '
'BMSVG).')
currency_id = fields.Many2one(
'res.currency',
default=lambda self: self.env.company.currency_id)
geltendmachung = fields.Date(
string='Geltendmachung',
default=fields.Date.today,
help='Datum der Geltendmachung gegenüber dem Arbeitgeber; die '
'Fälligkeit der Auszahlung tritt am Ende des '
'zweitfolgenden Monats ein (§ 16 BMSVG).')
kasse_id = fields.Many2one(
'l10n.at.bv.kasse', string='Mitarbeitervorsorgekasse',
compute='_compute_ergebnis')
monate_lokal = fields.Integer(
string='Beitragsmonate (dieser DG)',
compute='_compute_ergebnis',
help='Beitragsmonate aus validierten Lohnläufen mit BVG-Beitrag.')
beitraege_lokal = fields.Monetary(
string='Gutgebrachte Beiträge (dieser DG)',
currency_field='currency_id', compute='_compute_ergebnis')
monate_gesamt = fields.Integer(
string='Beitragsmonate gesamt', compute='_compute_ergebnis',
help='Beitragsmonate gesamt inkl. Vor-Arbeitgeber — '
'36-Monats-Kriterium des § 14 Abs 2 BMSVG.')
anwartschaft = fields.Monetary(
string='Anwartschaft (Beiträge + Erträge)',
currency_field='currency_id', compute='_compute_ergebnis')
verfuegbar = fields.Boolean(
string='Verfügbar (§ 14 Abs 2 BMSVG)', compute='_compute_ergebnis')
sperrgrund = fields.Text(
string='Verfügungsausschluss', compute='_compute_ergebnis')
faelligkeit = fields.Date(
string='Fälligkeit Auszahlung (§ 16 BMSVG)',
compute='_compute_ergebnis')
erklaerungsfrist = fields.Date(
string='Erklärungsfrist (§ 17 BMSVG)', compute='_compute_ergebnis')
@api.onchange('employee_id')
def _onchange_employee(self):
for wizard in self:
if not wizard.employee_id:
continue
wizard.austrittsdatum = (
wizard.employee_id.version_id.contract_date_end
or fields.Date.today())
wizard.monate_extern = \
wizard.employee_id.l10n_at_bv_monate_extern
wizard.ertraege = wizard.employee_id.l10n_at_bv_ertraege
@api.depends('employee_id', 'beendigungsart', 'monate_extern',
'ertraege', 'geltendmachung', 'austrittsdatum')
def _compute_ergebnis(self):
for wizard in self:
if wizard.employee_id:
fort = wizard.employee_id._at_bv_anwartschaft()
wizard.kasse_id = wizard.employee_id.l10n_at_bv_kasse_id
wizard.monate_lokal = fort['monate']
wizard.beitraege_lokal = fort['beitraege']
else:
wizard.kasse_id = False
wizard.monate_lokal = 0
wizard.beitraege_lokal = 0.0
wizard.monate_gesamt = wizard.monate_lokal \
+ wizard.monate_extern
wizard.anwartschaft = wizard.beitraege_lokal + wizard.ertraege
grund = False
if wizard.beendigungsart in ABS14_GRUPPEN:
grund = _('Verfügungsausschluss nach § 14 Abs 2 BMSVG '
'wegen der Beendigungsart (Kündigung durch '
'den AN, verschuldete Entlassung oder '
'unberechtigter vorzeitiger Austritt); die '
'Anwartschaft bleibt bei der MV-Kasse '
'weiterveranlagt.')
elif wizard.monate_gesamt < 36:
grund = _('Verfügungsausschluss nach § 14 Abs 2 BMSVG: '
'noch keine 36 Beitragsmonate (%(monaten)s von '
'36; alle Beitragszeiten zählen).',
monaten=wizard.monate_gesamt)
wizard.verfuegbar = not grund
wizard.sperrgrund = grund
if wizard.geltendmachung:
# § 16: Fälligkeit am Ende des zweitfolgenden Monats.
zweit = wizard.geltendmachung + relativedelta(months=2)
wizard.faelligkeit = (zweit.replace(day=1)
+ relativedelta(months=1)
- timedelta(days=1))
else:
wizard.faelligkeit = False
wizard.erklaerungsfrist = (
wizard.austrittsdatum + relativedelta(months=6)
if wizard.austrittsdatum else False)
def action_report(self):
self.ensure_one()
return self.env.ref(
'l10n_at_hr_payroll_private.action_report_bv_anwartschaft'
).report_action(self.employee_id)