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)