[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.
This commit is contained in:
@@ -67,13 +67,44 @@ Abs 6/7 ASVG, § 12 IESG), WF-Satzvektor Wien, Beitragsmonats-
|
||||
Allokation (Anspruchsprinzip) und DAG-Bewertung (§ 1 DAG).
|
||||
Rechenfälle `docs/rechenfaelle/sozialversicherung.md`.
|
||||
|
||||
**Offen (GP5+):** Abfertigung Neu (BMSVG), Meldewesen/Belege,
|
||||
FI-Buchung UGB; All-in-UI-Bericht (GP8); B14 längster-Urlaubs-Teil-
|
||||
Verfeinerung; DZ-WKO-Verifikation (8.11) vor Wien-Mandanten.
|
||||
**GP5 Abfertigung Neu (umgesetzt 2026-09-11):** BMSVG-Regel `BVG`
|
||||
(§ 6 Abs 1: 1,53 % DG auf Entgelt **und** SZ; Kern-Parameter
|
||||
`at_sv_bv_dg`) auf **EIGENER, ungecappter BV-Grundlage** (§ 6 Abs 5
|
||||
iVm § 49 ASVG — kein HBG-/Jahresdeckel, ohne §-67-Abs-3/6-Zeilen gemäß
|
||||
§ 49 Abs 3 Z 7 ASVG) — `models/abfertigung_private.py`:
|
||||
|
||||
- **Beitragsfreier erster Monat taggenau** (§ 6 Abs 1): Fenster
|
||||
[Eintritt, Eintritt + 1 Monat), Pflicht ab Monatsjubiläum
|
||||
(15. 06. → 15. 07.); 30-Tage-Aliquotierung (KB 4.500/30 × 17 =
|
||||
2.550); Monatsende-Klippe (31. 01.) → frei bis Monatsultimo,
|
||||
Pflicht ab Folgemonatserstem; AV ≤ 1 Monat zur Gänze beitragsfrei;
|
||||
**Wiederzulassung binnen 12 Monaten → Pflicht ab dem 1. Tag**
|
||||
(OGH 9 ObA 30/16a; Vor-AV beliebiger Dauer — auch < 1 Monat);
|
||||
- **SZ ohne Fenster-Kürzung**; frei nur bei Beitragsmonat im Fenster
|
||||
(taggenau); läuft **auch für geringfügig Beschäftigte und
|
||||
Lehrlinge** (§ 46, § 1 Abs 2 Z 1);
|
||||
- **Anwartschaft** (§ 15: gutgebrachte Beiträge + Erträge aus dem
|
||||
Kassen-Auszug, KEINE Monatsentgelt-Staffel) aus validierten
|
||||
Läufen; Vertragsfelder Abfertigung-Neu/BV-Kasse
|
||||
(`l10n.at.bv.kasse` mit Leitzahl für eSV/mBGM) + Erträge/Vor-Beitrags-
|
||||
monate am Mitarbeiter; **Anwartschaftsreport** (QWeb, am Mitarbeiter
|
||||
druckbar) + **Endabrechnungs-Wizard** (§§ 14-17: §-14-Abs-2-
|
||||
Verfügungsmatrix mit 36-Beitragsmonate-Kriterium, §-16-Fälligkeit,
|
||||
6-Monats-Erklärungsfrist, 5 §-17-Optionen) — Auszahlung selbst
|
||||
läuft über die MV-Kasse, nicht über den Payslip.
|
||||
|
||||
Rechenfälle `docs/rechenfaelle/abfertigung.md`.
|
||||
|
||||
**Offen (GP6+):** Meldewesen/Belege (GP7), FI-Buchung UGB (GP6),
|
||||
Jahreswechsel (GP8); All-in-UI-Bericht (GP8); B14 längster-Urlaubs-Teil-
|
||||
Verfeinerung; DZ-WKO-Verifikation (8.11) vor Wien-Mandanten;
|
||||
§-7-BMSVG-Fiktivfälle (Präsenzdienst/Kranken-/Wochengeld) als
|
||||
dokumentierte Option (Rechenfälle-Datei).
|
||||
|
||||
Tests: `test_kv_katalog` + `test_entgelt_private` (GP2) +
|
||||
`test_lst_private` (GP3) + `test_sv_private` (GP4) —
|
||||
`test_lst_private` (GP3) + `test_sv_private` (GP4) +
|
||||
`test_bv_private` (GP5) —
|
||||
`.venv/bin/python -m odoo -d <testdb> -u l10n_at_hr_payroll,l10n_at_hr_payroll_private
|
||||
--test-tags /l10n_at_hr_payroll,/l10n_at_hr_payroll_private --test-enable --stop-after-init
|
||||
--addons-path=odoo_19.0+e.20260910/odoo-19.0+e.20260910/odoo/addons,addons`
|
||||
(Lokal grün am 2026-09-11: **74/74 Tests**.)
|
||||
(Lokal grün am 2026-09-11: **84/84 Tests**.)
|
||||
@@ -1,7 +1,7 @@
|
||||
# Part of the odoo-at-payroll project. License: LGPL-3.
|
||||
{
|
||||
'name': 'Österreich - Privatwirtschaft Personalverrechnung (General-AT)',
|
||||
'version': '19.0.4.0.0',
|
||||
'version': '19.0.5.0.0',
|
||||
'author': 'ixsol / gem360',
|
||||
'website': 'https://gem360.at',
|
||||
'category': 'Human Resources/Payroll',
|
||||
@@ -34,18 +34,30 @@ Grundlagen: ``personalverrechnung/IMPLEMENTIERUNGSPLAN-Privat.md`` und
|
||||
- **GP3 Lohnsteuer (umgesetzt):** dünne Regeln SVDN_KV/PV/AV/AK/WF +
|
||||
LST_LFD/LST_SZ auf dem AP3-Kern (§ 66 kumulativ, § 67 Sechstel/
|
||||
Fixsätze, § 68-Steuerfreiheiten).
|
||||
- **GP4 SV/DB (dieser Stand):** SVDN_SZ-Nachtrag (§ 67 Abs 12 — Netto-
|
||||
- **GP4 SV/DB (umgesetzt):** SVDN_SZ-Nachtrag (§ 67 Abs 12 — Netto-
|
||||
Korrektur in §-67-Monaten), DG-Block SVDG_KV/PV/AV/UV/WF/IESG +
|
||||
SVDG_SZ_* + §-53a-UV Geringfügiger (ATP_DG, ohne Nettoeffekt;
|
||||
ÖGK 2026: DG 20,98 % / Wien 21,23 %), Lohnnebenkosten FLAFDB
|
||||
(§ 41 FLAG), KOMMST (§ 9 KommStG) und DZ (§§ 122/126 WKG,
|
||||
company-flag-gated) mit Freibetragsstaffel 1.095/1.460 € —
|
||||
Logik/Werte im Bundes-Kern (D3-Vorfinanzierung).
|
||||
- **GP5 Abfertigung Neu (dieser Stand):** BMSVG-Regel ``BVG``
|
||||
(§ 6: 1,53 % DG auf Entgelt und SZ, Kern-Parameter
|
||||
``at_sv_bv_dg``) auf EIGENER, ungecappter BV-Grundlage (§ 6 Abs 5
|
||||
iVm § 49 ASVG — kein HBG-Cap, ohne §-67-Abs-3/6-Zeilen),
|
||||
beitragsfreier erster Monat taggenau (30-Tage-Aliquotierung;
|
||||
Monatsende-Klippe), Wiederzulassung binnen 12 Monaten ab Tag 1
|
||||
(Vor-AV beliebiger Dauer), läuft auch für Geringfügige und
|
||||
Lehrlinge; Vertragsfeld Abfertigung-Neu/BV-Kasse + Erträge/Vor-
|
||||
Monate am Mitarbeiter; Anwartschaftsreport (QWeb) + Endabrechnungs-
|
||||
Wizard (§§ 14-17: Verfügungsausschluss-Matrix, 36-Beitragsmonate,
|
||||
§-16-Fälligkeit, §-17-Optionen).
|
||||
- **Seed D2 (Referenz-KVs R1):** SI-2203 „Handwerk und Gewerbe" +
|
||||
SI-2748 „Metallgewerbe" (Gehaltstabellen ab 1. 1. 2026, kuratiert aus
|
||||
den KV-Library-Volltexten).
|
||||
- **Folgen (GP5+):** Abfertigung Neu (BMSVG), Meldewesen/Belege,
|
||||
FI-Buchung UGB, Jahreswechsel.
|
||||
- **Folgen (GP6+):** FI-Buchung UGB (GP6), Meldewesen/Belege (GP7),
|
||||
Jahreswechsel (GP8); §-7-BMSVG-Fiktivfälle (Präsenzdienst/
|
||||
Kranken-/Wochengeld) als dokumentierte Option.
|
||||
|
||||
Kein VRV-Bezug (Gemeinde-Schiene bleibt ``l10n_at_gemeinde_payroll`` +
|
||||
gem360-Brücke).
|
||||
@@ -63,7 +75,11 @@ gem360-Brücke).
|
||||
'data/salary_rules_private.xml',
|
||||
'data/salary_rules_lst_private.xml',
|
||||
'data/salary_rules_sv_private.xml',
|
||||
'data/salary_rules_bvg_private.xml',
|
||||
'data/bv_kasse_seed.xml',
|
||||
'views/kv_views.xml',
|
||||
'views/bv_views.xml',
|
||||
'report/bv_report.xml',
|
||||
],
|
||||
'countries': ['at'],
|
||||
'license': 'LGPL-3',
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<!-- Seed der am Markt bestehenden Mitarbeitervorsorgekassen — -->
|
||||
<!-- NUR Namen (lb-gsv-11, Stand 2026-07: „derzeit sieben BV- -->
|
||||
<!-- Kassen (Allianz, APK, BONUS, BUAK, NÖ, Valida Plus, VBV)"). -->
|
||||
<!-- Leitzahlen und Kassen-Details bewusst NICHT gesetzt: aus den -->
|
||||
<!-- Kassen-Unterlagen des Mandanten übernehmen (eSV/mBGM, GP7) — -->
|
||||
<!-- niemals geraten (Verify-before-assume). noupdate, damit -->
|
||||
<!-- Mandats-Pflege (Leitzahl) bei Modul-Updates erhalten bleibt. -->
|
||||
<data noupdate="1">
|
||||
<record id="bv_kasse_allianz" model="l10n.at.bv.kasse">
|
||||
<field name="name">Allianz</field>
|
||||
</record>
|
||||
<record id="bv_kasse_apk" model="l10n.at.bv.kasse">
|
||||
<field name="name">APK</field>
|
||||
</record>
|
||||
<record id="bv_kasse_bonus" model="l10n.at.bv.kasse">
|
||||
<field name="name">BONUS</field>
|
||||
</record>
|
||||
<record id="bv_kasse_buak" model="l10n.at.bv.kasse">
|
||||
<field name="name">BUAK</field>
|
||||
</record>
|
||||
<record id="bv_kasse_noe" model="l10n.at.bv.kasse">
|
||||
<field name="name">NÖ</field>
|
||||
</record>
|
||||
<record id="bv_kasse_valida" model="l10n.at.bv.kasse">
|
||||
<field name="name">Valida Plus</field>
|
||||
</record>
|
||||
<record id="bv_kasse_vbv" model="l10n.at.bv.kasse">
|
||||
<field name="name">VBV</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<!-- ============================================================ -->
|
||||
<!-- GP5: Abfertigung Neu (BMSVG) — dünne BVG-Regel für die -->
|
||||
<!-- privatwirtschaftliche Structure. Logik/Werte im Kern -->
|
||||
<!-- (Parameter at_sv_bv_dg, 1,53 %) und im Privatmodul -->
|
||||
<!-- (models/abfertigung_private.py: eigene, ungecappte BV- -->
|
||||
<!-- Grundlage § 6 Abs 5 iVm § 49 ASVG, beitragsfreier erster -->
|
||||
<!-- Monat taggenau, Wiederzulassung, SZ ohne Fenster-Kürzung); -->
|
||||
<!-- hier nur die 1-Zeilen-Bindung (kein Wertefork). -->
|
||||
<!-- Kategorie ATP_DG: reine Dienstgeberkosten ohne Nettoeffekt; -->
|
||||
<!-- läuft bewusst auch für geringfügig Beschäftigte und -->
|
||||
<!-- Lehrlinge (§ 46 BMSVG). -->
|
||||
<!-- ============================================================ -->
|
||||
<record id="rule_bvg" model="hr.salary.rule">
|
||||
<field name="name">BV-Beitrag Abfertigung Neu (§ 6 BMSVG; DG)</field>
|
||||
<field name="code">BVG</field>
|
||||
<field name="sequence" eval="95"/>
|
||||
<field name="category_id" ref="l10n_at_hr_payroll_private.category_atp_dg"/>
|
||||
<field name="struct_id" ref="l10n_at_hr_payroll_private.structure_at_privat"/>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python">result = payslip._at_bv_beitrag(result_rules) > 0</field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="l10n_at_bemessung">neutral</field>
|
||||
<field name="amount_python_compute"><![CDATA[
|
||||
result = payslip._at_bv_beitrag(result_rules)
|
||||
]]></field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -0,0 +1,105 @@
|
||||
# Rechenfälle Abfertigung Neu (GP5) — BMSVG
|
||||
|
||||
**Quellen (✅ verifiziert):** `RECHTSQUELLEN-Privat.md` Abschnitt 8
|
||||
(konsolidierter BMSVG-Volltext, Stand 26. 07. 2025); Layer-2-Belege
|
||||
lb-vor-03 (Beitragszahlung, Stand 2026-08), lb-vor-06 (Geltungs-
|
||||
bereich), lb-end-03 (Verfügungsmöglichkeiten, Stand 2026-08),
|
||||
lb-vor-10 (Übertritt). Umsetzung: Kern-Parameter `at_sv_bv_dg`
|
||||
(`rule_parameters_sv_2026.xml`); privat `models/abfertigung_private.py`
|
||||
+ Regel `BVG` (`salary_rules_bvg_private.xml`) + Endabrechnungs-Wizard
|
||||
`l10n.at.bv.endabrechnung.wizard`. Tests: `test_bv_private.py`.
|
||||
|
||||
## Systematik (GP5, verifiziert)
|
||||
|
||||
- **Beitrag (§ 6 Abs 1):** DG zahlt **1,53 vH** des monatlichen Entgelts
|
||||
**und** der Sonderzahlungen an die Mitarbeitervorsorgekasse (Abfuhr
|
||||
über den KV-Träger, Fälligkeit 15. Folgemonat, Baubereich BUAK —
|
||||
Zahlungs-/Meldeseite GP7).
|
||||
- **Eigene BV-Grundlage (§ 6 Abs 5 iVm § 49 ASVG):** GFG und HBG
|
||||
**unberücksichtigt** → kein HBG-Cap, kein SZ-Jahresdeckel; §-49-
|
||||
bereinigt (laufend: `_l10n_at_sv_basis_roh`; sonstig: alle
|
||||
§-67-Zeilen). **§-67-Abs-3/6-Zeilen (Abfertigung Alt/Abfindung)
|
||||
scheiden aus** (§ 49 Abs 3 Z 7 ASVG, beitragsfrei) — bewusst eigene
|
||||
Grundlage statt Rückgriff auf die SV-SZ-Basis des Kerns, deren
|
||||
Z-7-Ausschluss nicht umgesetzt ist (GP4-Beobachtung, offen
|
||||
dokumentiert).
|
||||
- **Geltungsbereich (§ 46, § 1 Abs 2 Z 1):** private AV ab 1. 1. 2003
|
||||
(Vertrags-Flag `l10n_at_abfertigung_neu`, default True; Alt-Fälle/
|
||||
§ 47-Übertritte abwählen); Länder/Gemeinden ausgenommen (Sektor-
|
||||
Trennung zum GemBG-Modul § 130). **Geringfügig Beschäftigte und
|
||||
Lehrlinge sind erfasst** — BVG läuft auch ohne SV-Hauptbeiträge
|
||||
(§-53a-Fall); Geringfügigen-Jahreszahlung + 2,5 % Zinsenzuschlag =
|
||||
Zahlungsmodus (GP7/GP8).
|
||||
- **Beitragsfreier erster Monat (§ 6 Abs 1), taggenau:**
|
||||
- Fenster = [Eintritt, Eintritt + 1 Monat); **Beitragspflicht ab
|
||||
Monatsjubiläum** (Eintritt 15. 06. → frei 15. 06.–14. 07., Pflicht
|
||||
ab 15. 07.).
|
||||
- **Aliquotierung mit fixem 30-Tage-Monat** (Dachverband; KB-Beispiel
|
||||
4.500/30 × 17 = 2.550): laufende BG × pflichtige Kalendertage ÷
|
||||
Lohnsteuertage (Vollmonat = 30; Teilmonat = Kalendertage, damit das
|
||||
für Teilmonate bereits aliquote Entgelt nicht doppelt gekürzt
|
||||
wird).
|
||||
- **Monatsende-Klippe** (Eintritt 31. 01., Jubiläum 31. 02. existiert
|
||||
nicht): Fenster bis **Monatsultimo**, Pflicht ab **Folgemonats-
|
||||
erstem** (ABGB-Fristrechnung).
|
||||
- **AV ≤ 1 Monat → zur Gänze beitragsfrei** (auch bloße Urlaubs-
|
||||
ersatzleistung; Ende < Jubiläum).
|
||||
- **Wiederzulassung binnen 12 Monaten → Beitragspflicht ab dem
|
||||
1. Tag** (OGH 9 ObA 30/16a); Vor-AV **beliebiger Dauer** (auch
|
||||
< 1 Monat — Nutzer-Entscheidung 2026-09-11) und schlägt auch die
|
||||
Kurz-AV-Freiheit des laufenden AV. Suche über frühere
|
||||
`hr.version` desselben Mitarbeiters mit `contract_date_end` im
|
||||
12-Monats-Fenster vor dem neuen Start.
|
||||
- **Sonderzahlungen:** **keine** aliquote Kürzung um den beitragsfreien
|
||||
Monat; beitragsfrei bleiben sie nur, wenn ihr **Beitragsmonat**
|
||||
(Regelfeld `l10n_at_beitragsmonat`, sonst LZP-Beginn) **im Fenster**
|
||||
liegt — taggenau verglichen (SZ fällig 20. 07. ist bei Fensterende
|
||||
15. 07. pflichtig).
|
||||
- **EOSB (§§ 14-17):** Anwartschaft = **gutgebrachte Beiträge +
|
||||
Erträge** (§ 15 — KEINE Monatsentgelt-Staffel); Erträge aus dem
|
||||
Kassen-Auszug am Mitarbeiter pflegen (`l10n_at_bv_ertraege`).
|
||||
**Verfügungsausschluss (§ 14 Abs 2):** AN-Kündigung (außer
|
||||
MSchG/VKG-Teilzeit-Kündigung), verschuldete Entlassung,
|
||||
unberechtigter vorzeitiger Austritt — und **IMMER bei < 36
|
||||
Beitragsmonaten** (alle Beitragszeiten über Arbeitgeber hinweg
|
||||
zählen; Vor-Monate am Mitarbeiter: `l10n_at_bv_monate_extern`).
|
||||
Fälligkeit am **Ende des zweitfolgenden Monats** nach
|
||||
Geltendmachung (§ 16); **fünf** Verfügungsoptionen, ohne Erklärung
|
||||
binnen **6 Monaten** Weiterveranlagung (§ 17). Auszahlung durch die
|
||||
MV-Kasse — kein Payslip-Bestandteil (Wizard rein informativ).
|
||||
|
||||
## Rechenfälle (Fixtures test_bv_private.py)
|
||||
|
||||
| RF | Konstellation | Berechnung | Erwartung |
|
||||
|---|---|---|---|
|
||||
| RF-BV1 | Bestand (2020), November mit B13 (KV-Katalog 2.059,41) | (2.059,41 + 2.059,41) × 1,53 % | **BVG 63,02 €**; ATP_DG, NETTO unverändert |
|
||||
| RF-BV2 | Entgelt 8.000 € (Jänner, kein B13) | SVDG_KV: HBG 6.930 × 3,78 %; BVG: 8.000 × 1,53 % | SVDG_KV 262,55; **BVG 122,40 €** (kein HBG-Cap) |
|
||||
| RF-BV3 | Eintritt 15. 06. 2026, Entgelt 6.000 € | Juni: Fenster; Juli: 6.000/30 × 17; August: voll | Juni **0** · Juli 3.400 × 1,53 % = **52,02 €** · August **91,80 €** |
|
||||
| RF-BV4 | Vor-AV 01.–15. 06. 2026 (14 Tage), neuer AV ab 01. 07. | Wiederzulassung → Pflicht ab Tag 1 | Juli **91,80 €** (kein freier Monat trotz kurzem Vor-AV) |
|
||||
| RF-BV5 | AV 01.–30. 06. 2026 (genau 1 Monat) | Ende < Jubiläum 01. 07. | Juni **0** (zur Gänze beitragsfrei) |
|
||||
| RF-BV6 | Geringfügig 500 € (Jänner) | §-53a-Fall: SVDG 0, aber BMSVG erfasst | **BVG 7,65 €** |
|
||||
| RF-BV7 | Eintritt 15. 06.; Teststruktur LFD 2.000 / SZ 20. 06. / SZ 20. 07. / Abfertigung-Alt 10.000 (§ 67 Abs 3) | (2.000 × 17/30 + 1.000) × 1,53 %; SZ 20. 06. frei; Alt-Abfertigung ausgeschieden | **BVG 32,64 €** (1.133,33 + 1.000) |
|
||||
| RF-BV8 | 3 validierte Läufe (Jän-März, je 2.059,41); Austritt 31. 08., Geltendmachung 11. 09. | Anwartschaft 3 × 31,51 = 94,53; §-14-Prüfung | Monate 3 → gesperrt; +33 extern → 36, AG-Kündigung verfügbar; AN-Kündigung gesperrt; Fälligkeit **30. 11.**, Erklärungsfrist **28. 02. 2027** |
|
||||
| RF-BV9 | Alt-Verhältnis (Flag False), November | Regel aus | **BVG 0** |
|
||||
| RF-BV10 | Eintritt 31. 01. 2026 | Klippe: Fenster bis 28. 02., Pflicht ab 01. 03. | Februar **0** · März **91,80 €** |
|
||||
|
||||
## Offene Punkte / bewusst nicht in GP5
|
||||
|
||||
- **§ 7 BMSVG-Fiktivfälle** (Präsenz-/Ausbildungs-/Zivildienst:
|
||||
fiktive BG = Kinderbetreuungsgeld 435,90 €/Monat Stand 2026-08;
|
||||
Krankengeld: 50 % der BG vor dem Versicherungsfall ohne SZ;
|
||||
Wochengeld: 3-Monats-Ø inkl. anteiliger SZ): dokumentierte Option —
|
||||
setzt die Modellierung der Entgeltersatzleistungen voraus, die das
|
||||
Modul (noch) nicht hat; kein toter Parameter.
|
||||
- **SV-SZ-Basis des Kerns** (`_l10n_at_svdn_basis_sz`) schließt
|
||||
§-67-Abs-3/6-Zeilen nicht aus (beitragsfrei nach § 49 Abs 3 Z 7
|
||||
ASVG) — GP4-Beobachtung, bewusst nicht in GP5 angefasst
|
||||
(Nutzer-Entscheidung: eigene BV-Grundlage); möglicher Folgefix im
|
||||
SV-Kern separat zu klären.
|
||||
- **Abfuhr/Meldung** (mBGM: Leitzahl BV-Kasse, DG-Kontonummer;
|
||||
Geringfügigen-Jahreszahlung + 2,5 %), **Fristenkalender**
|
||||
(6-Monats-Erklärung, §-16-Fälligkeit als Aktivität), **BV-Provider-
|
||||
Anbindung**: GP7/GP8 bzw. Option.
|
||||
- Wiederzulassung wird je Mitarbeiter in der DB geprüft („gleicher
|
||||
Arbeitgeber" = gleiches Unternehmen angenommen); Multi-Company-
|
||||
Konstellationen sind dokumentierte Grenze.
|
||||
@@ -1,4 +1,7 @@
|
||||
# Part of the odoo-at-payroll project. License: LGPL-3.
|
||||
from . import kollektivvertrag
|
||||
from . import hr_version_private
|
||||
from . import payslip_private
|
||||
from . import payslip_private
|
||||
from . import bv_kasse
|
||||
from . import abfertigung_private
|
||||
from . import bv_report
|
||||
@@ -0,0 +1,257 @@
|
||||
# Part of the odoo-at-payroll project. License: LGPL-3.
|
||||
"""Abfertigung Neu (BMSVG) — GP5: laufender BV-Beitrag (§ 6) mit
|
||||
EIGENER BV-Grundlage, Anwartschafts-Fortschreibung (§ 15) und
|
||||
Endabrechnung (§§ 14-17).
|
||||
|
||||
Verifizierte Grundlagen (RECHTSQUELLEN-Privat Abschn. 8 ✅; KB
|
||||
lb-vor-03/lb-vor-06/lb-end-03, Stand 2026-08):
|
||||
- § 6 Abs 1: DG-Beitrag 1,53 vH des monatlichen Entgelts und der
|
||||
Sonderzahlungen an die Mitarbeitervorsorgekasse (Abfuhr über den
|
||||
KV-Träger, Fälligkeit 15. Folgemonat, Baubereich BUAK — GP7).
|
||||
- § 6 Abs 5 iVm § 49 ASVG: Bemessung ohne GFG und HBG → eigene,
|
||||
ungecappte BV-Grundlage. Bewusst KEIN Rückgriff auf die SV-SZ-Basis
|
||||
des Kerns: deren §-49-Abs-3-Z-7-Ausschluss für §-67-Abs-3/6-Bezüge
|
||||
(Abfertigung/Abfindung) ist dort nicht umgesetzt; die BV-Grundlage
|
||||
schließt diese Zeilen korrekt aus (Nutzer-Entscheidung 2026-09-11).
|
||||
- Geltungsbereich § 46/§ 1 Abs 2 Z 1: private Arbeitsverhältnisse ab
|
||||
1. 1. 2003 (Flag am Vertrag); geringfügig Beschäftigte und Lehrlinge
|
||||
sind ERFASST — BVG läuft auch ohne SV-Hauptbeiträge.
|
||||
- Beitragsfreier erster Monat (§ 6 Abs 1), taggenau: Fenster =
|
||||
[Eintritt, Eintritt + 1 Monat), Beitragspflicht ab Monatsjubiläum
|
||||
(Eintritt 15. 06. → frei bis 14. 07., Pflicht ab 15. 07.; KB-Beispiel
|
||||
4.500/30 × 17 = 2.550). Monatsende-Klippe (Eintritt 31. 01. → kein
|
||||
31. 02.): Fenster bis Monatsultimo, Pflicht ab Folgemonatserstem
|
||||
(ABGB-Fristrechnung). AV ≤ 1 Monat → zur Gänze beitragsfrei (auch
|
||||
Urlaubsersatzleistung). Wiederzulassung binnen 12 Monaten →
|
||||
Beitragspflicht ab dem 1. TAG; Vor-AV beliebiger Dauer (auch
|
||||
< 1 Monat; OGH 9 ObA 30/16a) — schlägt auch die Kurz-AV-Freiheit
|
||||
des laufenden AV (Nutzer-Entscheidung 2026-09-11).
|
||||
- Sonderzahlungen: keine aliquote Kürzung um den beitragsfreien
|
||||
Monat; beitragsfrei nur, wenn ihr Beitragsmonat ins Fenster fällt.
|
||||
- §§ 14-17: Anwartschaft = gutgebrachte Beiträge + Erträge (KEINE
|
||||
Monatsentgelt-Staffel); Verfügungsausschluss bei AN-Kündigung,
|
||||
verschuldeter Entlassung, unberechtigtem vorzeitigem Austritt oder
|
||||
weniger als 36 Beitragsmonaten (alle Beitragszeiten zählen, daher
|
||||
manuelle Vor-Arbeitgeber-Monate am Mitarbeiter); Fälligkeit am Ende
|
||||
des zweitfolgenden Monats nach Geltendmachung (§ 16); fünf
|
||||
Verfügungsoptionen, ohne Erklärung binnen 6 Monaten
|
||||
Weiterveranlagung (§ 17).
|
||||
"""
|
||||
from datetime import timedelta
|
||||
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class HrVersion(models.Model):
|
||||
_inherit = 'hr.version'
|
||||
|
||||
l10n_at_abfertigung_neu = fields.Boolean(
|
||||
string='AT Abfertigung Neu (BMSVG)',
|
||||
default=True,
|
||||
groups="hr_payroll.group_hr_payroll_user", tracking=True,
|
||||
help='Dienstverhältnis unterliegt dem BMSVG (§ 46: AV ab '
|
||||
'1. 1. 2003; § 1 Abs 2 Z 1: Länder/Gemeinden ausgenommen). '
|
||||
'Für Alt-Verhältnisse (§§ 23 f AngG) und § 47-Übertritte '
|
||||
'abwählen — dann läuft keine BVG-Regel. Geringfügig '
|
||||
'Beschäftigte und Lehrlinge sind erfasst.')
|
||||
l10n_at_bv_kasse_id = fields.Many2one(
|
||||
'l10n.at.bv.kasse', string='AT Mitarbeitervorsorgekasse',
|
||||
index=True, groups="hr_payroll.group_hr_payroll_user",
|
||||
tracking=True,
|
||||
help='Gewählte BV-Kasse dieses Dienstverhältnisses (lb-vor-02); '
|
||||
'Anwartschaftskonto und §-17-Verfügungen laufen über diese '
|
||||
'Kasse.')
|
||||
|
||||
|
||||
class HrEmployee(models.Model):
|
||||
_inherit = 'hr.employee'
|
||||
|
||||
l10n_at_abfertigung_neu = fields.Boolean(
|
||||
related="version_id.l10n_at_abfertigung_neu", inherited=True,
|
||||
readonly=False, groups="hr_payroll.group_hr_payroll_user")
|
||||
l10n_at_bv_kasse_id = fields.Many2one(
|
||||
related="version_id.l10n_at_bv_kasse_id", inherited=True,
|
||||
readonly=False, groups="hr_payroll.group_hr_payroll_user")
|
||||
l10n_at_bv_ertraege = fields.Monetary(
|
||||
string='AT BV-Erträge (Kassen-Auszug)',
|
||||
currency_field='currency_id',
|
||||
groups="hr_payroll.group_hr_payroll_user",
|
||||
help='Veranlagungserträge der Mitarbeitervorsorgekasse laut '
|
||||
'Kassen-Auszug (§ 15 BMSVG: Auszahlung der Anwartschaft = '
|
||||
'gutgebrachte Beiträge + Erträge) — manuell pflegen.')
|
||||
l10n_at_bv_monate_extern = fields.Integer(
|
||||
string='AT BV-Beitragsmonate Vor-Arbeitgeber',
|
||||
groups="hr_payroll.group_hr_payroll_user",
|
||||
help='Beitragsmonate aus Vor-Beschäftigungen (§ 14 Abs 2 BMSVG: '
|
||||
'alle Beitragszeiten über Arbeitgeber hinweg summierbar — '
|
||||
'36-Monats-Kriterium) — laut Auszug der MV-Kasse pflegen.')
|
||||
|
||||
def _at_bv_anwartschaft(self):
|
||||
"""Anwartschafts-Fortschreibung (§ 15 BMSVG): gutgebrachte
|
||||
Beiträge aus validierten Läufen, je Beitragsmonat/Jahr
|
||||
gruppiert (Refund-Läufe netten über negative Linien; Monate
|
||||
zählen auch aus negativen Linien — der Monat ist belegt)."""
|
||||
self.ensure_one()
|
||||
monate = set()
|
||||
total = 0.0
|
||||
jahre = {}
|
||||
slips = self.env['hr.payslip'].search([
|
||||
('employee_id', '=', self.id),
|
||||
('state', 'not in', ('draft', 'cancel')),
|
||||
])
|
||||
for line in slips.line_ids.filtered(
|
||||
lambda l: l.code == 'BVG'):
|
||||
if not line.total:
|
||||
continue
|
||||
monat = (line.l10n_at_beitragsmonat
|
||||
or line.date_from).replace(day=1)
|
||||
monate.add(monat)
|
||||
total += line.total
|
||||
eintrag = jahre.setdefault(
|
||||
monat.year, {'monate': set(), 'summe': 0.0})
|
||||
eintrag['monate'].add(monat)
|
||||
eintrag['summe'] += line.total
|
||||
return {
|
||||
'monate': len(monate),
|
||||
'beitraege': total,
|
||||
'jahre': [(jahr, len(w['monate']), w['summe'])
|
||||
for jahr, w in sorted(jahre.items())],
|
||||
}
|
||||
|
||||
|
||||
class HrPayslip(models.Model):
|
||||
_inherit = 'hr.payslip'
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Beitragsfreies Erstfenster (§ 6 Abs 1 BMSVG, taggenau)
|
||||
# ------------------------------------------------------------------
|
||||
def _at_bv_fenster(self):
|
||||
"""(pflichtig_ab, kurz_av) des Arbeitsverhältnisses:
|
||||
pflichtig_ab = erstes beitragspflichtiges Datum (None =
|
||||
Beitragspflicht ab dem 1. Tag bzw. ohne bewertbare
|
||||
Vertragsdaten); kurz_av = True → AV ≤ 1 Monat, zur Gänze
|
||||
beitragsfrei. Reihenfolge: Wiederzulassung schlägt alles,
|
||||
dann Kurz-AV-Freiheit, dann taggenaues Fenster."""
|
||||
self.ensure_one()
|
||||
version = self.version_id
|
||||
start = version.contract_date_start
|
||||
if not start:
|
||||
# Ohne Vertragsdaten ist die Befreiung nicht bewertbar →
|
||||
# konservativ Vollpflicht.
|
||||
return None, False
|
||||
# Wiederzulassung binnen 12 Monaten: Beitragspflicht ab dem
|
||||
# 1. Tag; Vor-Dienstverhältnis beliebiger Dauer (auch
|
||||
# < 1 Monat) löst die Ausnahme aus.
|
||||
prior = self.env['hr.version'].search([
|
||||
('employee_id', '=', self.employee_id.id),
|
||||
('id', '!=', version.id),
|
||||
('contract_date_end', '!=', False),
|
||||
('contract_date_end', '>=',
|
||||
start - relativedelta(months=12)),
|
||||
('contract_date_end', '<', start),
|
||||
], limit=1)
|
||||
if prior:
|
||||
return None, False
|
||||
# Monatsjubiläum, taggenau; Monatsende-Klippe: existiert der
|
||||
# Jubiläumstag im Folgemonat nicht (Eintritt 31. 01.), endet
|
||||
# das Fenster am Monatsultimo, Pflicht ab Folgemonatserstem.
|
||||
jubilaeum = start + relativedelta(months=1)
|
||||
if jubilaeum.day < start.day:
|
||||
jubilaeum += timedelta(days=1)
|
||||
end = version.contract_date_end
|
||||
if end and end < jubilaeum:
|
||||
# AV ≤ 1 Monat → zur Gänze beitragsfrei (lb-vor-03, auch
|
||||
# eine bloße Urlaubsersatzleistung).
|
||||
return None, True
|
||||
return jubilaeum, False
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Eigene BV-Grundlage (§ 6 Abs 5 iVm § 49 ASVG — ohne GFG/HBG)
|
||||
# ------------------------------------------------------------------
|
||||
def _at_bv_sonst_monate(self, result_rules=None):
|
||||
"""[(Beitragsmonat, sonstige BV-Basis)]: sonstige Bezüge ohne
|
||||
§-67-Abs-3/6-Zeilen (Abfertigung/Abfindung sind nach
|
||||
§ 49 Abs 3 Z 7 ASVG beitragsfrei, damit auch nicht BV-
|
||||
pflichtig), gruppiert nach Regel-Beitragsmonat mit voller
|
||||
Tagesgenauigkeit (Anspruchsprinzip, Muster
|
||||
_l10n_at_sv_basis_laufen_monate) — die Fenster-Prüfung braucht
|
||||
die Fälligkeit taggenau (SZ fällig 20. 07. ist bei Fenster-
|
||||
Ende 15. 07. pflichtig, der Monatsanfang wäre es nicht)."""
|
||||
self.ensure_one()
|
||||
gruppen = {}
|
||||
for rule in self.struct_id.rule_ids:
|
||||
if (rule.l10n_at_bemessung != 'sonstig'
|
||||
or rule.l10n_at_s67_abs != 'keine'):
|
||||
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
|
||||
gruppen[monat] = gruppen.get(monat, 0.0) + werte['total']
|
||||
return sorted(gruppen.items())
|
||||
|
||||
def _at_bv_basis_laufen(self, result_rules=None):
|
||||
"""Laufende BV-Grundlage: §-49-bereinigtes Entgelt ohne
|
||||
HBG-Cap (§ 6 Abs 5), aliquotiert um das beitragsfreie
|
||||
Erstfenster — 30-Tage-Monat (KB-Beispiel 4.500/30 × 17 =
|
||||
2.550); Teilmonate über die Lohnsteuertage, um Doppelabzüge
|
||||
zu vermeiden (das Entgelt ist für die Teilzeit bereits
|
||||
aliquotiert)."""
|
||||
self.ensure_one()
|
||||
pflichtig_ab, kurz_av = self._at_bv_fenster()
|
||||
if kurz_av:
|
||||
return 0.0
|
||||
basis = self._l10n_at_sv_basis_roh(result_rules)
|
||||
if pflichtig_ab is None or pflichtig_ab <= self.date_from:
|
||||
return basis
|
||||
if pflichtig_ab > self.date_to:
|
||||
# LZP liegt zur Gänze im beitragsfreien Erstfenster.
|
||||
return 0.0
|
||||
ende = min(self.date_to,
|
||||
self.version_id.contract_date_end or self.date_to)
|
||||
tage = (ende - pflichtig_ab).days + 1
|
||||
if tage <= 0:
|
||||
return 0.0
|
||||
return basis * tage / max(self._l10n_at_lohnsteuertage(), 1)
|
||||
|
||||
def _at_bv_basis_sonst(self, result_rules=None):
|
||||
"""BV-Grundlage sonstiger Bezüge (Sonderzahlungen & Co.):
|
||||
ungecappt (kein HBG-/Jahresdeckel, § 6 Abs 5) und ohne
|
||||
§-67-Abs-3/6-Zeilen; keine aliquote Kürzung um das freie
|
||||
Fenster — frei bleibt nur, was im Fenster fällig wird
|
||||
(Beitragsmonat im Fenster, lb-vor-03)."""
|
||||
self.ensure_one()
|
||||
pflichtig_ab, kurz_av = self._at_bv_fenster()
|
||||
if kurz_av:
|
||||
return 0.0
|
||||
total = 0.0
|
||||
for monat, basis in self._at_bv_sonst_monate(result_rules):
|
||||
if pflichtig_ab is not None and monat < pflichtig_ab:
|
||||
continue
|
||||
total += basis
|
||||
return total
|
||||
|
||||
def _at_bv_beitrag(self, result_rules=None):
|
||||
"""BV-Beitrag (§ 6 Abs 1 BMSVG): 1,53 % DG (at_sv_bv_dg) auf
|
||||
die eigene Grundlage laufend + sonstig; ATP_DG-Zeile ohne
|
||||
Nettoeffekt (FI-Zuordnung über hr_payroll_account, GP6).
|
||||
Läuft bewusst auch für geringfügig Beschäftigte und
|
||||
Lehrlinge (§ 46, § 1 Abs 2 Z 1; Jahreszahlung + 2,5 %
|
||||
Zinsenzuschlag der Geringfügigen = Zahlungsmodus GP7/GP8)."""
|
||||
self.ensure_one()
|
||||
if not self.version_id.l10n_at_abfertigung_neu:
|
||||
return 0.0
|
||||
basis = (self._at_bv_basis_laufen(result_rules)
|
||||
+ self._at_bv_basis_sonst(result_rules))
|
||||
satz = float(self._l10n_at_param('at_sv_bv_dg'))
|
||||
return basis * satz / 100.0
|
||||
@@ -0,0 +1,20 @@
|
||||
# Part of the odoo-at-payroll project. License: LGPL-3.
|
||||
"""Betriebliche Vorsorgekasse (BMSVG): Auswahl je Arbeitsverhältnis
|
||||
(lb-vor-02 — Voraussetzung der Beitragsabfuhr; die Kasse führt das
|
||||
Anwartschaftskonto und nimmt die §-17-Verfügungen entgegen). Die
|
||||
Leitzahl ist eSV-/mBGM-Angabe (GP7) und ist aus den Kassen-Unterlagen
|
||||
des Mandanten zu übernehmen — nicht aus Vermutung."""
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class L10nAtBvKasse(models.Model):
|
||||
_name = 'l10n.at.bv.kasse'
|
||||
_description = 'Betriebliche Vorsorgekasse (BMSVG)'
|
||||
_order = 'name'
|
||||
|
||||
name = fields.Char(required=True)
|
||||
leitzahl = fields.Char(
|
||||
string='Leitzahl (eSV/mBGM)',
|
||||
help='Leitzahl der Mitarbeitervorsorgekasse gemäß DM-ORG/'
|
||||
'mBGM-Angaben (GP7); aus den Kassen-Unterlagen übernehmen.')
|
||||
active = fields.Boolean(default=True)
|
||||
@@ -0,0 +1,20 @@
|
||||
# Part of the odoo-at-payroll project. License: LGPL-3.
|
||||
"""QWeb-Datenlieferant für den BV-Anwartschaftsreport (GP5): die
|
||||
Anwartschaftswerte werden im Python-Teil aufbereitet (Muster
|
||||
AbstractModel-Report), der QWeb-Teil bleibt frei von Methodenaufrufen."""
|
||||
from odoo import models
|
||||
|
||||
|
||||
class L10nAtBvAnwartschaftReport(models.AbstractModel):
|
||||
_name = 'report.l10n_at_hr_payroll_private.report_bv_anwartschaft'
|
||||
_description = 'BV-Anwartschaftsreport (Abfertigung Neu, § 15 BMSVG)'
|
||||
|
||||
def _get_report_values(self, docids, data=None):
|
||||
docs = self.env['hr.employee'].browse(docids)
|
||||
return {
|
||||
'docs': docs,
|
||||
'anwartschaft': {
|
||||
employee.id: employee._at_bv_anwartschaft()
|
||||
for employee in docs
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<!-- ============================================================ -->
|
||||
<!-- GP5: BV-Anwartschaftsreport (§ 15 BMSVG: gutgebrachte -->
|
||||
<!-- Beiträge + Erträge laut Kassen-Auszug — KEINE -->
|
||||
<!-- Monatsentgelt-Staffel). Daten aus dem AbstractModel-Report -->
|
||||
<!-- (models/bv_report.py); druckbar am Mitarbeiter und aus dem -->
|
||||
<!-- Endabrechnungs-Wizard. -->
|
||||
<!-- ============================================================ -->
|
||||
<record id="action_report_bv_anwartschaft" model="ir.actions.report">
|
||||
<field name="name">BV-Anwartschaftsreport (Abfertigung Neu)</field>
|
||||
<field name="model">hr.employee</field>
|
||||
<field name="report_type">qweb-pdf</field>
|
||||
<field name="report_name">l10n_at_hr_payroll_private.report_bv_anwartschaft</field>
|
||||
<field name="report_file">l10n_at_hr_payroll_private.report_bv_anwartschaft</field>
|
||||
<field name="paperformat_id" ref="base.paperformat_euro"/>
|
||||
<field name="binding_model_id" ref="hr.model_hr_employee"/>
|
||||
<field name="binding_type">report</field>
|
||||
</record>
|
||||
|
||||
<template id="report_bv_anwartschaft">
|
||||
<t t-call="web.html_container">
|
||||
<t t-foreach="docs" t-as="employee">
|
||||
<t t-set="fort" t-value="anwartschaft[employee.id]"/>
|
||||
<t t-call="web.external_layout">
|
||||
<div class="page">
|
||||
<h2>BV-Anwartschaftsreport (Abfertigung Neu)</h2>
|
||||
<table class="table table-sm o_main_table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><strong>Dienstnehmer:in</strong></td>
|
||||
<td><t t-out="employee.name"/></td>
|
||||
<td><strong>Mitarbeitervorsorgekasse</strong></td>
|
||||
<td><t t-out="employee.l10n_at_bv_kasse_id.name or '—'"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Beitragsmonate (dieser DG)</strong></td>
|
||||
<td><t t-out="fort['monate']"/></td>
|
||||
<td><strong>Beitragsmonate Vor-Arbeitgeber</strong></td>
|
||||
<td><t t-out="employee.l10n_at_bv_monate_extern"/></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h3>Gutgebrachte Beiträge je Beitragsjahr (§ 6 BMSVG)</h3>
|
||||
<table class="table table-sm o_main_table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Beitragsjahr</th>
|
||||
<th class="text-end">Beitragsmonate</th>
|
||||
<th class="text-end">Gutgebrachte Beiträge</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr t-foreach="fort['jahre']" t-as="jahr">
|
||||
<td><t t-out="jahr[0]"/></td>
|
||||
<td class="text-end"><t t-out="jahr[1]"/></td>
|
||||
<td class="text-end">
|
||||
<span t-out="jahr[2]"
|
||||
t-options='{"widget": "monetary", "display_currency": employee.currency_id}'/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Summe gutgebracht</strong></td>
|
||||
<td/>
|
||||
<td class="text-end">
|
||||
<span t-out="fort['beitraege']"
|
||||
t-options='{"widget": "monetary", "display_currency": employee.currency_id}'/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Erträge laut Kassen-Auszug (§ 15)</td>
|
||||
<td/>
|
||||
<td class="text-end">
|
||||
<span t-out="employee.l10n_at_bv_ertraege"
|
||||
t-options='{"widget": "monetary", "display_currency": employee.currency_id}'/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Anwartschaft gesamt (Beiträge + Erträge)</strong></td>
|
||||
<td/>
|
||||
<td class="text-end">
|
||||
<span t-out="fort['beitraege'] + employee.l10n_at_bv_ertraege"
|
||||
t-options='{"widget": "monetary", "display_currency": employee.currency_id}'/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p class="small text-muted">
|
||||
Auszahlung der Abfertigungsanwartschaft durch die
|
||||
Mitarbeitervorsorgekasse (§ 15 BMSVG — keine
|
||||
Monatsentgelt-Staffel); Fälligkeit am Ende des
|
||||
zweitfolgenden Monats nach Geltendmachung (§ 16).
|
||||
Verfügungsausschluss nach § 14 Abs 2 BMSVG bei
|
||||
Kündigung durch den AN, verschuldeter Entlassung,
|
||||
unberechtigtem vorzeitigem Austritt oder weniger
|
||||
als 36 Beitragsmonaten (Vor-Arbeitgeber-Monate
|
||||
zählen mit — Mitarbeiter-Stamm pflegen).
|
||||
</p>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
</odoo>
|
||||
@@ -8,4 +8,7 @@ access_l10n_at_payroll_kv_gruppe_manager,L10n AT Payroll KV-Gruppe Manager,model
|
||||
access_l10n_at_payroll_kv_gruppe_stufe_user,L10n AT Payroll KV-Gruppe-Stufe User,model_l10n_at_payroll_kv_gruppe_stufe,hr_payroll.group_hr_payroll_user,1,0,0,0
|
||||
access_l10n_at_payroll_kv_gruppe_stufe_manager,L10n AT Payroll KV-Gruppe-Stufe Manager,model_l10n_at_payroll_kv_gruppe_stufe,hr_payroll.group_hr_payroll_manager,1,1,1,1
|
||||
access_l10n_at_payroll_kv_import_wizard,L10n AT Payroll KV-Import Wizard,model_l10n_at_payroll_kv_import_wizard,hr_payroll.group_hr_payroll_user,1,1,1,1
|
||||
access_l10n_at_payroll_kv_import_line,L10n AT Payroll KV-Import Line,model_l10n_at_payroll_kv_import_line,hr_payroll.group_hr_payroll_user,1,1,1,1
|
||||
access_l10n_at_payroll_kv_import_line,L10n AT Payroll KV-Import Line,model_l10n_at_payroll_kv_import_line,hr_payroll.group_hr_payroll_user,1,1,1,1
|
||||
access_l10n_at_bv_kasse_user,L10n AT BV-Kasse User,model_l10n_at_bv_kasse,hr_payroll.group_hr_payroll_user,1,0,0,0
|
||||
access_l10n_at_bv_kasse_manager,L10n AT BV-Kasse Manager,model_l10n_at_bv_kasse,hr_payroll.group_hr_payroll_manager,1,1,1,1
|
||||
access_l10n_at_bv_endabrechnung_wizard,L10n AT BV-Endabrechnung Wizard,model_l10n_at_bv_endabrechnung_wizard,hr_payroll.group_hr_payroll_user,1,1,1,1
|
||||
|
@@ -2,4 +2,5 @@
|
||||
from . import test_kv_katalog
|
||||
from . import test_entgelt_private
|
||||
from . import test_lst_private
|
||||
from . import test_sv_private
|
||||
from . import test_sv_private
|
||||
from . import test_bv_private
|
||||
@@ -0,0 +1,297 @@
|
||||
# Part of the odoo-at-payroll project. License: LGPL-3.
|
||||
"""GP5 Abfertigung Neu (BMSVG) auf der privatwirtschaftlichen
|
||||
Structure: BVG-Regel (§ 6: 1,53 % DG auf Entgelt und SZ, EIGENE
|
||||
ungecappte Grundlage, beitragsfreier erster Monat taggenau mit
|
||||
Monatsende-Klippe, Wiederzulassung binnen 12 Monaten ab Tag 1 — auch
|
||||
bei kurzem Vor-AV —, SZ-Fälligkeit taggenau gegen das Fenster,
|
||||
§-67-Abs-3/6-Ausschluss, kein HBG-Cap, läuft auch bei Geringfügigkeit)
|
||||
sowie Anwartschafts-Fortschreibung (§ 15) und Endabrechnungs-Wizard
|
||||
(§§ 14-17: Verfügungsmatrix, 36-Beitragsmonate, §-16/§-17-Fristen).
|
||||
|
||||
Rechenfälle: docs/rechenfaelle/abfertigung.md."""
|
||||
from datetime import date, timedelta
|
||||
|
||||
from odoo.tests import TransactionCase, tagged
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class BvPrivateTest(TransactionCase):
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.structure = self.env.ref(
|
||||
'l10n_at_hr_payroll_private.structure_at_privat')
|
||||
self.kv2203 = self.env.ref(
|
||||
'l10n_at_hr_payroll_private.kv_si_2203')
|
||||
self.gruppe_ii = self.env.ref(
|
||||
'l10n_at_hr_payroll_private.kv2203_grp_ii')
|
||||
self.calendar = self.env['resource.calendar'].create({
|
||||
'name': 'AT Test 40h Mo-Fr',
|
||||
'tz': 'Europe/Vienna',
|
||||
'hours_per_day': 8,
|
||||
'attendance_ids': [
|
||||
(0, 0, {'name': 'Tag %d' % wd, 'dayofweek': str(wd),
|
||||
'hour_from': 8.0, 'hour_to': 16.0})
|
||||
for wd in range(5)
|
||||
],
|
||||
})
|
||||
self.env.company.resource_calendar_id = self.calendar
|
||||
|
||||
def _employee(self, version_vals=None):
|
||||
employee = self.env['hr.employee'].create({
|
||||
'name': 'Test BV privat',
|
||||
})
|
||||
version = self.env['hr.version'].search(
|
||||
[('employee_id', '=', employee.id)], limit=1)
|
||||
if not version:
|
||||
version = self.env['hr.version'].create({
|
||||
'employee_id': employee.id,
|
||||
'date_start': date(2020, 1, 1)})
|
||||
version.write({
|
||||
'contract_date_start': date(2020, 1, 1),
|
||||
'kv_id': self.kv2203.id,
|
||||
'kv_gruppe_id': self.gruppe_ii.id,
|
||||
'erfahrungsstufe': 1,
|
||||
'resource_calendar_id': self.calendar.id,
|
||||
})
|
||||
if version_vals:
|
||||
version.write(version_vals)
|
||||
return employee
|
||||
|
||||
def _slip(self, employee, monat, jahr=2026, confirm=False,
|
||||
struct=None):
|
||||
erster = date(jahr, monat, 1)
|
||||
naechster = (erster + timedelta(days=32)).replace(day=1)
|
||||
slip = self.env['hr.payslip'].create({
|
||||
'name': 'Test-BV-Privat %d/%d' % (monat, jahr),
|
||||
'employee_id': employee.id,
|
||||
'struct_id': (struct or self.structure).id,
|
||||
'date_from': erster,
|
||||
'date_to': naechster - timedelta(days=1),
|
||||
})
|
||||
slip.compute_sheet()
|
||||
if confirm:
|
||||
slip.action_payslip_done()
|
||||
return slip
|
||||
|
||||
def _total(self, slip, code):
|
||||
return sum(slip.line_ids.filtered(
|
||||
lambda l: l.code == code).mapped('total'))
|
||||
|
||||
# Teststruktur mit Fixbeträgen + eigener BVG-Regel (Muster
|
||||
# _lnk_struktur), um Fenster-/SZ-/§-67-Eckfälle zielgenau zu
|
||||
# treffen, ohne vom KV-Katalog-Entgelt abzuhängen.
|
||||
def _bv_struktur(self, code, regeln):
|
||||
struktur = self.env['hr.payroll.structure'].create({
|
||||
'name': 'Test AT BV %s' % code,
|
||||
'code': 'TEST_AT_BV_%s' % code,
|
||||
'type_id': self.structure.type_id.id,
|
||||
'country_id': self.env.ref('base.at').id,
|
||||
})
|
||||
brutto = self.env.ref(
|
||||
'l10n_at_hr_payroll_private.category_atp_brutto')
|
||||
dg = self.env.ref(
|
||||
'l10n_at_hr_payroll_private.category_atp_dg')
|
||||
for regel_code, betrag, bemessung, s67, beitragsmonat in regeln:
|
||||
vals = {
|
||||
'name': regel_code,
|
||||
'code': regel_code,
|
||||
'struct_id': struktur.id,
|
||||
'category_id': brutto.id,
|
||||
'condition_select': 'none',
|
||||
'l10n_at_bemessung': bemessung,
|
||||
'l10n_at_s67_abs': s67,
|
||||
'amount_select': 'fix',
|
||||
'amount_fix': betrag,
|
||||
}
|
||||
if beitragsmonat:
|
||||
vals['l10n_at_beitragsmonat'] = beitragsmonat
|
||||
self.env['hr.salary.rule'].create(vals)
|
||||
self.env['hr.salary.rule'].create({
|
||||
'name': 'BV-Beitrag Abfertigung Neu (Test)',
|
||||
'code': 'BVG',
|
||||
'struct_id': struktur.id,
|
||||
'category_id': dg.id,
|
||||
'condition_select': 'python',
|
||||
'condition_python':
|
||||
'result = payslip._at_bv_beitrag(result_rules) > 0',
|
||||
'amount_select': 'code',
|
||||
'l10n_at_bemessung': 'neutral',
|
||||
'amount_python_compute':
|
||||
'result = payslip._at_bv_beitrag(result_rules)',
|
||||
})
|
||||
return struktur
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# RF-BV1/2: Standardlauf und kein HBG-Cap (§ 6 Abs 5)
|
||||
# ------------------------------------------------------------------
|
||||
def test_01_standard_b13(self):
|
||||
"""November mit B13: BVG 1,53 % auf KVGEO + B13 (4.118,82 €
|
||||
→ 63,02 €), Kategorie ATP_DG ohne Nettoeffekt."""
|
||||
employee = self._employee()
|
||||
slip = self._slip(employee, 11)
|
||||
basis = (self._total(slip, 'KVGEO') + self._total(slip, 'B13'))
|
||||
self.assertAlmostEqual(basis, 4118.82, places=2)
|
||||
self.assertAlmostEqual(self._total(slip, 'BVG'),
|
||||
4118.82 * 0.0153, places=2)
|
||||
# Kein Nettoeffekt: ATP_DG bleibt außerhalb der NETTO-Rechnung.
|
||||
abzug = sum(self._total(slip, code) for code in
|
||||
('SVDN_KV', 'SVDN_PV', 'SVDN_AV', 'SVDN_AK',
|
||||
'SVDN_WF', 'SVDN_SZ', 'LST_LFD', 'LST_SZ'))
|
||||
self.assertAlmostEqual(self._total(slip, 'NETTO'),
|
||||
basis - abzug, places=1)
|
||||
|
||||
def test_02_kein_hbg_cap(self):
|
||||
"""Entgelt 8.000 € über der HBG 6.930 €: SVDG_KV cappt auf
|
||||
6.930, BVG läuft auf die volle Grundlage (§ 6 Abs 5 iVm
|
||||
§ 49 ASVG — GFG und HBG unberücksichtigt)."""
|
||||
employee = self._employee({'ueberzahlung': 8000.0})
|
||||
slip = self._slip(employee, 1)
|
||||
self.assertAlmostEqual(self._total(slip, 'KVGEO'), 8000.0,
|
||||
places=2)
|
||||
self.assertAlmostEqual(self._total(slip, 'SVDG_KV'),
|
||||
6930.0 * 0.0378, places=2)
|
||||
self.assertAlmostEqual(self._total(slip, 'BVG'),
|
||||
8000.0 * 0.0153, places=2)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# RF-BV3/4/5/10: Beitragsfreier erster Monat (§ 6 Abs 1)
|
||||
# ------------------------------------------------------------------
|
||||
def test_03_erster_monat_fenster(self):
|
||||
"""Eintritt 15. 06.: Juni zur Gänze beitragsfrei, Juli aliquot
|
||||
mit 30-Tage-Monat (KB-Beispiel: × 17/30), August voll."""
|
||||
employee = self._employee(
|
||||
{'ueberzahlung': 6000.0,
|
||||
'contract_date_start': date(2026, 6, 15)})
|
||||
slip_jun = self._slip(employee, 6)
|
||||
self.assertAlmostEqual(self._total(slip_jun, 'BVG'), 0.0,
|
||||
places=2)
|
||||
slip_jul = self._slip(employee, 7)
|
||||
self.assertAlmostEqual(self._total(slip_jul, 'KVGEO'), 6000.0,
|
||||
places=2)
|
||||
self.assertAlmostEqual(
|
||||
self._total(slip_jul, 'BVG'),
|
||||
6000.0 * 17 / 30 * 0.0153, places=2)
|
||||
slip_aug = self._slip(employee, 8)
|
||||
self.assertAlmostEqual(self._total(slip_aug, 'BVG'),
|
||||
6000.0 * 0.0153, places=2)
|
||||
|
||||
def test_04_wiederzulassung(self):
|
||||
"""Vor-AV nur 14 Tage, Ende binnen 12 Monaten → Wieder-
|
||||
zulassung: neuer AV beitragspflichtig ab dem 1. Tag (auch
|
||||
bei kurzem Vor-AV; OGH 9 ObA 30/16a)."""
|
||||
employee = self._employee(
|
||||
{'ueberzahlung': 6000.0,
|
||||
'contract_date_start': date(2026, 7, 1)})
|
||||
employee.version_id.write({'date_version': date(2026, 7, 1)})
|
||||
self.env['hr.version'].create({
|
||||
'employee_id': employee.id,
|
||||
'date_version': date(2026, 6, 1),
|
||||
'contract_date_start': date(2026, 6, 1),
|
||||
'contract_date_end': date(2026, 6, 15),
|
||||
})
|
||||
slip = self._slip(employee, 7)
|
||||
self.assertEqual(slip.version_id, employee.version_id)
|
||||
self.assertAlmostEqual(self._total(slip, 'BVG'),
|
||||
6000.0 * 0.0153, places=2)
|
||||
|
||||
def test_05_kurz_av(self):
|
||||
"""AV genau 1 Monat (1.-30. 06.) → zur Gänze beitragsfrei
|
||||
(lb-vor-03; auch bloße Urlaubsersatzleistungen)."""
|
||||
employee = self._employee(
|
||||
{'ueberzahlung': 6000.0,
|
||||
'contract_date_start': date(2026, 6, 1),
|
||||
'contract_date_end': date(2026, 6, 30)})
|
||||
slip = self._slip(employee, 6)
|
||||
self.assertAlmostEqual(self._total(slip, 'KVGEO'), 6000.0,
|
||||
places=2)
|
||||
self.assertAlmostEqual(self._total(slip, 'BVG'), 0.0, places=2)
|
||||
|
||||
def test_10_monatsende_klippe(self):
|
||||
"""Eintritt 31. 01. — der Jubiläumstag 31. 02. existiert
|
||||
nicht → Fenster bis Monatsultimo (28. 02.), Pflicht ab
|
||||
01. 03. (ABGB-Fristrechnung)."""
|
||||
employee = self._employee(
|
||||
{'ueberzahlung': 6000.0,
|
||||
'contract_date_start': date(2026, 1, 31)})
|
||||
slip_feb = self._slip(employee, 2)
|
||||
self.assertAlmostEqual(self._total(slip_feb, 'BVG'), 0.0,
|
||||
places=2)
|
||||
slip_mrz = self._slip(employee, 3)
|
||||
self.assertAlmostEqual(self._total(slip_mrz, 'BVG'),
|
||||
6000.0 * 0.0153, places=2)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# RF-BV6/7/9: Geringfügige, SZ-Fälligkeit/§-67-Ausschluss, Alt-Fall
|
||||
# ------------------------------------------------------------------
|
||||
def test_06_geringfuegig(self):
|
||||
"""Geringfügig Beschäftigte sind BMSVG-erfasst: BVG läuft
|
||||
(500 € → 7,65 €), obwohl der SVDG-Block auf 0 geht."""
|
||||
employee = self._employee({'ueberzahlung': 500.0})
|
||||
slip = self._slip(employee, 1)
|
||||
self.assertTrue(slip.l10n_at_geringfuegig)
|
||||
self.assertAlmostEqual(self._total(slip, 'SVDG_KV'), 0.0,
|
||||
places=2)
|
||||
self.assertAlmostEqual(self._total(slip, 'BVG'),
|
||||
500.0 * 0.0153, places=2)
|
||||
|
||||
def test_07_sz_fenster_und_s67(self):
|
||||
"""SZ mit Beitragsmonat im freien Fenster bleibt frei
|
||||
(Fälligkeit taggenau: 20. 06. frei, 20. 07. pflichtig);
|
||||
§-67-Abs-3-Abfertigung scheidet aus der BV-Grundlage aus
|
||||
(§ 49 Abs 3 Z 7 ASVG); laufender Anteil 17/30."""
|
||||
employee = self._employee(
|
||||
{'contract_date_start': date(2026, 6, 15)})
|
||||
struktur = self._bv_struktur('A', [
|
||||
('LFD', 2000.0, 'laufend', 'keine', None),
|
||||
('SZ_FENSTER', 1000.0, 'sonstig', 'keine',
|
||||
date(2026, 6, 20)),
|
||||
('SZ_PFLECHTIG', 1000.0, 'sonstig', 'keine',
|
||||
date(2026, 7, 20)),
|
||||
('ABF_ALT', 10000.0, 'sonstig', 'abs3', None),
|
||||
])
|
||||
slip = self._slip(employee, 7, struct=struktur)
|
||||
basis = 2000.0 * 17 / 30 + 1000.0
|
||||
self.assertAlmostEqual(self._total(slip, 'BVG'),
|
||||
basis * 0.0153, places=2)
|
||||
|
||||
def test_09_alt_fall_flag(self):
|
||||
"""Alt-Verhältnis (Flag abgewählt) läuft ohne BVG."""
|
||||
employee = self._employee({'l10n_at_abfertigung_neu': False})
|
||||
slip = self._slip(employee, 11)
|
||||
self.assertAlmostEqual(self._total(slip, 'KVGEO'), 2059.41,
|
||||
places=2)
|
||||
self.assertAlmostEqual(self._total(slip, 'BVG'), 0.0, places=2)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# RF-BV8: Anwartschaft + Endabrechnungs-Wizard (§§ 14-17)
|
||||
# ------------------------------------------------------------------
|
||||
def test_08_wizard_verfuegbarkeit(self):
|
||||
"""Anwartschaft aus 3 validierten Läufen (3 × 31,51 €);
|
||||
§-14-Matrix: < 36 Beitragsmonate sperrt IMMER, unschädliche
|
||||
AG-Kündigung + 36 Monate verfügbar, AN-Kündigung sperrt;
|
||||
Fristen § 16 (Ende des zweitfolgenden Monats) und § 17
|
||||
(6 Monate Erklärungsfrist)."""
|
||||
employee = self._employee()
|
||||
for monat in (1, 2, 3):
|
||||
self._slip(employee, monat, confirm=True)
|
||||
fort = employee._at_bv_anwartschaft()
|
||||
self.assertEqual(fort['monate'], 3)
|
||||
self.assertAlmostEqual(fort['beitraege'],
|
||||
3 * 2059.41 * 0.0153, places=1)
|
||||
self.assertEqual(fort['jahre'], [(2026, 3, fort['beitraege'])])
|
||||
wizard = self.env['l10n.at.bv.endabrechnung.wizard'].create({
|
||||
'employee_id': employee.id,
|
||||
'austrittsdatum': date(2026, 8, 31),
|
||||
'beendigungsart': 'ag_kuendigung',
|
||||
'geltendmachung': date(2026, 9, 11),
|
||||
})
|
||||
self.assertFalse(wizard.verfuegbar) # 3 < 36 Beitragsmonate
|
||||
self.assertIn('36', wizard.sperrgrund)
|
||||
wizard.write({'monate_extern': 33})
|
||||
self.assertEqual(wizard.monate_gesamt, 36)
|
||||
self.assertTrue(wizard.verfuegbar) # unschädliche Beendigung
|
||||
wizard.write({'beendigungsart': 'an_kuendigung'})
|
||||
self.assertFalse(wizard.verfuegbar) # § 14 Abs 2-Gruppe
|
||||
self.assertEqual(wizard.faelligkeit, date(2026, 11, 30))
|
||||
self.assertEqual(wizard.erklaerungsfrist, date(2027, 2, 28))
|
||||
@@ -0,0 +1,146 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<!-- ============================================================ -->
|
||||
<!-- GP5 Abfertigung Neu: BV-Kassen-Stamm, Vertragsfelder am -->
|
||||
<!-- Mitarbeiter (Payroll-Gruppe, Muster KV-Einstufung), -->
|
||||
<!-- Endabrechnungs-Wizard (§§ 14-17 BMSVG) und Menüs. -->
|
||||
<!-- ============================================================ -->
|
||||
|
||||
<record id="bv_kasse_list_view" model="ir.ui.view">
|
||||
<field name="name">l10n.at.bv.kasse.list</field>
|
||||
<field name="model">l10n.at.bv.kasse</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="Mitarbeitervorsorgekassen">
|
||||
<field name="name"/>
|
||||
<field name="leitzahl"/>
|
||||
<field name="active" column_invisible="1"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="bv_kasse_form_view" model="ir.ui.view">
|
||||
<field name="name">l10n.at.bv.kasse.form</field>
|
||||
<field name="model">l10n.at.bv.kasse</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Mitarbeitervorsorgekasse">
|
||||
<sheet>
|
||||
<group>
|
||||
<group>
|
||||
<field name="name"/>
|
||||
<field name="leitzahl"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="active" widget="boolean_toggle"/>
|
||||
</group>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_bv_kasse" model="ir.actions.act_window">
|
||||
<field name="name">Mitarbeitervorsorgekassen</field>
|
||||
<field name="res_model">l10n.at.bv.kasse</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
</record>
|
||||
|
||||
<!-- BMSVG-Stammdaten am Mitarbeitervertrag (Payroll-Gruppe, wie
|
||||
die KV-Einstufung). -->
|
||||
<record id="payroll_hr_employee_view_form_bv" model="ir.ui.view">
|
||||
<field name="name">hr.employee.form.bv</field>
|
||||
<field name="model">hr.employee</field>
|
||||
<field name="inherit_id" ref="hr_payroll.payroll_hr_employee_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<group name="payroll_group" position="inside">
|
||||
<group name="bv_einstufung" string="Abfertigung Neu (BMSVG)"
|
||||
groups="hr_payroll.group_hr_payroll_user">
|
||||
<field name="l10n_at_abfertigung_neu"/>
|
||||
<field name="l10n_at_bv_kasse_id"/>
|
||||
<field name="l10n_at_bv_monate_extern"/>
|
||||
<field name="l10n_at_bv_ertraege"/>
|
||||
</group>
|
||||
</group>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Endabrechnungs-Wizard (Austritt): Anwartschaftsausweis +
|
||||
§-14-Abs-2-Prüfung; Auszahlung läuft über die MV-Kasse. -->
|
||||
<record id="bv_endabrechnung_wizard_form_view" model="ir.ui.view">
|
||||
<field name="name">l10n.at.bv.endabrechnung.wizard.form</field>
|
||||
<field name="model">l10n.at.bv.endabrechnung.wizard</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="BV-Endabrechnung (Abfertigung Neu, §§ 14-17 BMSVG)">
|
||||
<group>
|
||||
<group string="Dienstverhältnis">
|
||||
<field name="employee_id"/>
|
||||
<field name="austrittsdatum"/>
|
||||
<field name="beendigungsart"/>
|
||||
<field name="geltendmachung"/>
|
||||
</group>
|
||||
<group string="Anwartschaft (§ 15 BMSVG)">
|
||||
<field name="kasse_id"/>
|
||||
<field name="monate_lokal"/>
|
||||
<field name="monate_extern"/>
|
||||
<field name="monate_gesamt"/>
|
||||
<field name="beitraege_lokal"/>
|
||||
<field name="ertraege"/>
|
||||
<field name="anwartschaft" class="oe_subtotal_footer_separator"/>
|
||||
</group>
|
||||
</group>
|
||||
<group string="Verfügbarkeit (§ 14 Abs 2 BMSVG)">
|
||||
<group>
|
||||
<field name="verfuegbar" widget="badge"
|
||||
decoration-success="verfuegbar"
|
||||
decoration-danger="not verfuegbar"/>
|
||||
<field name="sperrgrund" invisible="verfuegbar"
|
||||
nolabel="1" placeholder="Kein Verfügungsausschluss"/>
|
||||
</group>
|
||||
<group string="Fristen">
|
||||
<field name="erklaerungsfrist"/>
|
||||
<field name="faelligkeit"/>
|
||||
</group>
|
||||
</group>
|
||||
<div class="alert alert-info" role="alert">
|
||||
Verfügung über die Anwartschaft (§ 17 BMSVG — innerhalb
|
||||
von 6 Monaten nach Austritt erklären, sonst
|
||||
Weiterveranlagung): 1) Kapitalauszahlung (6 % Lohnsteuer,
|
||||
§ 67 Abs 3 EStG — Einbehalt durch die MV-Kasse),
|
||||
2) Weiterveranlagung bei der MV-Kasse, 3) Übertragung auf
|
||||
die MV-Kasse des neuen Arbeitgebers, 4) Überweisung an
|
||||
einen Versicherer als Einmalprämie für eine
|
||||
Pensionszusatzversicherung (§ 108b EStG — keine LSt),
|
||||
5) Überweisung an eine Pensionskasse. Weitere
|
||||
Auszahlungsfälle (§ 14 Abs 1; Einzelbeurteilung mit der
|
||||
Kasse): Inanspruchnahme einer Alterspension (§ 4 Abs 3
|
||||
APG), Auflösung ab Korridorpensions-Alter (62), fünf
|
||||
Jahre kein BMSVG-pflichtiges Arbeitsverhältnis, Tod
|
||||
(ungekürzt an unterhaltsberechtigte Erben).
|
||||
</div>
|
||||
<footer>
|
||||
<button string="Anwartschaftsreport drucken"
|
||||
name="action_report" type="object"
|
||||
class="btn-primary"/>
|
||||
<button string="Schließen" class="btn-secondary"
|
||||
special="cancel"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_bv_endabrechnung_wizard" model="ir.actions.act_window">
|
||||
<field name="name">BV-Endabrechnung (Austritt)</field>
|
||||
<field name="res_model">l10n.at.bv.endabrechnung.wizard</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="menu_l10n_at_payroll_private_bv_kasse"
|
||||
name="Mitarbeitervorsorgekassen"
|
||||
parent="menu_l10n_at_payroll_private_root"
|
||||
action="action_bv_kasse" sequence="50"/>
|
||||
|
||||
<menuitem id="menu_l10n_at_payroll_private_bv_end"
|
||||
name="BV-Endabrechnung (Austritt)"
|
||||
parent="menu_l10n_at_payroll_private_root"
|
||||
action="action_bv_endabrechnung_wizard" sequence="60"/>
|
||||
</odoo>
|
||||
@@ -1,2 +1,3 @@
|
||||
# Part of the odoo-at-payroll project. License: LGPL-3.
|
||||
from . import kv_katalog_import
|
||||
from . import kv_katalog_import
|
||||
from . import bv_endabrechnung
|
||||
@@ -0,0 +1,156 @@
|
||||
# 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)
|
||||
Reference in New Issue
Block a user