diff --git a/.agents/MEMORY.md b/.agents/MEMORY.md
index 6784ad1..ea7ee48 100644
--- a/.agents/MEMORY.md
+++ b/.agents/MEMORY.md
@@ -2,6 +2,58 @@
## Current focus
+**AP15-C Vergleichssummen (§ 67 Abs 8 lit a) umgesetzt — Kern 73/73, Privat 188/188 Tests grün**
+(2026-09-15, uncommittet bis Nutzerfreigabe; Privat **19.0.15.0.0**):
+
+- **Recht (RIS-Wortlaut § 67 Abs 8 lit a verifiziert, lb-end-21/22):**
+ Vergleichssummen (gerichtl./außergerichtl. Vergleiche, Nachzahlungen aus
+ Urteilen/Bescheiden, Kündigungsanfechtung) — soweit nicht Abs 3/6: Abs-10-
+ Tarif im Zuflussmonat; nach §-62-Z-3/4/5-Abzug ist 1/5 steuerfrei (Deckel
+ 1/5 × 9 × mHBG = 12.474), Abs 2 (Sechstel) nicht anzuwenden;
+ BV-Anwartschafts-Teil bis 7.500 € mit 6 % (parametrisiert
+ `at_lst_67_abs8_vergleich_bv` — § 67 Abs 8 ist §-33a/§-124b-indexiert),
+ Überhang fällt zurück ins Fünftel; LNK-PFLICHTIG (§ 41 Abs 4 lit b FLAG
+ nennt nur Abs 3/6); SV-PFLICHTIG (Zuflussmonat; Aufrollung je Beitragsmonat
+ via Kern-`l10n_at_beitragsmonat` bzw. PV-Verlängerung, lb-end-22/E-MVB
+ 011-02-00-001); Widmungen nur bei Strittigkeit anerkannt (VwGH);
+ ausdrücklich vereinbarte gesetzl. Abfertigung → gesamt Abs 3 (VwGH
+ 2013/13/0001 = ARD 6490/20/2016); lit e (Pensionsabfindung)/lit f
+ (Sozialplan) gehören NICHT zu lit a. Deckel-Reihenfolge: §-62-Abzug ERST,
+ dann Fünftel — das 70.000-€-Quellenbeispiel meint die Bemessungsgrundlage,
+ der Cap greift brutto-abhängig erst ab ~76.200 €.
+- **Privat (19.0.15.0.0):** Regeln `VERGLEICH` (seq 33, neutral/lauend,
+ `l10n_at_s67_fuenftel`) + `VERGLEICH_BV` (seq 34, neutral/lauend, neues
+ Regel-Flag `l10n_at_vergleich_bv`, KEIN `s67_abs` — sonst fälschlich
+ LNK-frei), `LST_VERGLEICH_BV` (seq 74, 6 %); Eingaben
+ ATP_VERGLEICH/ATP_VERGLEICH_BV; Helfer in endabrechnung.py:
+ `_at_vergleich_bv_total/_beguenstigt/_ueberhang/_lst_6`,
+ `_at_fuenftel_frei_betr`; `_at_fuenftel_total` zieht den BV-Überhang ins
+ Fünftel, `_at_lnk_basis` + BV-begünstigter Teil (Pin-Kommentar
+ aktualisiert); Konten 6200/6000 bzw. 3540 (vorläufig wie Abf);
+ Wizard-Abschnitt (Bestätigungspflichten Aufgliederung/Strittigkeit +
+ BV-Anwartschaft, BV ≤ Deckel erzwungen, Vorschau Fünftel/Tarif/BV-LSt);
+ Payslip-Feld `l10n_at_vergleich_nachweis` + Tab.
+- **Tests:** endabrechnung 21 (RF-VGL-1..4: Rest-Fünftel 9.000 €, BV 9.000 €
+ mit 7.500/1.500-Split und anteiliger Beitragszuordnung Rz 1102b, Deckel
+ 100.000 € brutto, Wizard-E2E + 3 UserError-Fälle); Suite **205/205** (73
+ Kern + 188 Privat) auf Wegwerf-DB `ap15c_test5`.
+- **Verifikationsreste (dokumentiert, nicht geblockt):** KZ-Positionierung
+ der lit-a-Tarif-Teile (laufend über LST_LFD) + §-291d-Zuordnung von
+ `VERGLEICH`/`VERGLEICH_BV` (wie ABGANG ohne Pfändungs-Flag) gegen LStR
+ Rz 1087 ff/§ 291d EO; Fünftel-Deckel-Pooling über ALLE Fünftel-Regeln
+ (ABGANG + VERGLEICH im selben Lauf minimal konservativ);
+ SV-Aufrollung aktiver Ansprüche nicht automatisiert; gesonderter
+ Lohnzettel bei Zahlung nach dem Beendigungsmonat (LStR Rz 911a, GP7-Rest).
+- **Umgebung (wichtig für künftige Läufe):** nach dem Repo-Umzug war `.venv`
+ tot (editable-Pfade ohne `odoo-projects`-Komponente, Shebangs auf alten
+ python3.14-Pfad); repariert via sed über
+ `__editable___odoo_19_0_e_20260910_finder.py`, `direct_url.json` und
+ `.venv/bin/*`-Skripte. Aufruf `.venv/bin/python -m odoo` — im Baum fehlt
+ `odoo/cli/main.py`, `-m odoo` geht über `__main__.py` → `cli.command`;
+ setuptools fehlt im venv (pip install -e nicht möglich).
+- **Offen:** AP14-B (Prämien-Beendigung Feinschliff), AP16 Karenz-Reste
+ (Gate 8.13). Nicht committen ohne Nutzeranforderung.
+
**AP15-B freiwillige Abfertigung (§ 67 Abs 6) umgesetzt — Kern 55/55, Privat 146/146 Tests grün**
(2026-09-15, uncommittet bis Nutzerfreigabe; Privat **19.0.14.0.0**, Kern **19.0.10.0.0**):
diff --git a/addons/l10n_at_hr_payroll_private/__manifest__.py b/addons/l10n_at_hr_payroll_private/__manifest__.py
index cddf45f..4aeb06e 100644
--- a/addons/l10n_at_hr_payroll_private/__manifest__.py
+++ b/addons/l10n_at_hr_payroll_private/__manifest__.py
@@ -1,7 +1,7 @@
# Part of the odoo-at-payroll project. License: LGPL-3.
{
'name': 'Österreich - Privatwirtschaft Personalverrechnung (General-AT)',
- 'version': '19.0.14.0.0',
+ 'version': '19.0.15.0.0',
'author': 'Florian Egger',
'website': 'https://gem360.at',
'category': 'Human Resources/Payroll',
diff --git a/addons/l10n_at_hr_payroll_private/data/input_types_endabrechnung_private.xml b/addons/l10n_at_hr_payroll_private/data/input_types_endabrechnung_private.xml
index d5885dd..06f5b02 100644
--- a/addons/l10n_at_hr_payroll_private/data/input_types_endabrechnung_private.xml
+++ b/addons/l10n_at_hr_payroll_private/data/input_types_endabrechnung_private.xml
@@ -39,4 +39,12 @@
Freiwillige Abfertigung (Brutto)
ATP_ABF_FREI
+
+ Vergleichssumme — Rest (Brutto, Fünftelregelung)
+ ATP_VERGLEICH
+
+
+ Vergleichssumme — BV-Anwartschafts-Teil (Brutto)
+ ATP_VERGLEICH_BV
+
\ No newline at end of file
diff --git a/addons/l10n_at_hr_payroll_private/data/rule_parameters_endabrechnung_private.xml b/addons/l10n_at_hr_payroll_private/data/rule_parameters_endabrechnung_private.xml
index ea1b502..c423a38 100644
--- a/addons/l10n_at_hr_payroll_private/data/rule_parameters_endabrechnung_private.xml
+++ b/addons/l10n_at_hr_payroll_private/data/rule_parameters_endabrechnung_private.xml
@@ -14,4 +14,16 @@
2026-01-01
{'divisor_werktage': 26.0, 'divisor_arbeitstage': 22.0, 'abfertigung_alt_staffel': [[36, 2.0], [60, 3.0], [120, 4.0], [180, 6.0], [240, 9.0], [300, 12.0]], 'abfertigung_steuersatz': 6.0}
+
+
+ § 67 Abs 8 lit a — Vergleich BV-Anwartschafts-Teil Deckel
+ at_lst_67_abs8_vergleich_bv
+
+
+
+
+ 2026-01-01
+ 7500.0
+
\ No newline at end of file
diff --git a/addons/l10n_at_hr_payroll_private/data/salary_rules_endabrechnung_private.xml b/addons/l10n_at_hr_payroll_private/data/salary_rules_endabrechnung_private.xml
index 90780b4..79a078d 100644
--- a/addons/l10n_at_hr_payroll_private/data/salary_rules_endabrechnung_private.xml
+++ b/addons/l10n_at_hr_payroll_private/data/salary_rules_endabrechnung_private.xml
@@ -11,6 +11,10 @@
+
+
+
+
@@ -130,6 +134,45 @@ result = payslip._at_abf_frei_ueberhang(result_rules)
]]>
+
+
+ Vergleichssumme — Rest (§ 67 Abs 8 lit a)
+ VERGLEICH
+
+
+
+ python
+ result = 'ATP_VERGLEICH' in inputs and inputs['ATP_VERGLEICH'].amount
+ neutral
+ laufend
+
+ code
+
+
+
+
+
+ Vergleichssumme — BV-Anwartschafts-Teil (§ 67 Abs 8 lit a)
+ VERGLEICH_BV
+
+
+
+ python
+ result = 'ATP_VERGLEICH_BV' in inputs and inputs['ATP_VERGLEICH_BV'].amount
+ neutral
+ laufend
+
+ code
+
+
+
Zeitguthaben Normalarbeitszeit + 50 % (§ 19e Abs 2 AZG)
diff --git a/addons/l10n_at_hr_payroll_private/data/salary_rules_lst_private.xml b/addons/l10n_at_hr_payroll_private/data/salary_rules_lst_private.xml
index 052e094..5aa2a44 100644
--- a/addons/l10n_at_hr_payroll_private/data/salary_rules_lst_private.xml
+++ b/addons/l10n_at_hr_payroll_private/data/salary_rules_lst_private.xml
@@ -134,4 +134,19 @@ result = payslip._at_abf_alt_lohnsteuer(result_rules)
result = payslip._at_abf_frei_lst_6(result_rules)
]]>
+
+ Lohnsteuer Vergleich — BV-Anwartschafts-Teil (§ 67 Abs 8 lit a)
+ LST_VERGLEICH_BV
+
+
+
+ python
+ result = payslip._at_vergleich_bv_lst_6(result_rules) > 0
+ code
+ neutral
+ keine
+
+
\ No newline at end of file
diff --git a/addons/l10n_at_hr_payroll_private/docs/rechenfaelle/endabrechnung.md b/addons/l10n_at_hr_payroll_private/docs/rechenfaelle/endabrechnung.md
index c35c5f9..408d1d8 100644
--- a/addons/l10n_at_hr_payroll_private/docs/rechenfaelle/endabrechnung.md
+++ b/addons/l10n_at_hr_payroll_private/docs/rechenfaelle/endabrechnung.md
@@ -1,4 +1,4 @@
-# Endabrechnungs-Zyklus — Rechenfälle AP14-A / AP15-A
+# Endabrechnungs-Zyklus — Rechenfälle AP14-A / AP15-A / AP15-C
Stand: 2026-09. AP14-A bildet die Beendigungs-Abrechnung auf der
privaten Structure ab: Urlaubsersatzleistung (UEL), Abgangsentschädigung,
@@ -166,15 +166,84 @@ HBG), begünstigt sind also höchstens 187.110 €.
bestätigt (lückenlose Vordienstzeiten zum Lohnkonto, § 76 EStG,
keine Anspruchsumwandlung, lb-end-15).
-## Offene Punkte (AP15-C und Verifikationsreste)
+## RF-VGL-1 — Vergleichssumme Rest, Fünftelregelung (§ 67 Abs 8 lit a)
+
+Außergerichtlicher Vergleich 9.000 € brutto für nachgeforderte
+Mehleistungen (lb-end-21 Beispiel 1; kein erkennbarer Abfertigungs-
+anteil, Ansprüche strittig):
+
+- `VERGLEICH` (bemessung neutral, `l10n_at_s67_fuenftel`): nach Abzug
+ der §-62-DN-Beiträge (Beispiel: 18,07 % → 1.626,30 €) ist 1/5 der
+ Bemessung steuerfrei (1.474,74 €), Deckel 12.474 € (= 1/5 × 9 ×
+ HBG) greift hier nicht;
+- 4/5 (5.898,96 € im Beispiel) laufen tarifisch im Zuflussmonat
+ (LST_LFD), ohne Jahressechstel-Erhöhung (Abs 2 nicht anzuwenden);
+ bei Zahlung Monate nach dem DV-Ende ohne weitere laufende Bezüge
+ ist die Bemessungsgrundlage nur der Vergleichsbetrag;
+- SV-pflichtig im Zuflussmonat (Pauschalvergleich im Zweifel
+ beitragspflichtig, E-MVB 011-02-00-001); Ansprüche aus der aktiven
+ DV-Zeit sind je Beitragsmonat aufzurollen (Kern-Feld
+ `l10n_at_beitragsmonat`), Ansprüche nach DV-Ende verlängern die
+ Pflichtversicherung (lb-end-22);
+- voll LNK-pflichtig (§ 41 Abs 4 lit b FLAG nennt nur Abs 3 und 6).
+
+## RF-VGL-2 — BV-Anwartschafts-Teil (6 %, Deckel 7.500 €)
+
+Vergleichssumme 9.000 €, davon Anteil für Zeiträume mit
+BV-Kassen-Anwartschaft (lb-end-21 Beispiel 2):
+
+- erste 7.500 € (`VERGLEICH_BV`): nach anteiligem §-62-Abzug
+ (83,33 % der Beiträge) mit 6 % = `LST_VERGLEICH_BV`;
+- Überhang 1.500 € (16,67 % der Beiträge) fällt in die
+ Fünftelregelung zurück (1/5 steuerfrei, 4/5 tarifisch);
+- SV und LNK zur Gänze pflichtig; Deckel parametrisiert
+ (`at_lst_67_abs8_vergleich_bv` — § 67 Abs 8 unterliegt der
+ Inflationsanpassung § 33a/§ 124b EStG);
+- beim Übertritt Alt→Neu gilt der 6-%-Teil nur für übertragene
+ Zeiträume; eingefrorene Zeiträume laufen über die Fünftelregelung
+ (lb-end-21 Hinweis).
+
+## RF-VGL-3 — Deckelfälle
+
+- Rest mit Bemessungsgrundlage 70.000 € (lb-end-21 Deckelbeispiel;
+ das ist bereits die §-62-gekürzte Grundlage): Fünftel an der Grenze
+ 12.474 € (9 × HBG/5), Rest tarifisch. Da die Engine erst den
+ §-62-Abzug ansetzt und DANACH das Fünftel deckelt, greift der Deckel
+ brutto-abhängig ab ca. 76.200 € (Testfall 100.000 € brutto);
+- BV-Teil 10.000 €: begünstigt 7.500 € mit 6 %, Überhang 2.500 € in
+ die Fünftelregelung.
+
+## RF-VGL-4 — Wizard, Aufteilung und Pfändung
+
+- Erkennbare Abfertigungsanteile werden getrennt erfasst: gesetzliche
+ Abfertigung über den Abf-Alt-Abschnitt (§ 67 Abs 3; ausdrücklich
+ vereinbarte Abfertigung unterhalb der Klagsforderung → gesamt § 67
+ Abs 3, VwGH 2013/13/0001 = ARD 6490/20/2016), freiwillige über den
+ Abf-Frei-Abschnitt (§ 67 Abs 6); der Rest über den Vergleichs-
+ abschnitt. Widmungen sind nur anzuerkennen, wenn die Ansprüche
+ strittig waren (lb-end-22); beim Pauschalvergleich ist der
+ Verhältnisfaktor Forderung : Vergleichssumme zu dokumentieren
+ (E-MVB 011-02-00-001); Pensionsabfindungen (lit e) und
+ Sozialplan-Zahlungen (lit f) gehören nicht in lit a.
+- Wizard-Bestätigungspflichten: Aufgliederung/Strittigkeit,
+ BV-Anwartschaft je betroffenem Zeitraum, BV-Teil ≤ Deckel (über-
+ steigender Betrag als Rest erfassen). Vergleichs-Nachweis landet
+ auf dem Lauf (Payslip-Tab) und ist für den gesonderten Lohnzettel
+ bei Zahlung nach dem Beendigungsmonat aufzubereiten (LStR Rz 911a,
+ GP7-Rest).
+- `VERGLEICH`/`VERGLEICH_BV` laufen — wie `ABGANG` — NICHT über den
+ §-291d-Beendigungstopf (offener Verifikationspunkt, s. u.); der
+ Fünftel-Tarifteil ist über die laufende Bemessung pfändbar, seine
+ Lohnsteuer steckt in `LST_LFD`.
+
+## Offene Punkte (Verifikationsreste)
-- **Vergleich (§ 67 Abs 8 lit a)** → AP15-C mit rollierender
- 12-Monats-Basis und Alt-/Neu-Aufteilung (freiwillige Abfertigung
- § 67 Abs 6 ist mit AP15-B umgesetzt).
- KZ-Positionierung der Abs-6-Bezüge (außerhalb KZ 210/220, LSt in
KZ 260 — GP3-Konvention wie Abs 3) und die Zuordnung des Z-6-Überhangs
zum §-291d-EO-Topf sind gegen LStR Rz 1087 ff / § 291d EO zu
- verifizieren, bevor der MVP freigegeben wird.
+ verifizieren, bevor der MVP freigegeben wird. Dasselbe gilt für die
+ KZ-Positionierung der lit-a-Tarif-Teile (laufend über LST_LFD) und
+ die §-291d-Zuordnung von `VERGLEICH`/`VERGLEICH_BV`.
- Geminderte Bezüge in der 12-Monats-Basis: die LStR-Rückrechnungs-
praxis (zwölf Monate voller Bezüge) widerspricht dem VwGH
(Krankengeld 6/7 einbeziehen) — bewusst ohne Automatik, Basis ist
diff --git a/addons/l10n_at_hr_payroll_private/models/account_chart_private.py b/addons/l10n_at_hr_payroll_private/models/account_chart_private.py
index d8fabe3..57f5a22 100644
--- a/addons/l10n_at_hr_payroll_private/models/account_chart_private.py
+++ b/addons/l10n_at_hr_payroll_private/models/account_chart_private.py
@@ -70,6 +70,7 @@ L10N_AT_BUCHUNGSMATRIX = {
'rule_lst_sz': {'credit': ('3540',)},
'rule_lst_abf': {'credit': ('3540',)},
'rule_lst_abf_frei': {'credit': ('3540',)},
+ 'rule_lst_vergleich_bv': {'credit': ('3540',)},
# Gesetzliche Abfertigung Alt: bis zur Referenzbuchhaltungs-
# Fixierung allgemeiner Gehalts-/Lohnaufwand, company-dependent
# Regelkonto bleibt übersteuerbar.
@@ -78,6 +79,10 @@ L10N_AT_BUCHUNGSMATRIX = {
# tarifischer Überhang — gleiche vorläufige Aufwandskonten.
'rule_abf_frei': {'debit': ('6200',), 'debit_arbeiter': ('6000',)},
'rule_abf_frei_tarif': {'debit': ('6200',), 'debit_arbeiter': ('6000',)},
+ # Vergleichssumme (§ 67 Abs 8 lit a): Rest und BV-Anwartschafts-
+ # Teil — gleiche vorläufige Aufwandskonten wie die Abfertigungen.
+ 'rule_vergleich': {'debit': ('6200',), 'debit_arbeiter': ('6000',)},
+ 'rule_vergleich_bv': {'debit': ('6200',), 'debit_arbeiter': ('6000',)},
# Nettoauszahlung (reconcilable, s. o.)
'rule_netto': {'credit': ('3850',)},
# DG-Beiträge (650–655 SV Arbeiter · 656–659 SV Angestellte)
diff --git a/addons/l10n_at_hr_payroll_private/models/endabrechnung.py b/addons/l10n_at_hr_payroll_private/models/endabrechnung.py
index 6d65af5..8b09124 100644
--- a/addons/l10n_at_hr_payroll_private/models/endabrechnung.py
+++ b/addons/l10n_at_hr_payroll_private/models/endabrechnung.py
@@ -2,6 +2,7 @@
"""AP14-A: Endabrechnungs-Zyklus — Urlaubsersatzleistung (UEL),
Abgangsentschädigung (§ 67 Abs 8 lit b), Aufrechnung offener Vorschüsse
und Zeitguthaben/-schulden (§ 19e AZG).
+AP15-C: Vergleichssummen (§ 67 Abs 8 lit a).
Am Originalwortlaut verifiziert (.ris/, Stand 2026-09-14):
- § 11 Abs 2 ASVG: die Pflichtversicherung besteht für die Zeit des
@@ -25,6 +26,21 @@ Am Originalwortlaut verifiziert (.ris/, Stand 2026-09-14):
- § 19e Abs 2 AZG: Guthaben an Normalarbeitszeit + 50 % Zuschlag
(Entfall bei unberechtigtem vorzeitigem Austritt; KV kann
abweichend regeln).
+- § 67 Abs 8 lit a EStG (AP15-C, Stand 2026-09-15): Vergleichssummen
+ sind, SOWEIT sie nicht nach Abs 3, 6 oder dem letzten Satz mit dem
+ festen Steuersatz zu versteuern sind, nach Abs 10 im Kalendermonat
+ der Zahlung zu erfassen; nach Abzug der §-62-Z-3/4/5-Beiträge ist
+ ein Fünftel steuerfrei (Deckel ein Fünftel des Neunfachen der
+ monatlichen HBG), Abs 2 (Sechstel) ist nicht anzuwenden. Erkennbare
+ Abfertigungsanteile laufen getrennt über Abs 3 (VwGH
+ 2013/13/0001 = ARD 6490/20/2016: ausdrücklich vereinbarte
+ gesetzliche Abfertigung → gesamt Abs 3) bzw. Abs 6. Fallen die
+ Vergleichssummen bei oder nach Beendigung an UND betreffen sie
+ Zeiträume mit BV-Kassen-Anwartschaft, sind sie bis 7 500 € mit 6 %
+ zu versteuern (parametrisiert, §-33a-Indexkette); der übersteigende
+ Teil fällt in die Fünftelregelung zurück. SV-Aufrollung aktiver
+ Ansprüche je Beitragsmonat bzw. PV-Verlängerung: lb-end-22
+ (E-MVB 011-02-00-001), Kern-Mechanismus l10n_at_beitragsmonat.
"""
from datetime import timedelta
@@ -53,6 +69,13 @@ class HrSalaryRule(models.Model):
'(Deckel: ein Fünftel des Neunfachen der monatlichen HBG), '
'der Rest läuft tarifisch im Zuflussmonat — ohne '
'Jahressechstel-Erhöhung (Abs 2 nicht anzuwenden).')
+ l10n_at_vergleich_bv = fields.Boolean(
+ string='AT § 67 Abs 8 lit a — BV-Anwartschafts-Teil',
+ help='Teil einer Vergleichssumme, der bei oder nach Beendigung '
+ 'für Zeiträume mit BV-Kassen-Anwartschaft ausbezahlt wird: '
+ 'bis zum Deckel (at_lst_67_abs8_vergleich_bv, 2026: 7 500 €) '
+ 'mit 6 %, der übersteigende Teil fällt in die '
+ 'Fünftelregelung zurück. SV-pflichtig und LNK-pflichtig.')
l10n_at_s26_z4_reise = fields.Boolean(
string='AT § 26 Z 4 Reisevergütung',
help='§-26-Z-4-Reisevergütungen zählen nicht in die '
@@ -115,6 +138,13 @@ class HrPayslip(models.Model):
l10n_at_abf_frei_nachweis = fields.Text(
string='AT Freiwillige Abfertigung — Nachweis/Begründung',
copy=False)
+ l10n_at_vergleich_nachweis = fields.Text(
+ string='AT Vergleich — Nachweis/Begründung', copy=False,
+ help='Aufgliederung der Vergleichsvereinbarung (lb-end-21): '
+ 'strittige Ansprüche, erkennbare Abfertigungsanteile, '
+ 'BV-Anwartschafts-Zeiträume; beim Pauschalvergleich der '
+ 'Verhältnisfaktor Forderungsbetrag : Vergleichssumme '
+ '(E-MVB 011-02-00-001).')
# ------------------------------------------------------------------
# SV-Tage-Umrechnung und Verlängerungsmonate (§ 11 Abs 2 ASVG)
@@ -401,6 +431,9 @@ class HrPayslip(models.Model):
else:
linie = self.line_ids.filtered(lambda l: l.code == rule.code)
total += sum(linie.mapped('total'))
+ # § 67 Abs 8 lit a: der über den BV-6-%-Deckel hinausgehende
+ # Teil der Vergleichssumme fällt in die Fünftelregelung zurück.
+ total += self._at_vergleich_bv_ueberhang(result_rules)
return total
def _at_fuenftel_tarif_basis(self, result_rules=None):
@@ -425,6 +458,77 @@ class HrPayslip(models.Model):
frei = min(bemessung / 5.0, deckel)
return bemessung - frei
+ # ------------------------------------------------------------------
+ # Vergleichssumme — § 67 Abs 8 lit a (Rest, BV-Anwartschafts-Teil)
+ # ------------------------------------------------------------------
+ def _at_vergleich_bv_total(self, result_rules=None):
+ """Summe der als BV-Anwartschafts-Teil gekennzeichneten
+ Vergleichs-Zeilen (Zeiträume mit BV-Kassen-Anwartschaft,
+ § 67 Abs 8 lit a letzter Satz)."""
+ self.ensure_one()
+ total = 0.0
+ for rule in self.struct_id.rule_ids:
+ if not rule.l10n_at_vergleich_bv:
+ continue
+ if result_rules is not None:
+ if rule.code in result_rules:
+ total += result_rules[rule.code]['total']
+ else:
+ total += sum(self.line_ids.filtered(
+ lambda l, c=rule.code: l.code == c).mapped('total'))
+ return total
+
+ def _at_vergleich_bv_beguenstigt(self, result_rules=None):
+ """Bis zum Deckel (2026: 7 500 €) begünstigter Teil — 6 %-Fahrspur
+ (parametrisiert: § 67 Abs 8 unterliegt der Inflationsanpassung
+ § 33a/§ 124b EStG)."""
+ self.ensure_one()
+ deckel = float(self._l10n_at_param('at_lst_67_abs8_vergleich_bv'))
+ return min(self._at_vergleich_bv_total(result_rules), deckel)
+
+ def _at_vergleich_bv_ueberhang(self, result_rules=None):
+ """Über den BV-Deckel hinausgehender Teil — fällt in die
+ Fünftelregelung zurück (erster Satz des lit a)."""
+ self.ensure_one()
+ deckel = float(self._l10n_at_param('at_lst_67_abs8_vergleich_bv'))
+ return max(self._at_vergleich_bv_total(result_rules) - deckel, 0.0)
+
+ def _at_vergleich_bv_lst_6(self, result_rules=None):
+ """6 % auf den begünstigten BV-Teil nach anteiligem §-62-Abzug
+ (LStR Rz 1102b: die SV-Beiträge sind den Teilbeträgen
+ anteilsmäßig zuzuordnen — approximiert über den effektiven
+ DN-Satz der laufenden Basis, wie im Fünftelpfad)."""
+ self.ensure_one()
+ beguenstigt = self._at_vergleich_bv_beguenstigt(result_rules)
+ if beguenstigt <= 0:
+ return 0.0
+ satz = sum(self._l10n_at_sv_satz(
+ 'dn', komponente,
+ staffel_basis=self._l10n_at_sv_basis_roh(result_rules))
+ for komponente in ('kv', 'pv', 'av', 'ak', 'wf'))
+ bemessung = beguenstigt * (1.0 - satz / 100.0)
+ steuersatz = float(self._l10n_at_param(
+ 'at_endabrechnung')['abfertigung_steuersatz'])
+ return bemessung * steuersatz / 100.0
+
+ def _at_fuenftel_frei_betr(self, result_rules=None):
+ """Steuerfreies Fünftel der Fünftel-Bezüge (inkl. BV-Überhang),
+ gedeckelt mit einem Fünftel des Neunfachen der monatlichen HBG
+ (lb-end-21: 2026 6.930 × 9/5 = 12.474) — getrennt vom
+ tarifischen Rest für die Wizard-Vorschau."""
+ self.ensure_one()
+ total = self._at_fuenftel_total(result_rules)
+ if total <= 0:
+ return 0.0
+ satz = sum(self._l10n_at_sv_satz(
+ 'dn', komponente,
+ staffel_basis=self._l10n_at_sv_basis_roh(result_rules))
+ for komponente in ('kv', 'pv', 'av', 'ak', 'wf'))
+ bemessung = max(total * (1.0 - satz / 100.0), 0.0)
+ deckel = float(self._l10n_at_param('at_sv_hbg_monatlich')) \
+ * 9.0 / 5.0
+ return min(bemessung / 5.0, deckel)
+
# ------------------------------------------------------------------
# Engine-Anbindung: Tarif-Teil ohne Sechstel, LNK-Pflicht, SV-Umlegung
# ------------------------------------------------------------------
@@ -438,11 +542,12 @@ class HrPayslip(models.Model):
def _at_lnk_basis(self, steuer, result_rules=None):
"""Upstream-Überschreibung (Muster account_posting_private;
Vorlage payslip_private._at_lnk_basis — bei Änderungen dort
- gegen diesen Pin neu abgleichen): Kündigungsentschädigungen
- sind Arbeitslöhne iSd § 41 Abs 3 FLAG und unterliegen NICHT
- den Abs-4-Ausschlüssen (lit b nennt nur § 67 Abs 3 und 6) —
- sie sind VOR der Freibetragsstaffel in die LNK-Basis
- aufzunehmen."""
+ gegen diesen Pin neu abgleichen): Kündigungsentschädigungen und
+ Vergleichssummen sind Arbeitslöhne iSd § 41 Abs 3 FLAG und
+ unterliegen NICHT den Abs-4-Ausschlüssen (lit b nennt nur
+ § 67 Abs 3 und 6 — der lit-a-BV-Teil ist ebenfalls nicht
+ ausgenommen) — sie sind VOR der Freibetragsstaffel in die
+ LNK-Basis aufzunehmen."""
self.ensure_one()
basis = (self._l10n_at_brutto('laufend', result_rules)
+ self._l10n_at_brutto('sonstig', result_rules)
@@ -457,6 +562,10 @@ class HrPayslip(models.Model):
lambda l: l.code == rule.code)
basis -= sum(linie.mapped('total'))
basis += self._at_fuenftel_total(result_rules)
+ # BV-Anwartschafts-Teil: begünstigter ≤-Deckel-Teil hier, der
+ # Überhang läuft bereits über _at_fuenftel_total — zusammen
+ # die volle Vergleichssumme, LNK-pflichtig (lb-end-20).
+ basis += self._at_vergleich_bv_beguenstigt(result_rules)
basis = max(basis, 0.0)
if steuer in ('flaf', 'dz'):
version = self.version_id
diff --git a/addons/l10n_at_hr_payroll_private/tests/test_endabrechnung_private.py b/addons/l10n_at_hr_payroll_private/tests/test_endabrechnung_private.py
index ee7b5b0..8f59464 100644
--- a/addons/l10n_at_hr_payroll_private/tests/test_endabrechnung_private.py
+++ b/addons/l10n_at_hr_payroll_private/tests/test_endabrechnung_private.py
@@ -2,7 +2,9 @@
"""AP14-A: Endabrechnungs-Zyklus — UEL-Zweikomponenten (§ 67 Abs 8
lit d), SV-Verlängerung (§ 11 Abs 2 ASVG, sequentielle Fenster),
Abgangsentschädigung (§ 67 Abs 8 lit b Fünftel), Aufrechnung offener
-Vorschüsse (§ 293 Abs 3 EO) und Zeitguthaben (§ 19e Abs 2 AZG)."""
+Vorschüsse (§ 293 Abs 3 EO) und Zeitguthaben (§ 19e Abs 2 AZG).
+AP15-C: Vergleichssummen (§ 67 Abs 8 lit a) — Rest-Fünftel,
+BV-Anwartschafts-Teil mit 6 %/7.500-€-Deckel, Wizard."""
from datetime import date, timedelta
from odoo.exceptions import UserError
@@ -611,3 +613,188 @@ class EndabrechnungPrivateTest(TransactionCase):
self.assertEqual(slip.l10n_at_abf_frei_dienstzeit_monate, 180)
self.assertEqual(slip.l10n_at_abf_frei_nachweis,
'Vordienstzeiten zum Lohnkonto nachgewiesen')
+
+ # ------------------------------------------------------------------
+ # RF-VGL-1: Vergleichssumme Rest (§ 67 Abs 8 lit a, Fünftelregelung)
+ # ------------------------------------------------------------------
+ def test_18_vergleich_rest_fuenftel(self):
+ """9.000 € Vergleich-Rest (lb-end-21 Beispiel 1): nach
+ §-62-Abzug ist 1/5 steuerfrei (Deckel 12.474 €), 4/5 laufen
+ tarifisch im Zuflussmonat OHNE Sechstel-Effekt; SV-pflichtig im
+ Zuflussmonat, voll LNK-pflichtig."""
+ emp_mit = self._employee(ueberzahlung=3000.0)
+ emp_ohne = self._employee(ueberzahlung=3000.0)
+ slip_mit = self._slip(emp_mit)
+ slip_ohne = self._slip(emp_ohne)
+ self._input(slip_mit, 'ATP_VERGLEICH', 9000.0)
+
+ self.assertAlmostEqual(self._total(slip_mit, 'VERGLEICH'), 9000.0,
+ places=2)
+ # Sechstel unverändert (Abs 2 nicht anzuwenden)
+ self.assertAlmostEqual(
+ slip_mit._l10n_at_sechstel() - slip_ohne._l10n_at_sechstel(),
+ 0.0, places=2)
+ satz = sum(slip_mit._l10n_at_sv_satz(
+ 'dn', k, staffel_basis=slip_mit._l10n_at_sv_basis_roh())
+ for k in ('kv', 'pv', 'av', 'ak', 'wf'))
+ bemessung = 9000.0 * (1.0 - satz / 100.0)
+ deckel = float(slip_mit._l10n_at_param('at_sv_hbg_monatlich')) \
+ * 9.0 / 5.0
+ frei = min(bemessung / 5.0, deckel)
+ self.assertAlmostEqual(slip_mit._at_fuenftel_frei_betr(), frei,
+ places=2)
+ self.assertAlmostEqual(slip_mit._at_fuenftel_tarif_basis(),
+ bemessung - frei, places=2)
+ svdn_diff = (slip_mit._l10n_at_svdn_gesamt()
+ - slip_ohne._l10n_at_svdn_gesamt())
+ self.assertAlmostEqual(
+ slip_mit._l10n_at_bemessung_laufen()
+ - slip_ohne._l10n_at_bemessung_laufen() + svdn_diff,
+ bemessung - frei, places=2)
+ # LNK-Basis um die volle Vergleichssumme erhöht
+ self.assertAlmostEqual(
+ slip_mit._at_lnk_basis('flaf')
+ - slip_ohne._at_lnk_basis('flaf'), 9000.0, places=2)
+ # SV-pflichtig im Zuflussmonat (rohe Basis vor HBG-Cap)
+ self.assertAlmostEqual(
+ slip_mit._l10n_at_sv_basis_roh()
+ - slip_ohne._l10n_at_sv_basis_roh(), 9000.0, places=2)
+
+ # ------------------------------------------------------------------
+ # RF-VGL-2: BV-Anwartschafts-Teil (§ 67 Abs 8 lit a, 6 %/7.500 €)
+ # ------------------------------------------------------------------
+ def test_19_vergleich_bv_sechs_prozent(self):
+ """9.000 € Vergleichssumme mit BV-Anwartschaft (lb-end-21
+ Beispiel 2): erste 7.500 € nach anteiligem §-62-Abzug mit 6 %,
+ der Überhang von 1.500 € fällt in die Fünftelregelung zurück
+ (anteilige Beitragszuordnung, LStR Rz 1102b)."""
+ emp_mit = self._employee(ueberzahlung=3000.0)
+ emp_ohne = self._employee(ueberzahlung=3000.0)
+ slip_mit = self._slip(emp_mit)
+ slip_ohne = self._slip(emp_ohne)
+ self._input(slip_mit, 'ATP_VERGLEICH_BV', 9000.0)
+
+ self.assertAlmostEqual(self._total(slip_mit, 'VERGLEICH_BV'),
+ 9000.0, places=2)
+ satz = sum(slip_mit._l10n_at_sv_satz(
+ 'dn', k, staffel_basis=slip_mit._l10n_at_sv_basis_roh())
+ for k in ('kv', 'pv', 'av', 'ak', 'wf'))
+ steuersatz = float(slip_mit._l10n_at_param(
+ 'at_endabrechnung')['abfertigung_steuersatz'])
+ self.assertAlmostEqual(
+ self._total(slip_mit, 'LST_VERGLEICH_BV'),
+ 7500.0 * (1.0 - satz / 100.0) * steuersatz / 100.0, places=2)
+ # Überhang in die Fünftelregelung zurück
+ self.assertAlmostEqual(slip_mit._at_fuenftel_total(), 1500.0,
+ places=2)
+ deckel = float(slip_mit._l10n_at_param('at_sv_hbg_monatlich')) \
+ * 9.0 / 5.0
+ ueberhang_bem = 1500.0 * (1.0 - satz / 100.0)
+ self.assertAlmostEqual(
+ slip_mit._at_fuenftel_frei_betr(),
+ min(ueberhang_bem / 5.0, deckel), places=2)
+ # Sechstel unverändert; LNK und SV über die volle Summe
+ self.assertAlmostEqual(
+ slip_mit._l10n_at_sechstel() - slip_ohne._l10n_at_sechstel(),
+ 0.0, places=2)
+ self.assertAlmostEqual(
+ slip_mit._at_lnk_basis('flaf')
+ - slip_ohne._at_lnk_basis('flaf'), 9000.0, places=2)
+ self.assertAlmostEqual(
+ slip_mit._l10n_at_sv_basis_roh()
+ - slip_ohne._l10n_at_sv_basis_roh(), 9000.0, places=2)
+
+ # ------------------------------------------------------------------
+ # RF-VGL-3: Deckel — Fünftel an 9 × HBG/5, BV-Teil an 7.500 €
+ # ------------------------------------------------------------------
+ def test_20_vergleich_deckel(self):
+ """Deckelfälle (lb-end-21): der Fünftel-Deckel greift, wenn die
+ §-62-gekürzte Bemessung 5 × 12.474 € übersteigt — bei Brutto
+ 100.000 € ist das der Fall; das 70.000-€-Beispiel der Quelle
+ meint die bereits gekürzte Bemessungsgrundlage. BV-Teil
+ 10.000 € → 7.500 € begünstigt, 2.500 € zurück in die
+ Fünftelregelung."""
+ emp = self._employee(ueberzahlung=3000.0)
+ slip = self._slip(emp)
+ self._input(slip, 'ATP_VERGLEICH', 100000.0)
+ satz = sum(slip._l10n_at_sv_satz(
+ 'dn', k, staffel_basis=slip._l10n_at_sv_basis_roh())
+ for k in ('kv', 'pv', 'av', 'ak', 'wf'))
+ bemessung = 100000.0 * (1.0 - satz / 100.0)
+ deckel = float(slip._l10n_at_param('at_sv_hbg_monatlich')) \
+ * 9.0 / 5.0
+ self.assertGreater(bemessung / 5.0, deckel)
+ self.assertAlmostEqual(slip._at_fuenftel_frei_betr(), deckel,
+ places=2)
+ self.assertAlmostEqual(
+ slip._at_fuenftel_tarif_basis(), bemessung - deckel, places=2)
+
+ slip.input_line_ids.filtered(
+ lambda i: i.input_type_id.code == 'ATP_VERGLEICH').write(
+ {'amount': 0.0})
+ self._input(slip, 'ATP_VERGLEICH_BV', 10000.0)
+ self.assertAlmostEqual(slip._at_vergleich_bv_beguenstigt(), 7500.0,
+ places=2)
+ self.assertAlmostEqual(slip._at_vergleich_bv_ueberhang(), 2500.0,
+ places=2)
+ self.assertAlmostEqual(slip._at_fuenftel_total(), 2500.0,
+ places=2)
+ steuersatz = float(slip._l10n_at_param(
+ 'at_endabrechnung')['abfertigung_steuersatz'])
+ self.assertAlmostEqual(
+ self._total(slip, 'LST_VERGLEICH_BV'),
+ 7500.0 * (1.0 - satz / 100.0) * steuersatz / 100.0, places=2)
+
+ # ------------------------------------------------------------------
+ # RF-VGL-4: Wizard-E2E und Validierungen (Bestätigungspflichten)
+ # ------------------------------------------------------------------
+ def test_21_vergleich_wizard(self):
+ emp = self._employee()
+ emp.version_id.write({
+ 'structure_type_id': self.env.ref(
+ 'l10n_at_hr_payroll_private.structure_type_at_privat').id,
+ 'l10n_at_abfertigung_neu': False,
+ })
+ basis = {
+ 'employee_id': emp.id,
+ 'austrittsdatum': date(2026, 3, 15),
+ 'departure_reason_id': self.env.ref(
+ 'l10n_at_hr_payroll.departure_reason_einvernehmlich').id,
+ 'vergleich_aktiv': True,
+ 'vergleich_rest_betrag': 9000.0,
+ 'vergleich_nachweis':
+ 'Außergerichtlicher Vergleich, Ansprüche strittig',
+ }
+ wizard = self.env['l10n.at.payroll.endabrechnung.wizard'].create(
+ dict(basis))
+ with self.assertRaises(UserError):
+ wizard.action_payslip() # Aufgliederung nicht bestätigt
+ wizard.write({'vergleich_aufteilung_bestaetigt': True,
+ 'vergleich_bv_betrag': 8000.0,
+ 'vergleich_bv_anwartschaft_bestaetigt': True})
+ with self.assertRaises(UserError):
+ wizard.action_payslip() # BV-Teil über 7.500 €
+ wizard.write({'vergleich_bv_betrag': 5000.0,
+ 'vergleich_bv_anwartschaft_bestaetigt': False})
+ with self.assertRaises(UserError):
+ wizard.action_payslip() # BV-Anwartschaft nicht bestätigt
+ wizard.write({'vergleich_bv_betrag': 7500.0,
+ 'vergleich_bv_anwartschaft_bestaetigt': True})
+ ergebnis = wizard.action_payslip()
+ slip = self.env['hr.payslip'].browse(ergebnis['res_id'])
+
+ self.assertAlmostEqual(self._total(slip, 'VERGLEICH'), 9000.0,
+ places=2)
+ self.assertAlmostEqual(self._total(slip, 'VERGLEICH_BV'), 7500.0,
+ places=2)
+ self.assertEqual(
+ slip.l10n_at_vergleich_nachweis,
+ 'Außergerichtlicher Vergleich, Ansprüche strittig')
+ satz = sum(slip._l10n_at_sv_satz(
+ 'dn', k, staffel_basis=slip._l10n_at_sv_basis_roh())
+ for k in ('kv', 'pv', 'av', 'ak', 'wf'))
+ steuersatz = float(slip._l10n_at_param(
+ 'at_endabrechnung')['abfertigung_steuersatz'])
+ self.assertAlmostEqual(
+ self._total(slip, 'LST_VERGLEICH_BV'),
+ 7500.0 * (1.0 - satz / 100.0) * steuersatz / 100.0, places=2)
diff --git a/addons/l10n_at_hr_payroll_private/views/endabrechnung_views.xml b/addons/l10n_at_hr_payroll_private/views/endabrechnung_views.xml
index 64a6cec..aba8478 100644
--- a/addons/l10n_at_hr_payroll_private/views/endabrechnung_views.xml
+++ b/addons/l10n_at_hr_payroll_private/views/endabrechnung_views.xml
@@ -65,6 +65,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -135,6 +151,24 @@
+
+ hr.payslip.form.vergleich
+ hr.payslip
+
+
+
+
+
+
+
+
+
+
+
+
Endabrechnung (Beendigung)
l10n.at.payroll.endabrechnung.wizard
diff --git a/addons/l10n_at_hr_payroll_private/wizard/endabrechnung.py b/addons/l10n_at_hr_payroll_private/wizard/endabrechnung.py
index 687d4d9..3e28443 100644
--- a/addons/l10n_at_hr_payroll_private/wizard/endabrechnung.py
+++ b/addons/l10n_at_hr_payroll_private/wizard/endabrechnung.py
@@ -4,7 +4,9 @@ Beendigungs-Abrechnung (Urlaubsersatzleistung, Abgangsentschädigung,
Zeitguthaben/-schulden, offene Vorschüsse) und legt den
Endabrechnungs-Lauf mit vorbefüllten Eingaben an. Reiner
Sammel-/Anlageprozess; die BV-Anwartschaft läuft über den eigenen
-BV-Wizard (GP5), die Pfändung über die AP11-Maschine."""
+BV-Wizard (GP5), die Pfändung über die AP11-Maschine.
+AP15-C: Vergleichssumme (§ 67 Abs 8 lit a) — Rest (Fünftelregelung)
+und BV-Anwartschafts-Teil (bis 7.500 € mit 6 %)."""
from datetime import timedelta
from dateutil.relativedelta import relativedelta
@@ -138,6 +140,49 @@ class L10nAtEndabrechnungWizard(models.TransientModel):
help='Vordienstzeiten-Nachweis (Lohnkonto § 76 EStG), erhaltene '
'Abfertigungen, Basis-Abweichungen und Anspruchsgrundlage '
'dokumentieren.')
+ vergleich_aktiv = fields.Boolean(
+ string='Vergleich (§ 67 Abs 8 lit a)',
+ help='Auf gerichtlichen oder außergerichtlichen Vergleichen '
+ 'beruhende Vergleichssummen sowie Bereinigungen/Nachzahlungen '
+ 'aus Urteilen/Bescheiden und Zahlungen bei '
+ 'Kündigungsanfechtung (lb-end-21). Erkennbare '
+ 'Abfertigungsanteile werden getrennt über die Abschnitte '
+ 'Abfertigung Alt (§ 67 Abs 3) bzw. freiwillige Abfertigung '
+ '(§ 67 Abs 6) erfasst; Pensionsabfindungen (lit e) und '
+ 'Sozialplan-Zahlungen (lit f) gehören nicht hierher.')
+ vergleich_rest_betrag = fields.Monetary(
+ string='Vergleichssumme — Rest (Fünftelregelung)',
+ currency_field='currency_id',
+ help='Nach Abzug der §-62-Beiträge ist ein Fünftel steuerfrei '
+ '(Deckel 1/5 × 9 × monatliche HBG), 4/5 laufen tarifisch im '
+ 'Zuflussmonat — ohne Jahressechstel-Effekt (§ 67 Abs 8 lit '
+ 'a iVm Abs 10).')
+ vergleich_bv_betrag = fields.Monetary(
+ string='Vergleichssumme — BV-Anwartschafts-Teil',
+ currency_field='currency_id',
+ help='Anteil für Zeiträume mit BV-Kassen-Anwartschaft: bis zum '
+ 'Deckel (2026: 7 500 €) mit 6 %, der übersteigende Teil '
+ 'fällt in die Fünftelregelung zurück. Der Alt-/Neu-Übertritt '
+ 'steuert die Anwendung (lb-end-21: eingefrorene Zeiträume '
+ 'kein 6 %-Teil).')
+ vergleich_bv_anwartschaft_bestaetigt = fields.Boolean(
+ string='BV-Anwartschaft für die betroffenen Zeiträume',
+ help='Voraussetzung des 6-%-Teils: die Vergleichssumme wird für '
+ 'Zeiträume ausbezahlt, für die eine Anwartschaft gegenüber '
+ 'einer BV-Kasse besteht (§ 67 Abs 8 lit a letzter Satz).')
+ vergleich_aufteilung_bestaetigt = fields.Boolean(
+ string='Aufgliederung und Strittigkeit bestätigt',
+ help='Bestätigung der Aufgliederung in der Vergleichsvereinbarung '
+ '(Praxistipp lb-end-21): Widmungen sind abgabenrechtlich nur '
+ 'anerkannt, wenn die Ansprüche strittig waren (lb-end-22, '
+ 'VwGH); beim Pauschalvergleich ist der Verhältnisfaktor '
+ 'Forderungsbetrag : Vergleichssumme zu dokumentieren '
+ '(E-MVB 011-02-00-001).')
+ vergleich_nachweis = fields.Text(
+ string='Nachweis/Begründung',
+ help='Vergleichstext, strittige Ansprüche, erkennbare '
+ 'Abfertigungsanteile, BV-Zeiträume und allfälliger '
+ 'Verhältnisfaktor dokumentieren.')
currency_id = fields.Many2one(
'res.currency', default=lambda self: self.env.company.currency_id)
@@ -186,6 +231,15 @@ class L10nAtEndabrechnungWizard(models.TransientModel):
abf_frei_netto_vorschau = fields.Monetary(
string='Begünstigter Teil netto (Vorschau)',
currency_field='currency_id', compute='_compute_vorschau')
+ vergleich_fuenftel_frei_vorschau = fields.Monetary(
+ string='Steuerfreies Fünftel (Vorschau)', currency_field='currency_id',
+ compute='_compute_vorschau')
+ vergleich_tarif_basis_vorschau = fields.Monetary(
+ string='Fünftel-Bezüge tarifischer Teil (Vorschau)',
+ currency_field='currency_id', compute='_compute_vorschau')
+ vergleich_bv_lst_vorschau = fields.Monetary(
+ string='LSt 6 % BV-Teil (Vorschau)', currency_field='currency_id',
+ compute='_compute_vorschau')
@api.onchange('employee_id')
def _onchange_employee(self):
@@ -250,7 +304,9 @@ class L10nAtEndabrechnungWizard(models.TransientModel):
'abf_frei_aktiv', 'abf_frei_betrag', 'abf_frei_basis_12',
'abf_frei_dienstzeit_monate',
'abf_frei_erhaltene_abfertigungen',
- 'abf_frei_gesetzlicher_anspruch')
+ 'abf_frei_gesetzlicher_anspruch',
+ 'vergleich_aktiv', 'vergleich_rest_betrag',
+ 'vergleich_bv_betrag')
def _compute_vorschau(self):
for wizard in self:
wizard.uel_lfd_vorschau = 0.0
@@ -268,6 +324,9 @@ class L10nAtEndabrechnungWizard(models.TransientModel):
wizard.abf_frei_ueberhang_vorschau = 0.0
wizard.abf_frei_lst_vorschau = 0.0
wizard.abf_frei_netto_vorschau = 0.0
+ wizard.vergleich_fuenftel_frei_vorschau = 0.0
+ wizard.vergleich_tarif_basis_vorschau = 0.0
+ wizard.vergleich_bv_lst_vorschau = 0.0
if not wizard.employee_id or not wizard.austrittsdatum:
continue
slip = wizard._vorschau_payslip()
@@ -300,6 +359,16 @@ class L10nAtEndabrechnungWizard(models.TransientModel):
split['beguenstigt'] * satz / 100.0
wizard.abf_frei_netto_vorschau = \
split['beguenstigt'] - wizard.abf_frei_lst_vorschau
+ if wizard.vergleich_aktiv:
+ # Der tarifische Fünftel-Teil läuft über die laufende
+ # Bemessung (LST_LFD); die Vorschau zeigt die Pfade
+ # getrennt (Fünftel-Pool inkl. ABGANG, falls erfasst).
+ wizard.vergleich_fuenftel_frei_vorschau = \
+ slip._at_fuenftel_frei_betr()
+ wizard.vergleich_tarif_basis_vorschau = \
+ slip._at_fuenftel_tarif_basis()
+ wizard.vergleich_bv_lst_vorschau = \
+ slip._at_vergleich_bv_lst_6()
def _vorschau_payslip(self):
"""Flüchtiger Payslip-Kontext für die Vorschau-Berechnung (kein
@@ -335,6 +404,7 @@ class L10nAtEndabrechnungWizard(models.TransientModel):
'l10n_at_abf_frei_gesetzlicher_anspruch':
self.abf_frei_gesetzlicher_anspruch,
'l10n_at_abf_frei_nachweis': self.abf_frei_nachweis,
+ 'l10n_at_vergleich_nachweis': self.vergleich_nachweis,
})
eingaben = [
('ATP_UEL_TAGE', self.uel_tage),
@@ -342,6 +412,10 @@ class L10nAtEndabrechnungWizard(models.TransientModel):
* self.abf_alt_mehrfach if self.abf_alt_aktiv else 0.0),
('ATP_ABF_FREI', self.abf_frei_betrag
if self.abf_frei_aktiv else 0.0),
+ ('ATP_VERGLEICH', self.vergleich_rest_betrag
+ if self.vergleich_aktiv else 0.0),
+ ('ATP_VERGLEICH_BV', self.vergleich_bv_betrag
+ if self.vergleich_aktiv else 0.0),
]
slip.input_line_ids = [
(0, 0, {
@@ -366,6 +440,10 @@ class L10nAtEndabrechnungWizard(models.TransientModel):
* self.abf_alt_mehrfach if self.abf_alt_aktiv else 0.0),
('ATP_ABF_FREI', self.abf_frei_betrag
if self.abf_frei_aktiv else 0.0),
+ ('ATP_VERGLEICH', self.vergleich_rest_betrag
+ if self.vergleich_aktiv else 0.0),
+ ('ATP_VERGLEICH_BV', self.vergleich_bv_betrag
+ if self.vergleich_aktiv else 0.0),
]
def _abf_alt_validieren(self):
@@ -427,12 +505,43 @@ class L10nAtEndabrechnungWizard(models.TransientModel):
'gesetzlicher Anspruch und Dienstzeit dürfen '
'nicht negativ sein.'))
+ def _vergleich_validieren(self):
+ self.ensure_one()
+ if not self.vergleich_aktiv:
+ return
+ if not self.vergleich_aufteilung_bestaetigt:
+ raise UserError(_('Bitte die Aufgliederung der Vergleichs-'
+ 'summe und die Strittigkeit der Ansprüche '
+ 'bestätigen (lb-end-21/22).'))
+ if self.vergleich_rest_betrag < 0 or self.vergleich_bv_betrag < 0:
+ raise UserError(_('Vergleichsbeträge dürfen nicht negativ '
+ 'sein.'))
+ if not self.vergleich_rest_betrag and not self.vergleich_bv_betrag:
+ raise UserError(_('Bitte den Rest der Vergleichssumme oder '
+ 'den BV-Anwartschafts-Teil erfassen; '
+ 'erkennbare Abfertigungsanteile gehören in '
+ 'die Abfertigungs-Abschnitte.'))
+ slip = self._vorschau_payslip()
+ deckel = float(slip._l10n_at_param('at_lst_67_abs8_vergleich_bv'))
+ if self.vergleich_bv_betrag > deckel:
+ raise UserError(_(
+ 'Der BV-Anwartschafts-Teil ist mit %.2f € gedeckelt '
+ '(§ 67 Abs 8 lit a letzter Satz); den übersteigenden '
+ 'Betrag als Rest (Fünftelregelung) erfassen.') % deckel)
+ if self.vergleich_bv_betrag > 0 \
+ and not self.vergleich_bv_anwartschaft_bestaetigt:
+ raise UserError(_('Der 6-%-Teil setzt eine bestätigte '
+ 'BV-Anwartschaft für die betroffenen '
+ 'Zeiträume voraus (§ 67 Abs 8 lit a letzter '
+ 'Satz).'))
+
def action_payslip(self):
"""Endabrechnungs-Lauf im Austrittsmonat anlegen und die
gesammelten offenen Posten als Eingaben vorbefüllen."""
self.ensure_one()
self._abf_alt_validieren()
self._abf_frei_validieren()
+ self._vergleich_validieren()
erster = self.austrittsdatum.replace(day=1)
letzter = (erster + timedelta(days=32)).replace(day=1) \
- timedelta(days=1)
@@ -476,6 +585,8 @@ class L10nAtEndabrechnungWizard(models.TransientModel):
if self.abf_frei_aktiv else 0.0,
'l10n_at_abf_frei_nachweis':
self.abf_frei_nachweis if self.abf_frei_aktiv else False,
+ 'l10n_at_vergleich_nachweis':
+ self.vergleich_nachweis if self.vergleich_aktiv else False,
})
typen = {code: self.env['hr.payslip.input.type'].search(
[('code', '=', code)], limit=1)