[IMP] l10n_at_hr_payroll_private: Lohnpfändung — Existenzminimum § 291a EO, DN-Abzug, Kostenersatz, DSE (AP11)
Pfändungs-/Exekutionsmaschinerie nach EO §§ 291/291a/292/301/302/ 292h für den Privatwirtschafts-DN (Plan: personalverrechnung/PLAN-Restluecken-AP11.md §AP11, MVP + Kostenersatz): - Neues Modell l10n.at.payroll.pfaendung (models/pfaendung.py): Gläubiger, Vollstreckungsträger, Pfändungsart, Zustellungsdatum, Forderungshöhe, Status aktiv/gesetzt/geloest, Verwalter (§ 301 Abs 1 Z 6), 4-Wochen-Zahlungssperre (§ 304), Kostenersatz-Variante (§ 292h), DSE-/Auszahlungsfristen (computed), Rang nach Einlangedatum, Constraint Löse-/Zustellungsdatum; hr.version: l10n_at_unterhaltspflichten (0–5) und l10n_at_sz_anspruch (§ 291a Abs 2 Z 1/Z 2); hr.salary.rule: Flags l10n_at_pfandung_unpfandbar (AP12-Schnittstelle) und l10n_at_pfandung_beendigung (AP14/15-Schnittstelle) - Engine (hr.payslip): Existenzminimum-Maschine § 291a EO je Topf (laufend/sz13/sz14/beendigung), Pfändungsberechnungsgrundlage § 291 EO (laufend = Bezüge − unpfändbar − SV-DN − LSt), Masse, Kostenersatz § 292h (2 %/max 8 € + 1 %/max 4 €, nie vom Existenzminimum), Regeln PFAENDUNG (seq 75) / PFAENDUNG_SZ (seq 76); result_rules enthält ungerundete Rule-Summen — Lesezugänge runden auf Cents (Zeilenbeträge), sonst 1-Cent-Drift - Parameter at_eo_azr (2025: 1.273,99 / 2026: 1.308,39 €) und at_eo_kostenersatz; DSE-Report (qweb-pdf auf hr.employee), Views/Menu unter Payroll-Wurzel, Security (payroll-Groups + Company-ir.rule) - Rechenfälle docs/rechenfaelle/pfaendung.md (RF-PFA-1…7): lb-pfa-03 exakt verifiziert (33.840 € → EX 16.684 € / pfändbar 17.156 €); 103/103 Tests grün - Offene Reste dokumentiert: Unterhaltsexekution 75 %/Koinzidenz, Vorschüsse § 290c, Sachbezüge (AP12), Beendigungs-Regeln (AP14/15), 2027-AZR-Werte Version 19.0.9.5.0
This commit is contained in:
@@ -2,6 +2,36 @@
|
||||
|
||||
## Current focus
|
||||
|
||||
**AP 11 Lohnpfändung umgesetzt — 103/103 Tests grün** (2026-09-13,
|
||||
freigegeben 2026-09-12/13, Plan: `personalverrechnung/PLAN-Restluecken-AP11.md` §AP11):
|
||||
|
||||
- **Model** `l10n.at.payroll.pfaendung` (models/pfaendung.py):
|
||||
Gläubiger, Vollstreckungsträger, Pfändungsart, Zustellungsdatum,
|
||||
Forderungshöhe, Status aktiv/gesetzt/geloest, Verwalter § 301,
|
||||
Zahlungssperre § 304, Kostenersatz-Variante § 292h, dse_frist/
|
||||
auszahlung_frist, Rang (computed), Constraint Löse-/Zustelldatum;
|
||||
`hr.version`: `l10n_at_unterhaltspflichten` (0–5), `l10n_at_sz_anspruch`;
|
||||
`hr.salary.rule`: Flags `l10n_at_pfandung_unpfandbar` (AP12-Schnittstelle),
|
||||
`l10n_at_pfandung_beendigung` (AP14/15-Schnittstelle).
|
||||
- **Engine** (hr.payslip): Existenzminimum-Maschine § 291a EO je Topf
|
||||
(laufend/sz13/sz14/beendigung), Pfändungsberechnungsgrundlage § 291 EO
|
||||
(laufend = Bezüge − unpfändbar − SV-DN − LSt), Masse, Kostenersatz
|
||||
§ 292h, Regeln `PFAENDUNG` (seq 75) / `PFAENDUNG_SZ` (seq 76),
|
||||
Parameter `at_eo_azr` (2026: 1.308,39) + `at_eo_kostenersatz`,
|
||||
DSE-Report (qweb-pdf), Views/Menu/Security (payroll-Groups +
|
||||
Company-Rule), Rechenfälle `docs/rechenfaelle/pfaendung.md`
|
||||
(RF-PFA-1…7; lb-pfa-03 exakt verifiziert: 33.840 → EX 16.684 /
|
||||
pfändbar 17.156). Version **19.0.9.5.0**.
|
||||
- **Fix im Testlauf:** `result_rules` enthält UNGERUNDETE Rule-Summen
|
||||
(SVDN_KV 79.699167 vs. Zeile 79.70) — Pfändungsberechnungsgrundlage
|
||||
muss die gerundeten Bezugsarten (Cents) verwenden → `round(..., 2)`
|
||||
in `_at_pfa_rule_amount`/`_at_pfa_bruto` (result_rules-Pfad) + finales
|
||||
`round` in `_at_pfaendung_basis` (alle Töpfe).
|
||||
- **Offene Reste** (dokumentiert, nicht geblockt): Unterhaltsexekution
|
||||
75 %/Koinzidenz, Vorschüsse § 290c, Sachbezüge (AP12), Beendigungs-
|
||||
Regeldetails (AP14/15), 2027-AZR-Werte. **Kein Commit** (Nutzer-
|
||||
Freigabe ausstehend).
|
||||
|
||||
**AP 8 Arbeits-/Entgeltbestätigungen umgesetzt — 152/152 Tests
|
||||
grün — damit ist der AP0–AP10-Nutzer-Katalog komplett** (2026-09-12,
|
||||
AEB-Session; siehe auch Parallel-Session-Notiz unten):
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Part of the odoo-at-payroll project. License: LGPL-3.
|
||||
{
|
||||
'name': 'Österreich - Privatwirtschaft Personalverrechnung (General-AT)',
|
||||
'version': '19.0.9.4.0',
|
||||
'version': '19.0.9.5.0',
|
||||
'author': 'Florian Egger',
|
||||
'website': 'https://gem360.at',
|
||||
'category': 'Human Resources/Payroll',
|
||||
@@ -108,6 +108,8 @@ gem360-Brücke).
|
||||
'data/salary_rules_lst_private.xml',
|
||||
'data/salary_rules_efzg_private.xml',
|
||||
'data/salary_rules_msch_private.xml',
|
||||
|
||||
'data/salary_rules_pfaendung_private.xml',
|
||||
'data/salary_rules_sv_private.xml',
|
||||
'data/salary_rules_bvg_private.xml',
|
||||
'data/salary_rules_bvg_private.xml',
|
||||
@@ -116,6 +118,7 @@ gem360-Brücke).
|
||||
'data/rule_parameters_wien_private.xml',
|
||||
'data/rule_parameters_efzg_private.xml',
|
||||
'data/rule_parameters_msch_private.xml',
|
||||
'data/rule_parameters_pfaendung_private.xml',
|
||||
'data/bv_kasse_seed.xml',
|
||||
'views/kv_views.xml',
|
||||
'views/bv_views.xml',
|
||||
@@ -123,9 +126,11 @@ gem360-Brücke).
|
||||
'views/vertragsfelder_views.xml',
|
||||
'views/mitarbeiter_views.xml',
|
||||
'views/aeb_views.xml',
|
||||
'views/pfaendung_views.xml',
|
||||
'report/gp8_reports.xml',
|
||||
'report/buchungsbeleg_report.xml',
|
||||
'report/bv_report.xml',
|
||||
'report/pfaendung_dse_report.xml',
|
||||
],
|
||||
'demo': ['data/demo.xml'],
|
||||
'countries': ['at'],
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<!-- ============================================================ -->
|
||||
<!-- AP11: Lohnpfändung — AZR-Werte (ASVG § 293 Abs 1 lit a -->
|
||||
<!-- sublit bb) versioniert je Gültigkeitsjahr. Die EO-§-291a- -->
|
||||
<!-- Grundbeträge (allg./erhöht/Unterhalt) und die HöBG werden -->
|
||||
<!-- in der Engine aus dem AZR abgeleitet (KB-KONFLIKTE A9: -->
|
||||
<!-- 2025 1.273,99 € BGBl II 417/2024; 2026 1.308,39 € BGBl II -->
|
||||
<!-- 263/2025). Jährliche Anpassung über die Anm.-Reihe nach- -->
|
||||
<!-- führen (Wartung, s. RUNBOOK). -->
|
||||
<!-- ============================================================ -->
|
||||
<record id="hr_rule_parameter_at_eo_azr" model="hr.rule.parameter">
|
||||
<field name="name">EO Pfändung — Ausgleichszulagenrichtsatz (AZR)</field>
|
||||
<field name="code">at_eo_azr</field>
|
||||
<field name="country_id" ref="base.at"/>
|
||||
</record>
|
||||
<record id="hr_rule_parameter_value_at_eo_azr_2025"
|
||||
model="hr.rule.parameter.value">
|
||||
<field name="rule_parameter_id" ref="hr_rule_parameter_at_eo_azr"/>
|
||||
<field name="date_from">2025-01-01</field>
|
||||
<field name="parameter_value">{'azr': 1273.99}</field>
|
||||
</record>
|
||||
<record id="hr_rule_parameter_value_at_eo_azr_2026"
|
||||
model="hr.rule.parameter.value">
|
||||
<field name="rule_parameter_id" ref="hr_rule_parameter_at_eo_azr"/>
|
||||
<field name="date_from">2026-01-01</field>
|
||||
<field name="parameter_value">{'azr': 1308.39}</field>
|
||||
</record>
|
||||
|
||||
<!-- Kostenersatz für Pfändungsdurchführung (§ 292h EO) und
|
||||
Drittschuldnererklärung (§ 302 EO) — pauschaliert, lb-pfa-09.
|
||||
USt auf den §-302-Kostenersatz: keine (VwGH 2011/15/0181). -->
|
||||
<record id="hr_rule_parameter_at_eo_kostenersatz"
|
||||
model="hr.rule.parameter">
|
||||
<field name="name">EO Pfändung — Kostenersatz (§ 292h/§ 302)</field>
|
||||
<field name="code">at_eo_kostenersatz</field>
|
||||
<field name="country_id" ref="base.at"/>
|
||||
</record>
|
||||
<record id="hr_rule_parameter_value_at_eo_kostenersatz"
|
||||
model="hr.rule.parameter.value">
|
||||
<field name="rule_parameter_id"
|
||||
ref="hr_rule_parameter_at_eo_kostenersatz"/>
|
||||
<field name="date_from">2017-01-01</field>
|
||||
<field name="parameter_value">{'erste_rate': 2.0, 'erste_max': 8.0, 'weitere_rate': 1.0, 'weitere_max': 4.0, 'dse_normal': 35.0, 'dse_ausnahme': 25.0}</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -0,0 +1,59 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<!-- ============================================================ -->
|
||||
<!-- AP11: Lohnpfändung — DN-Abzug (Einbehalt) in ATP_ABZUG. -->
|
||||
<!-- Reihenfolge: nach SV-DN (seq 60–65) und LSt (seq 70–71), -->
|
||||
<!-- vor NET (seq 100) — die Pfändungsberechnungsgrundlage -->
|
||||
<!-- (§ 291 EO) ist das Netto nach SV-DN/LSt (Bemessung 'neutral' -->
|
||||
<!-- → kein SV-/LSt-Effekt). PFAENDUNG = laufender Topf; -->
|
||||
<!-- PFAENDUNG_SZ = 13./14.-Bezug (eigener Berechnungskreis, -->
|
||||
<!-- lb-pfa-05). Kostenersatz (§ 292h/§ 302) ist Teil des -->
|
||||
<!-- pfändbaren Betrags (Verteilposition, keine extra Abzugs- -->
|
||||
<!-- zeile — sonst Doppelabzug; s. _at_pfaendung_kostenersatz). -->
|
||||
<!-- ============================================================ -->
|
||||
|
||||
<record id="rule_pfaendung" model="hr.salary.rule">
|
||||
<field name="name">Lohnpfändung — Einbehalt (laufender Bezug, § 291a EO)</field>
|
||||
<field name="code">PFAENDUNG</field>
|
||||
<field name="sequence" eval="75"/>
|
||||
<field name="category_id" ref="l10n_at_hr_payroll_private.category_atp_abzug"/>
|
||||
<field name="struct_id" ref="l10n_at_hr_payroll_private.structure_at_privat"/>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python"><![CDATA[
|
||||
# Einbehalt nur bei aktiver Pfändung und pfändbarem laufendem Bezug.
|
||||
result = bool(payslip.employee_id._at_pfaendungen_aktiv(payslip.date_to)) \
|
||||
and payslip._at_pfaendung_masse(result_rules, 'laufend') > 0
|
||||
]]></field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="l10n_at_bemessung">neutral</field>
|
||||
<field name="amount_python_compute"><![CDATA[
|
||||
# Laufender Topf: Berechnungsgrundlage (Netto nach SV-DN/LSt, ohne
|
||||
# unpfändbare Bezugsarten) minus Existenzminimum (§ 291a EO).
|
||||
result = payslip._at_pfaendung_masse(result_rules, 'laufend')
|
||||
]]></field>
|
||||
</record>
|
||||
|
||||
<record id="rule_pfaendung_sz" model="hr.salary.rule">
|
||||
<field name="name">Lohnpfändung — 13./14. Bezug (eigener Topf, § 291a EO)</field>
|
||||
<field name="code">PFAENDUNG_SZ</field>
|
||||
<field name="sequence" eval="76"/>
|
||||
<field name="category_id" ref="l10n_at_hr_payroll_private.category_atp_abzug"/>
|
||||
<field name="struct_id" ref="l10n_at_hr_payroll_private.structure_at_privat"/>
|
||||
<field name="condition_select">python</field>
|
||||
<field name="condition_python"><![CDATA[
|
||||
# 13./14.-Bezug als eigener Pfändungsberechnungskreis (lb-pfa-05).
|
||||
masse = (payslip._at_pfaendung_masse(result_rules, 'sz13')
|
||||
+ payslip._at_pfaendung_masse(result_rules, 'sz14'))
|
||||
result = bool(payslip.employee_id._at_pfaendungen_aktiv(payslip.date_to)) \
|
||||
and masse > 0
|
||||
]]></field>
|
||||
<field name="amount_select">code</field>
|
||||
<field name="l10n_at_bemessung">neutral</field>
|
||||
<field name="amount_python_compute"><![CDATA[
|
||||
# 13. und 14. Bezug je eigener Existenzminimum (allg. Grundbetrag,
|
||||
# SZ = § 290b → nicht erhöht).
|
||||
result = (payslip._at_pfaendung_masse(result_rules, 'sz13')
|
||||
+ payslip._at_pfaendung_masse(result_rules, 'sz14'))
|
||||
]]></field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -0,0 +1,111 @@
|
||||
# Rechenfälle Lohnpfändung (AP11)
|
||||
|
||||
Modell: `models/pfaendung.py` (Pfändungsvormerkung + Existenzminimum-
|
||||
Maschine), `models/pfaendung_report.py` (DSE), Regeln `PFAENDUNG`/
|
||||
`PFAENDUNG_SZ` (`data/salary_rules_pfaendung_private.xml`), Parameter
|
||||
`at_eo_azr`/`at_eo_kostenersatz` (`data/rule_parameters_pfaendung_private.xml`).
|
||||
|
||||
## Rechtsquellen (verifiziert 2026-09-13)
|
||||
|
||||
- **EO § 291a** (Existenzminimum) — Wortlaut
|
||||
`.additional_docs/Lexis360_291a_eo.pdf` (NOR40233792, idF 1.7.2021):
|
||||
(1) allg. Grundbetrag = AZR; (2) +1/6 bei keinen §-290b-Leistungen
|
||||
(erhöhter allg. Grundbetrag), +20 % je Unterhaltsperson (max 5);
|
||||
(3) 30 % des Mehrbetrags + 10 % je Person (max 5); über dem Vierfachen
|
||||
des AZR (HöBG) jedenfalls pfändbar; (5) Grundbeträge auf volle Euro,
|
||||
Höchstteil nach § 291 Abs 2 (20-€-Schritte).
|
||||
- **EO § 292 Abs 4** (absolutes Geldexistenzminimum = halber allg.
|
||||
Grundbetrag) — `.additional_docs/Lexis360_292_eo.pdf` (NOR40187912).
|
||||
- **AZR** am RIS (KB-KONFLIKTE A9, 3. RIS-Runde 2026-09-11): 2025
|
||||
1.273,99 € (BGBl II 417/2024), 2026 1.308,39 € (BGBl II 263/2025).
|
||||
- **KB** lb-pfa-01…14 (Stand 2026-07), wk-pfa-01: Einlangen/Rang
|
||||
(lb-pfa-01/10), Durchführung/§ 304 (lb-pfa-07), Kostenersatz
|
||||
§ 292h/§ 302 (lb-pfa-09), Beendigungsansprüche § 291d (lb-pfa-03),
|
||||
Vorschüsse § 290c + abs. EX (lb-pfa-12), Unterhalt § 292f (lb-pfa-11),
|
||||
Sachbezüge (lb-pfa-14).
|
||||
|
||||
## Maschinenmodell (2026, AZR 1.308,39 €)
|
||||
|
||||
- **Werte (Parameter `at_eo_azr`):** allg. Grundbetrag **1.308 €** ·
|
||||
erhöhter (×7/6) **1.526 €** · Unterhaltsgrundbetrag (20 %) **261 €**
|
||||
(max 5) · HöBG (4×, 20-€-Abrundung) **5.220 €**/Bezugsmonat ·
|
||||
absolutes Geld-EX (½) **654 €**.
|
||||
- **Formel** (§ 291a, gegen lb-pfa-03 verifiziert — s. RF-PFA-3):
|
||||
`grund = (allg|erh) + n·unterhalt`; `hoechst = 5.220 × bezugsmonate`;
|
||||
`mehr = max(0, min(basis, hoechst) − grund)`;
|
||||
`EX = min(basis, grund + (0,30 + 0,10·n)·mehr)`; `pfändbar = basis − EX`.
|
||||
- **Töpfe:** laufend (Netto nach SV-DN/LSt, ohne unpfändbare Bezüge),
|
||||
13./14. (eigener Kreis, allg. Grundbetrag), Beendigung (erhöhter
|
||||
Grundbetrag, HöBG × Bezugsmonate, lb-pfa-03).
|
||||
- **Bemessungsart** `neutral` (DN-Abzug nach SV/LSt, kein SV-/LSt-Effekt);
|
||||
Kategorien `ATP_ABZUG`, seq 75/76 (nach LSt 71, vor NET 100).
|
||||
|
||||
## RF-PFA-1 — Laufender Topf, 0 vs. 2 Unterhaltspflichten
|
||||
|
||||
SZ-Anspruch (erhöht **nicht**), basis (Netto) 2.000 €:
|
||||
|
||||
- **n=0:** grund 1.308 · mehr 692 · EX 1.308 + 0,30×692 = **1.515,60 €**
|
||||
· pfändbar **484,40 €**.
|
||||
- **n=2:** grund 1.308 + 2×261 = 1.830 · mehr 170 · EX 1.830 + 0,50×170
|
||||
= **1.915,00 €** · pfändbar **85,00 €**.
|
||||
|
||||
## RF-PFA-2 — Höchstberechnungsgrundlage (Cap)
|
||||
|
||||
n=0, basis 6.000 € (> HöBG 5.220 €): mehr gedeckelt auf 5.220−1.308 =
|
||||
3.912 · EX = 1.308 + 0,30×3.912 = **2.481,60 €** · pfändbar =
|
||||
6.000 − 2.481,60 = **3.518,40 €** (davon 780 € = 6.000−5.220 jedenfalls
|
||||
pfändbar, 2.738,40 € aus dem Bereich bis HöBG).
|
||||
|
||||
## RF-PFA-3 — Beendigungstopf (lb-pfa-03, exakt)
|
||||
|
||||
Abfertigung „Alt" 6×6.000 € brutto, 6 % LSt → basis **33.840 €**;
|
||||
n=2; erhöhter Grundbetrag (Beendigung); 6 Bezugsmonate:
|
||||
|
||||
- grund = 1.526 + 2×261 = **2.048 €** · HöBG = 5.220×6 = **31.320 €**
|
||||
- basis > HöBG → mehr = 31.320 − 2.048 = 29.272
|
||||
- EX = 2.048 + 0,50×29.272 = **16.684,00 €** ✓ (KB: 16.684 €)
|
||||
- pfändbar = 33.840 − 16.684 = **17.156,00 €** ✓ (KB: 17.156 €)
|
||||
|
||||
## RF-PFA-4 — SZ-Anspruch vs. erhöhter Grundbetrag
|
||||
|
||||
basis 2.000 €, n=0:
|
||||
|
||||
- **mit SZ-Anspruch** (allg. 1.308): EX **1.515,60 €**, pfändbar 484,40 €.
|
||||
- **ohne SZ-Anspruch** (erhöht 1.526): mehr 474 · EX 1.526 + 0,30×474
|
||||
= **1.668,20 €** · pfändbar **331,80 €** (niedrigerer Bezug →
|
||||
höherer Schutz).
|
||||
|
||||
## RF-PFA-5 — Absolutes Geldexistenzminimum (§ 292 Abs 4)
|
||||
|
||||
2026: **654 €** (= ½ × 1.308,39 € abgerundet) — bleibt dem DN bei
|
||||
Rückverrechnung (Vorschüsse/Darlehen, lb-pfa-12) jedenfalls verbleiben;
|
||||
bei Unterhaltspfändung 75 % (490,50 €, dokumentierter Rest).
|
||||
|
||||
## RF-PFA-6 — Kostenersatz (§ 292h EO)
|
||||
|
||||
Pfändbare Masse 1.000 €: erste Zahlung 2 %/max 8 € = **8,00 €** +
|
||||
weitere 1 %/max 4 € auf (1.000−8) = **4,00 €** → Kostenersatz
|
||||
**12,00 €** (minded nur den an den Gläubiger gehenden Anteil, nie das
|
||||
EX; USt: keine, VwGH 2011/15/0181).
|
||||
|
||||
## RF-PFA-7 — DN-Abzug im Lauf (PFAENDUNG-Regel)
|
||||
|
||||
Mitarbeiter mit KV-Entgelt + aktive Pfändung: `PFAENDUNG`-Zeile (laufend
|
||||
Topf) = berechnungsgrundlage(laufend) − Existenzminimum; Netto wird
|
||||
entsprechend reduziert; `PFAENDUNG_SZ` greift nur im SZ-Monat.
|
||||
|
||||
## Offene Punkte / dokumentierte Reste
|
||||
|
||||
- **Unterhaltsexekution (75 %) & Koinzidenz/Unterhaltsmasse** (lb-pfa-05):
|
||||
bei pfändenden Unterhaltsgläubigern niedrigeres EX (75 %, exekutierte
|
||||
Pflicht zählt nicht mit) + exklusive Unterhaltsmasse — Vollmechanik
|
||||
(mehrere Pfändungen, Rang, Masse) im Parallellauf verifizieren;
|
||||
MVP nutzt die Schuldner-Unterhaltspflichten (n) in der Grundform.
|
||||
- **Vorschüsse/Nachzahlungen** (§ 290c EO, lb-pfa-12): Neutralität +
|
||||
Aufrollung; Schnittstelle über abs. Geld-EX (RF-PFA-5) vorbereitet.
|
||||
- **Sachbezugspfändung** (lb-pfa-14): Schnittstelle AP12 (Flag
|
||||
`l10n_at_pfandung_unpfandbar` / Sachbezugsbasis).
|
||||
- **Beendigungs-Regel-Anbindung** (AP14/15): Engine + RF-PFA-3 vorhanden,
|
||||
Regel folgt mit Abfertigung/Urlaubsersatzleistung.
|
||||
- **2027-Werte** (AZR): bei Jahreswechsel über `at_eo_azr` nachführen
|
||||
(Wartung, RUNBOOK).
|
||||
@@ -15,3 +15,5 @@ from . import wien_dag
|
||||
from . import efzg
|
||||
from . import mutterschutz
|
||||
from . import aeb
|
||||
from . import pfaendung
|
||||
from . import pfaendung_report
|
||||
|
||||
@@ -0,0 +1,446 @@
|
||||
# Part of the odoo-at-payroll project. License: LGPL-3.
|
||||
"""AP11: Lohnpfändung/Exekution — Pfändungsvormerkung (Drittschuldner),
|
||||
Existenzminimum-Maschine (§ 291a EO) je Pfändungstopf und Kostenersatz
|
||||
(§ 292h/§ 302 EO).
|
||||
|
||||
Verifizierte Grundlagen (RIS-Gate, 2026-09-13):
|
||||
- **§ 291a EO** (Unpfändbarer Freibetrag/Existenzminimum) — exakter
|
||||
Wortlaut `.additional_docs/Lexis360_291a_eo.pdf` (NOR40233792, idF
|
||||
1.7.2021): (1) allgemeiner Grundbetrag = AZR; (2) +1/6 bei keinen
|
||||
§-290b-Leistungen (erhöhter allgemeiner Grundbetrag) und +20 % je
|
||||
Unterhaltsperson (max 5, Unterhaltsgrundbetrag); (3) 30 % des
|
||||
Mehrbetrags + 10 % je Unterhaltsperson (max 5); der Teil über dem
|
||||
Vierfachen des AZR (Höchstberechnungsgrundlage) ist jedenfalls
|
||||
pfändbar; (5) Grundbeträge auf volle Euro abrunden, Höchstteil nach
|
||||
§ 291 Abs 2 (20-€-Schritte).
|
||||
- **§ 292 EO** (Zusammenrechnung/Sachleistungen) —
|
||||
`.additional_docs/Lexis360_292_eo.pdf` (NOR40187912): Abs 4 absolutes
|
||||
Geldexistenzminimum = halber allgemeiner Grundbetrag.
|
||||
- **AZR** (ASVG § 293 Abs 1 lit a sublit bb) am RIS belegt
|
||||
(KB-KONFLIKTE A9, 3. RIS-Runde 2026-09-11): 2025 1.273,99 € (BGBl II
|
||||
417/2024), 2026 1.308,39 € (BGBl II 263/2025).
|
||||
- **KB** lb-pfa-01…14 (Stand 2026-07), wk-pfa-01: Einlangen/Rang,
|
||||
Durchführung/§ 304-Zahlungssperre, Kostenersatz, Beendigungsansprüche
|
||||
(§ 291d EO), Vorschüsse (§ 290c EO), Sachbezüge.
|
||||
|
||||
Formel (§ 291a, gegen KB-Rechenbeispiel lb-pfa-03 verifiziert:
|
||||
Abfertigung 6×6.000 €, 6 % LSt → BG 33.840 €, 2 Unterhaltspflichten,
|
||||
HöBG 6×5.220 € → EX 16.684 € / pfändbar 17.156 € — exakt):
|
||||
|
||||
azr = Parameter at_eo_azr (versioniert)
|
||||
allg = floor(azr) [allg. Grundbetrag]
|
||||
erh = floor(azr * 7/6) [erhöhter allg.]
|
||||
unterhalt = floor(azr * 0.20) [je Person, max 5]
|
||||
grund = (erh|allg) + n * unterhalt
|
||||
hoechst_j = floor_to_20(4 * azr) [HöBG/Jahr]
|
||||
hoechst = hoechst_j * bezugsmonate
|
||||
mehr = max(0, min(basis, hoechst) - grund)
|
||||
ex = min(basis, grund + (0.30 + 0.10*n) * mehr)
|
||||
pfaendbar = basis - ex
|
||||
"""
|
||||
import math
|
||||
from datetime import timedelta
|
||||
|
||||
from odoo import api, fields, models
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
def _floor_to(x, step):
|
||||
"""Abbruch auf das Vielfache von ``step`` (20-€-Schritte § 291 Abs 2)."""
|
||||
return math.floor(round(x, 6) / step) * step
|
||||
|
||||
|
||||
class L10nAtPayrollPfaendung(models.Model):
|
||||
_name = 'l10n.at.payroll.pfaendung'
|
||||
_description = 'Lohnpfändung / Exekution (Drittschuldner)'
|
||||
_order = 'zustellungsdatum, id'
|
||||
_rec_name = 'glaubiger'
|
||||
|
||||
active = fields.Boolean(default=True)
|
||||
employee_id = fields.Many2one(
|
||||
'hr.employee', string='Dienstnehmer:in', required=True, index=True)
|
||||
company_id = fields.Many2one(
|
||||
'res.company', string='Company',
|
||||
related='employee_id.company_id', readonly=True, required=True)
|
||||
currency_id = fields.Many2one(
|
||||
'res.currency', related='company_id.currency_id', readonly=True)
|
||||
glaubiger = fields.Char(
|
||||
string='Gläubiger', required=True,
|
||||
help='Betreibende Partei (Name des Gläubigers bzw. der '
|
||||
'Vollstreckungsbehörde).')
|
||||
vollstreckung_traeger = fields.Selection(
|
||||
[('gericht', 'Gericht (Exekutionsbewilligung)'),
|
||||
('finanz', 'Finanzamt (Abgabenexekution)'),
|
||||
('sv', 'Sozialversicherungsträger'),
|
||||
('verwaltung', 'Verwaltungsbehörde'),
|
||||
('privat', 'Privatrechtlich (Verpfändung/Zession)')],
|
||||
string='Vollstreckungsträger', default='gericht', required=True,
|
||||
help='Steuert Pflichtenmatrix (Drittschuldnererklärung, '
|
||||
'Wartefrist) — lb-pfa-04/06/07.')
|
||||
pfandung_art = fields.Selection(
|
||||
[('gewoehnlich', 'Gewöhnliche Exekution'),
|
||||
('unterhalt', 'Unterhaltsexekution'),
|
||||
('verpfandung', 'Vertragliche Verpfändung'),
|
||||
('zession', 'Gehaltsabtretung (Zession)')],
|
||||
string='Pfändungsart', default='gewoehnlich', required=True,
|
||||
help='Unterhalts- vs. gewöhnliche Pfändung steuert Tabellenwahl '
|
||||
'und Unterhaltsmasse (lb-pfa-04/05).')
|
||||
zustellungsdatum = fields.Date(
|
||||
string='Zustellungs-/Einlangedatum', required=True, index=True,
|
||||
help='Einlangen der Pfändung beim Drittschuldner; bestimmt Rang '
|
||||
'und Lauf der Fristen (lb-pfa-01/07).')
|
||||
forderungshoehe = fields.Monetary(
|
||||
string='Forderungshöhe', currency_field='currency_id',
|
||||
help='Höhe der gepfändeten Forderung (Ranganmerkung).')
|
||||
status = fields.Selection(
|
||||
[('aktiv', 'Aktiv'),
|
||||
('gesetzt', 'Gesetzt (ausgesetzt)'),
|
||||
('geloest', 'Gelöst / vollstreckt')],
|
||||
string='Status', default='aktiv', required=True,
|
||||
help='Aktiv: Einbehalt läuft; Gelöst: keine weiteren Einbehalte '
|
||||
'(lb-pfa-08 Pfändungsende).')
|
||||
loesungsdatum = fields.Date(
|
||||
string='Löse-/Enddatum',
|
||||
help='Datum der Aufhebung/Vollstreckung (lb-pfa-08).')
|
||||
verwalter = fields.Boolean(
|
||||
string='Verwalter bestellt (§ 301 Abs 1 Z 6 EO)',
|
||||
help='Bestellter Verwalter: Erklärung/Überweisung an ihn; '
|
||||
'Berechnung des Freibetrags anregbar (lb-pfa-01/04).')
|
||||
verwalter_name = fields.Char(
|
||||
string='Verwalter (Name)',
|
||||
help='Name des bestellten Verwalters.')
|
||||
zahlungssperre = fields.Boolean(
|
||||
string='4-Wochen-Zahlungssperre (§ 304 EO)',
|
||||
help='Exekutionsbewilligung ordnet Wartefrist an: Einbehalt '
|
||||
'gesperrt, Auszahlung an Gläubiger frühestens 4 Wochen ab '
|
||||
'Zustellung (lb-pfa-07).')
|
||||
kostenersatz_variante = fields.Selection(
|
||||
[('einbehalt', 'Kosteneinbehalt vom Bezug'),
|
||||
('verzicht', 'Verzicht (kein Kostenersatz)'),
|
||||
('ueberweisung', 'Überweisung durch den Gläubiger')],
|
||||
string='Kostenersatz-Variante', default='einbehalt',
|
||||
help='Geltendmachung des pauschalen Kostenersatzes (§ 292h/§ 302 '
|
||||
'EO) — lb-pfa-09.')
|
||||
dse_frist = fields.Date(
|
||||
string='Drittschuldnererklärung-Frist', compute='_compute_fristen',
|
||||
store=True, readonly=False,
|
||||
help='IdR 4 Wochen ab Zustellung (gerichtliche Exekution, § 301 '
|
||||
'EO) — lb-pfa-06.')
|
||||
auszahlung_frist = fields.Date(
|
||||
string='Spätester Auszahlungstermin', compute='_compute_fristen',
|
||||
store=True, readonly=False,
|
||||
help='Nächster Bezugstermin, spätestens 8 Wochen nach Einlangen '
|
||||
'(lb-pfa-07).')
|
||||
rang = fields.Integer(
|
||||
string='Rang', compute='_compute_rang', store=True, readonly=True,
|
||||
help='Rangfolge nach Einlangedatum (lb-pfa-01/10).')
|
||||
note = fields.Text(string='Bemerkung')
|
||||
|
||||
@api.constrains('loesungsdatum', 'zustellungsdatum')
|
||||
def _check_loesungsdatum(self):
|
||||
for rec in self:
|
||||
if rec.loesungsdatum and rec.zustellungsdatum \
|
||||
and rec.loesungsdatum < rec.zustellungsdatum:
|
||||
raise UserError(_(
|
||||
"Das Löse-/Enddatum (%(loes)s) darf nicht vor dem "
|
||||
"Zustellungsdatum (%(zust)s) liegen.",
|
||||
loes=rec.loesungsdatum,
|
||||
zust=rec.zustellungsdatum))
|
||||
|
||||
@api.depends('zustellungsdatum')
|
||||
def _compute_fristen(self):
|
||||
for rec in self:
|
||||
if rec.zustellungsdatum:
|
||||
rec.dse_frist = rec.zustellungsdatum + timedelta(weeks=4)
|
||||
rec.auszahlung_frist = rec.zustellungsdatum + timedelta(
|
||||
weeks=8)
|
||||
else:
|
||||
rec.dse_frist = None
|
||||
rec.auszahlung_frist = None
|
||||
|
||||
@api.depends('employee_id', 'zustellungsdatum', 'active')
|
||||
def _compute_rang(self):
|
||||
for rec in self:
|
||||
if not (rec.employee_id and rec.zustellungsdatum):
|
||||
rec.rang = 0
|
||||
continue
|
||||
voher = self.search_count([
|
||||
('employee_id', '=', rec.employee_id.id),
|
||||
('active', '=', True),
|
||||
('zustellungsdatum', '<', rec.zustellungsdatum),
|
||||
('id', '!=', rec.id),
|
||||
])
|
||||
rec.rang = voher + 1
|
||||
|
||||
def name_get(self):
|
||||
return [
|
||||
(rec.id, '%s — %s (%s)' % (
|
||||
rec.glaubiger, rec.employee_id.name or '',
|
||||
rec.zustellungsdatum or ''))
|
||||
for rec in self]
|
||||
|
||||
def action_geloest(self):
|
||||
"""Pfändung als gelöst/vollstreckt markieren (lb-pfa-08)."""
|
||||
today = fields.Date.context_today(self)
|
||||
for rec in self:
|
||||
rec.write({'status': 'geloest',
|
||||
'loesungsdatum': rec.loesungsdatum or today})
|
||||
return True
|
||||
|
||||
def action_aktiv(self):
|
||||
for rec in self:
|
||||
rec.write({'status': 'aktiv'})
|
||||
return True
|
||||
|
||||
def action_gesetzt(self):
|
||||
for rec in self:
|
||||
rec.write({'status': 'gesetzt'})
|
||||
return True
|
||||
|
||||
|
||||
class HrVersion(models.Model):
|
||||
_inherit = 'hr.version'
|
||||
|
||||
l10n_at_unterhaltspflichten = fields.Integer(
|
||||
string='AT gesetzliche Unterhaltspflichten', default=0,
|
||||
groups="hr_payroll.group_hr_payroll_user", tracking=True,
|
||||
help='Anzahl gesetzlicher Unterhaltspflichten (max 5 wirksam, '
|
||||
'§ 291a Abs 2 Z 2 EO) — wirkt auf Grund- und '
|
||||
'Steigerungsbeträge (lb-pfa-11, Vereinfachungsregel '
|
||||
'§ 292f Abs 2 EO: Angaben des DN genügen).')
|
||||
l10n_at_sz_anspruch = fields.Boolean(
|
||||
string='AT Sonderzahlungsanspruch (13./14.)', default=True,
|
||||
groups="hr_payroll.group_hr_payroll_user", tracking=True,
|
||||
help='Besteht ein Anspruch auf 13./14. Sonderbezug? Ohne SZ-'
|
||||
'Anspruch gilt der erhöhte allgemeine Grundbetrag '
|
||||
'(§ 291a Abs 2 Z 1 EO, Faktor 7/6) — lb-pfa-05.')
|
||||
|
||||
|
||||
class HrEmployee(models.Model):
|
||||
_inherit = 'hr.employee'
|
||||
|
||||
l10n_at_unterhaltspflichten = fields.Integer(
|
||||
related='version_id.l10n_at_unterhaltspflichten', inherited=True,
|
||||
readonly=False, groups="hr_payroll.group_hr_payroll_user")
|
||||
l10n_at_sz_anspruch = fields.Boolean(
|
||||
related='version_id.l10n_at_sz_anspruch', inherited=True,
|
||||
readonly=False, default=True,
|
||||
groups="hr_payroll.group_hr_payroll_user")
|
||||
|
||||
def _at_pfaendungen_aktiv(self, date_to):
|
||||
"""Aktive Pfändungen zum Stichtag (sortiert nach Einlangedatum)."""
|
||||
self.ensure_one()
|
||||
return self.env['l10n.at.payroll.pfaendung'].search([
|
||||
('employee_id', '=', self.id),
|
||||
('active', '=', True),
|
||||
('status', '=', 'aktiv'),
|
||||
('zustellungsdatum', '<=', date_to),
|
||||
], order='zustellungsdatum, id')
|
||||
|
||||
def _at_unterhaltspflichten(self):
|
||||
"""Wirksame gesetzliche Unterhaltspflichten (max 5, § 291a
|
||||
Abs 2 Z 2) — Vereinfachungsregel § 292f Abs 2 EO (lb-pfa-11)."""
|
||||
self.ensure_one()
|
||||
return min(self.l10n_at_unterhaltspflichten or 0, 5)
|
||||
|
||||
def _at_sz_anspruch(self):
|
||||
"""Sonderzahlungsanspruch (13./14.) — bestimmt allg. vs. erhöhten
|
||||
Grundbetrag (§ 291a Abs 2 Z 1: erhöht nur bei KEINEN §-290b-
|
||||
Leistungen)."""
|
||||
self.ensure_one()
|
||||
return bool(self.l10n_at_sz_anspruch)
|
||||
|
||||
|
||||
class HrSalaryRule(models.Model):
|
||||
_inherit = 'hr.salary.rule'
|
||||
|
||||
l10n_at_pfandung_unpfandbar = fields.Boolean(
|
||||
string='AT unpfändbarer Bezug (§ 291 EO)',
|
||||
help='Bezugsart, die nicht pfändbar ist und aus der '
|
||||
'Pfändungsberechnungsgrundlage ausfällt (zB '
|
||||
'Reisekostenentschädigung) — lb-pfa-05. Schnittstelle '
|
||||
'AP12 Sachbezüge.')
|
||||
l10n_at_pfandung_beendigung = fields.Boolean(
|
||||
string='AT Beendigungsanspruch (§ 291d EO)',
|
||||
help='Einmalige Beendigungsleistung (Abfertigung, '
|
||||
'Urlaubsersatzleistung, Abgangsentschädigung, '
|
||||
'Pensionsabfindung) — eigener Pfändungsberechnungskreis '
|
||||
'(lb-pfa-03). Schnittstelle AP14/15.')
|
||||
|
||||
|
||||
class HrPayslip(models.Model):
|
||||
_inherit = 'hr.payslip'
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Werte (versioniert, Parameter at_eo_azr)
|
||||
# ------------------------------------------------------------------
|
||||
def _at_pfaendung_werte(self, datum=None):
|
||||
"""AZR + abgeleitete Grund-/Höchstbeträge (§ 291a EO) zum
|
||||
Stichtag — aus dem versionierten Parameter ``at_eo_azr``."""
|
||||
self.ensure_one()
|
||||
datum = datum or self.date_to
|
||||
werte = self._l10n_at_param('at_eo_azr', datum)
|
||||
azr = float(werte['azr'])
|
||||
return {
|
||||
'azr': azr,
|
||||
'allg': math.floor(azr),
|
||||
'erh': math.floor(azr * 7.0 / 6.0),
|
||||
'unterhalt': math.floor(azr * 0.20),
|
||||
'hoechst_j': _floor_to(4.0 * azr, 20),
|
||||
'abs_minimum': math.floor(azr / 2.0),
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Existenzminimum (§ 291a EO)
|
||||
# ------------------------------------------------------------------
|
||||
def _at_pfaendung_existenzminimum(self, basis, unterhaltspflichten,
|
||||
erhoht, bezugsmonate=1):
|
||||
"""Unpfändbarer Freibetrag (Existenzminimum) nach § 291a EO.
|
||||
|
||||
:param basis: Pfändungsberechnungsgrundlage (Netto, > 0)
|
||||
:param unterhaltspflichten: n, gesetzliche Unterhaltspflichten
|
||||
:param erhoht: erhöhter allgemeiner Grundbetrag (kein §-290b /
|
||||
Beendigungsleistungen)
|
||||
:param bezugsmonate: Vervielfachung der HöBG (Beendigungs-
|
||||
ansprüche, lb-pfa-03)
|
||||
"""
|
||||
self.ensure_one()
|
||||
w = self._at_pfaendung_werte()
|
||||
n = min(unterhaltspflichten or 0, 5)
|
||||
bezugsmonate = max(1, int(bezugsmonate or 1))
|
||||
grund = (w['erh'] if erhoht else w['allg']) + n * w['unterhalt']
|
||||
hoechst = w['hoechst_j'] * bezugsmonate
|
||||
basis = max(0.0, basis)
|
||||
if basis <= grund:
|
||||
return basis
|
||||
mehr = min(basis, hoechst) - grund
|
||||
ex = grund + (0.30 + 0.10 * n) * mehr
|
||||
return min(basis, round(ex, 2))
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Pfändungsberechnungsgrundlage (§ 291 EO) je Topf
|
||||
# ------------------------------------------------------------------
|
||||
def _at_pfa_rule_amount(self, result_rules, code):
|
||||
if result_rules is not None:
|
||||
werte = result_rules.get(code)
|
||||
# result_rules enthält ungerundete Rule-Summen; die
|
||||
# Bezugsart ist der gerundete Zeilenbetrag (Cents) —
|
||||
# mit diesem wird auch die Pfändungsberechnungsgrundlage
|
||||
# (§ 291 EO) bestimmt.
|
||||
return round(float(werte['total']), 2) if werte else 0.0
|
||||
linie = self.line_ids.filtered(lambda l: l.code == code)
|
||||
return sum(linie.mapped('total')) if linie else 0.0
|
||||
|
||||
def _at_pfa_bruto(self, result_rules, unpfandbar=None):
|
||||
"""Summe laufender Brutto-Bezüge (Bemessung 'laufend'); optional
|
||||
nur die unpfändbaren (Flag l10n_at_pfandung_unpfandbar)."""
|
||||
self.ensure_one()
|
||||
total = 0.0
|
||||
for rule in self.struct_id.rule_ids:
|
||||
if (rule.category_id.code != 'ATP_BRUTTO'
|
||||
or rule.l10n_at_bemessung != 'laufend'):
|
||||
continue
|
||||
if (unpfandbar is not None
|
||||
and bool(rule.l10n_at_pfandung_unpfandbar) != unpfandbar):
|
||||
continue
|
||||
if result_rules is not None:
|
||||
werte = result_rules.get(rule.code)
|
||||
if werte:
|
||||
total += round(float(werte['total']), 2)
|
||||
else:
|
||||
linie = self.line_ids.filtered(
|
||||
lambda l, c=rule.code: l.code == c)
|
||||
total += sum(linie.mapped('total'))
|
||||
return total
|
||||
|
||||
def _at_pfaendung_basis(self, result_rules, topf='laufend'):
|
||||
"""Pfändungsberechnungsgrundlage (§ 291 EO) je Topf.
|
||||
|
||||
- ``laufend``: laufende Bezüge − unpfändbar − SV-DN(lfd) − LSt(lfd)
|
||||
- ``sz13``/``sz14``: 13./14. Bezug (eigener Berechnungskreis)
|
||||
- ``beendigung``: einmalige Beendigungsansprüche (§ 291d EO)
|
||||
"""
|
||||
self.ensure_one()
|
||||
if topf == 'laufend':
|
||||
bruto = self._at_pfa_bruto(result_rules)
|
||||
unpf = self._at_pfa_bruto(result_rules, unpfandbar=True)
|
||||
svdn = sum(self._at_pfa_rule_amount(
|
||||
result_rules, c) for c in (
|
||||
'SVDN_KV', 'SVDN_PV', 'SVDN_AV', 'SVDN_AK', 'SVDN_WF'))
|
||||
lst = self._at_pfa_rule_amount(result_rules, 'LST_LFD')
|
||||
return round(max(0.0, bruto - unpf - svdn - lst), 2)
|
||||
if topf in ('sz13', 'sz14'):
|
||||
code = {'sz13': 'B13', 'sz14': 'B14'}[topf]
|
||||
return round(max(0.0, self._at_pfa_rule_amount(result_rules, code)), 2)
|
||||
if topf == 'beendigung':
|
||||
total = 0.0
|
||||
for rule in self.struct_id.rule_ids:
|
||||
if not rule.l10n_at_pfandung_beendigung:
|
||||
continue
|
||||
if result_rules is not None:
|
||||
werte = result_rules.get(rule.code)
|
||||
if werte:
|
||||
total += float(werte['total'])
|
||||
else:
|
||||
linie = self.line_ids.filtered(
|
||||
lambda l, c=rule.code: l.code == c)
|
||||
total += sum(linie.mapped('total'))
|
||||
return round(max(0.0, total), 2)
|
||||
raise UserError('Unbekannter Pfändungstopf: %s' % topf)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Pfändbare Masse je Topf (Basis − Existenzminimum)
|
||||
# ------------------------------------------------------------------
|
||||
def _at_pfaendung_masse(self, result_rules, topf):
|
||||
"""Pfändbarer Betrag (= Berechnungsgrundlage − Existenzminimum)
|
||||
je Topf. SZ-Töpfe: allgemeiner Grundbetrag (SZ = §-290b).
|
||||
Beendigungsansprüche: erhöhter Grundbetrag (lb-pfa-03)."""
|
||||
self.ensure_one()
|
||||
basis = self._at_pfaendung_basis(result_rules, topf)
|
||||
if basis <= 0:
|
||||
return 0.0
|
||||
n = self.employee_id._at_unterhaltspflichten()
|
||||
if topf in ('sz13', 'sz14'):
|
||||
erhoht = False
|
||||
elif topf == 'beendigung':
|
||||
erhoht = True
|
||||
else:
|
||||
erhoht = not self.employee_id._at_sz_anspruch()
|
||||
ex = self._at_pfaendung_existenzminimum(basis, n, erhoht)
|
||||
return max(0.0, round(basis - ex, 2))
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Gesamt-Einbehalt (laufend + SZ) und Kostenersatz (§ 292h/§ 302)
|
||||
# ------------------------------------------------------------------
|
||||
def _at_pfaendung_einbehalt(self, result_rules):
|
||||
"""Gesamter einbehaltener pfändbarer Betrag (laufend + SZ-Töpfe)."""
|
||||
self.ensure_one()
|
||||
if not self.employee_id._at_pfaendungen_aktiv(self.date_to):
|
||||
return 0.0
|
||||
total = self._at_pfaendung_masse(result_rules, 'laufend')
|
||||
total += self._at_pfaendung_masse(result_rules, 'sz13')
|
||||
total += self._at_pfaendung_masse(result_rules, 'sz14')
|
||||
return round(total, 2)
|
||||
|
||||
def _at_pfaendung_kostenersatz(self, result_rules):
|
||||
"""Pauschaler Kostenersatz (§ 292h EO): erste Zahlung 2 %/max 8 €,
|
||||
weitere 1 %/max 4 € — nur vom pfändbaren Bezug, nie vom EX
|
||||
(lb-pfa-09). Variante 'verzicht' → 0."""
|
||||
self.ensure_one()
|
||||
pfaendungen = self.employee_id._at_pfaendungen_aktiv(self.date_to)
|
||||
if not pfaendungen:
|
||||
return 0.0
|
||||
if all(p.kostenersatz_variante == 'verzicht' for p in pfaendungen):
|
||||
return 0.0
|
||||
masse = self._at_pfaendung_einbehalt(result_rules)
|
||||
if masse <= 0:
|
||||
return 0.0
|
||||
p = self._l10n_at_param('at_eo_kostenersatz')
|
||||
erste_max = float(p['erste_max'])
|
||||
weitere_max = float(p['weitere_max'])
|
||||
# Erste Zahlung (erstrangig) 2 %/max 8 €, weitere 1 %/max 4 €.
|
||||
kosten = min(masse * float(p['erste_rate']) / 100.0, erste_max)
|
||||
if masse > kosten:
|
||||
kosten += min((masse - kosten) * float(p['weitere_rate'])
|
||||
/ 100.0, weitere_max)
|
||||
return round(min(kosten, masse), 2)
|
||||
@@ -0,0 +1,57 @@
|
||||
# Part of the odoo-at-payroll project. License: LGPL-3.
|
||||
"""AP11: Drittschuldnererklärung (DSE) — QWeb-Datenlieferant.
|
||||
|
||||
Die DSE informiert Gläubiger/Verwalter über die Bezügeansprüche des
|
||||
Schuldners (lb-pfa-06): abzugeben idR binnen 4 Wochen ab Zustellung
|
||||
(§ 301 EO), auch bei Null-Pfändung / Ex-Mitarbeiter. Die pfändungs-
|
||||
rechtlichen Beträge (Berechnungsgrundlage, Existenzminimum, pfändbarer
|
||||
Betrag) stammen aus dem AP11-Engine am neuesten validierten Lauf
|
||||
(models/pfaendung.py)."""
|
||||
from odoo import models
|
||||
|
||||
|
||||
class L10nAtPayrollPfaendungDseReport(models.AbstractModel):
|
||||
_name = 'report.l10n_at_hr_payroll_private.report_pfaendung_dse'
|
||||
_description = 'Drittschuldnererklärung (Lohnpfändung)'
|
||||
|
||||
def _get_report_values(self, docids, data=None):
|
||||
docs = self.env['hr.employee'].browse(docids)
|
||||
pfa = self.env['l10n.at.payroll.pfaendung']
|
||||
return {
|
||||
'docs': docs,
|
||||
'werte': {
|
||||
employee.id: self._at_dse_werte(employee, pfa)
|
||||
for employee in docs
|
||||
},
|
||||
}
|
||||
|
||||
def _at_dse_werte(self, employee, pfa):
|
||||
datum = self.env['hr.payslip'].search(
|
||||
[('employee_id', '=', employee.id),
|
||||
('state', 'not in', ('draft', 'cancel'))],
|
||||
order='date_to desc, id desc', limit=1)
|
||||
if datum:
|
||||
pfandungen = employee._at_pfaendungen_aktiv(datum.date_to)
|
||||
else:
|
||||
pfandungen = pfa.search([
|
||||
('employee_id', '=', employee.id),
|
||||
('active', '=', True),
|
||||
('status', 'in', ('aktiv', 'gesetzt'))],
|
||||
order='zustellungsdatum, id')
|
||||
ergebnis = {
|
||||
'lauf_basis': None,
|
||||
'lauf_ex': None,
|
||||
'lauf_pfaendbar': None,
|
||||
}
|
||||
if datum:
|
||||
basis = datum._at_pfaendung_basis(None, 'laufend')
|
||||
ex = datum._at_pfaendung_existenzminimum(
|
||||
basis,
|
||||
employee._at_unterhaltspflichten(),
|
||||
not employee._at_sz_anspruch())
|
||||
ergebnis.update({
|
||||
'lauf_basis': basis,
|
||||
'lauf_ex': ex,
|
||||
'lauf_pfaendbar': round(max(0.0, basis - ex), 2),
|
||||
})
|
||||
return {'pfandungen': pfandungen, **ergebnis}
|
||||
@@ -0,0 +1,128 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<!-- ============================================================ -->
|
||||
<!-- AP11: Drittschuldnererklärung (DSE, lb-pfa-06). An das -->
|
||||
<!-- Exekutionsgericht und den betreibenden Gläubiger bzw. bei -->
|
||||
<!-- Verwalterbestellung an diesen; idR binnen 4 Wochen ab -->
|
||||
<!-- Zustellung (§ 301 EO). Auch bei Null-Pfändung / Ex- -->
|
||||
<!-- Mitarbeiter abzugeben. -->
|
||||
<!-- ============================================================ -->
|
||||
<record id="action_report_pfaendung_dse" model="ir.actions.report">
|
||||
<field name="name">Drittschuldnererklärung (Lohnpfändung)</field>
|
||||
<field name="model">hr.employee</field>
|
||||
<field name="report_type">qweb-pdf</field>
|
||||
<field name="report_name">l10n_at_hr_payroll_private.report_pfaendung_dse</field>
|
||||
<field name="report_file">l10n_at_hr_payroll_private.report_pfaendung_dse</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_pfaendung_dse">
|
||||
<t t-call="web.html_container">
|
||||
<t t-foreach="docs" t-as="employee">
|
||||
<t t-set="w" t-value="werte[employee.id]"/>
|
||||
<t t-call="web.external_layout">
|
||||
<div class="page">
|
||||
<h2>Drittschuldnererklärung — Arbeitsverhältnis</h2>
|
||||
<p class="text-muted">
|
||||
Erklärung des Dienstgebers als Drittschuldner
|
||||
über die Bezügeansprüche des Verpflichteten
|
||||
(§ 301 EO; lb-pfa-06).
|
||||
</p>
|
||||
<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>Unterhaltspflichten</strong></td>
|
||||
<td><t t-out="employee.l10n_at_unterhaltspflichten or 0"/> (max 5 wirksam)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Sonderzahlungsanspruch (13./14.)</strong></td>
|
||||
<td><t t-if="employee.l10n_at_sz_anspruch">ja</t><t t-else="">nein (erhöhter Grundbetrag)</t></td>
|
||||
<td><strong>Beschäftigt beim Drittschuldner</strong></td>
|
||||
<td><t t-if="w['lauf_basis'] is not None">ja (letzter validierter Lauf vorhanden)</t><t t-else="">— kein validierter Lauf</t></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h3>Pfändungen / Exekutionen (nach Rang)</h3>
|
||||
<table class="table table-sm o_main_table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Rang</th>
|
||||
<th>Gläubiger</th>
|
||||
<th>Art</th>
|
||||
<th>Zustellung</th>
|
||||
<th class="text-end">Forderung</th>
|
||||
<th>Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr t-foreach="w['pfandungen']" t-as="p">
|
||||
<td><t t-out="p.rang"/></td>
|
||||
<td><t t-out="p.glaubiger"/></td>
|
||||
<td><t t-out="p.pfandung_art"/></td>
|
||||
<td><t t-out="p.zustellungsdatum"/></td>
|
||||
<td class="text-end">
|
||||
<span t-out="p.forderungshoehe"
|
||||
t-options='{"widget": "monetary", "display_currency": employee.currency_id}'/>
|
||||
</td>
|
||||
<td><t t-out="p.status"/></td>
|
||||
</tr>
|
||||
<tr t-if="not w['pfandungen']">
|
||||
<td colspan="6" class="text-muted">
|
||||
Keine aktive Pfändungsvormerkung erfasst.
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h3>Pfändbarer Betrag (laufender Bezug, § 291a EO)</h3>
|
||||
<table class="table table-sm o_main_table">
|
||||
<tbody>
|
||||
<tr t-if="w['lauf_basis'] is not None">
|
||||
<td>Berechnungsgrundlage (Netto nach SV-DN/LSt)</td>
|
||||
<td class="text-end">
|
||||
<span t-out="w['lauf_basis']"
|
||||
t-options='{"widget": "monetary", "display_currency": employee.currency_id}'/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr t-if="w['lauf_basis'] is not None">
|
||||
<td>Existenzminimum (unpfändbar)</td>
|
||||
<td class="text-end">
|
||||
<span t-out="w['lauf_ex']"
|
||||
t-options='{"widget": "monetary", "display_currency": employee.currency_id}'/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr t-if="w['lauf_basis'] is not None">
|
||||
<td><strong>Pfändbarer Betrag</strong></td>
|
||||
<td class="text-end">
|
||||
<strong><span t-out="w['lauf_pfaendbar']"
|
||||
t-options='{"widget": "monetary", "display_currency": employee.currency_id}'/></strong>
|
||||
</td>
|
||||
</tr>
|
||||
<tr t-if="w['lauf_basis'] is None">
|
||||
<td colspan="2" class="text-muted">
|
||||
Kein validierter Lauf — Beträge nicht
|
||||
ermittelt (Null-Pfändung / Ex-Mitarbeiter:
|
||||
Erklärung dennoch abgeben, lb-pfa-06).
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p class="small text-muted">
|
||||
Hinweis: Die Erklärung ist auch dann zu erstatten,
|
||||
wenn der Verpflichtete nicht (mehr) beschäftigt ist
|
||||
oder nichts Pfändbares bezogen wird (lb-pfa-06).
|
||||
Kostenersatz für die Erklärung: § 302 EO (keine USt,
|
||||
VwGH 2011/15/0181).
|
||||
</p>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
</odoo>
|
||||
@@ -19,3 +19,5 @@ access_l10n_at_payroll_vr_user,L10n AT Payroll VR User,model_l10n_at_payroll_vr,
|
||||
access_l10n_at_payroll_vr_event_user,L10n AT Payroll VR-Event User,model_l10n_at_payroll_vr_event,hr_payroll.group_hr_payroll_user,1,1,1,1
|
||||
access_l10n_at_payroll_aeb_user,L10n AT Payroll AEB User,model_l10n_at_payroll_aeb,hr_payroll.group_hr_payroll_user,1,1,1,0
|
||||
access_l10n_at_payroll_aeb_manager,L10n AT Payroll AEB Manager,model_l10n_at_payroll_aeb,hr_payroll.group_hr_payroll_manager,1,1,1,1
|
||||
access_l10n_at_payroll_pfaendung_user,L10n AT Payroll Pfaendung User,model_l10n_at_payroll_pfaendung,hr_payroll.group_hr_payroll_user,1,1,1,0
|
||||
access_l10n_at_payroll_pfaendung_manager,L10n AT Payroll Pfaendung Manager,model_l10n_at_payroll_pfaendung,hr_payroll.group_hr_payroll_manager,1,1,1,1
|
||||
|
||||
|
@@ -30,4 +30,11 @@
|
||||
<field name="groups"
|
||||
eval="[(4, ref('hr_payroll.group_hr_payroll_user'))]"/>
|
||||
</record>
|
||||
<record id="rule_pfaendung_company" model="ir.rule">
|
||||
<field name="name">Lohnpfändung: eigene Company</field>
|
||||
<field name="model_id" ref="model_l10n_at_payroll_pfaendung"/>
|
||||
<field name="domain_force">[('company_id', 'in', company_ids)]</field>
|
||||
<field name="groups"
|
||||
eval="[(4, ref('hr_payroll.group_hr_payroll_user'))]"/>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -12,3 +12,4 @@ from . import test_einstufung_private
|
||||
from . import test_efzg_private
|
||||
from . import test_mutterschutz_private
|
||||
from . import test_aeb_private
|
||||
from . import test_pfaendung_private
|
||||
|
||||
@@ -0,0 +1,208 @@
|
||||
# Part of the odoo-at-payroll project. License: LGPL-3.
|
||||
"""AP11: Lohnpfändung — Existenzminimum-Maschine (§ 291a EO) je Topf,
|
||||
DN-Abzug (PFAENDUNG), Kostenersatz (§ 292h) und Rang.
|
||||
Rechenfälle: docs/rechenfaelle/pfaendung.md."""
|
||||
import datetime
|
||||
|
||||
from odoo.tests import TransactionCase, tagged
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class PfaendungPrivateTest(TransactionCase):
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.company = self.env.company
|
||||
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 PFA 40h',
|
||||
'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.company.resource_calendar_id = self.calendar
|
||||
|
||||
def _employee(self, eintritt, unterhalt=0, sz_anspruch=True):
|
||||
employee = self.env['hr.employee'].create({'name': 'PFA Test'})
|
||||
version = self.env['hr.version'].search(
|
||||
[('employee_id', '=', employee.id)], limit=1)
|
||||
version.write({
|
||||
'contract_date_start': eintritt,
|
||||
'kv_id': self.kv2203.id,
|
||||
'kv_gruppe_id': self.gruppe_ii.id,
|
||||
'erfahrungsstufe': 1,
|
||||
'resource_calendar_id': self.calendar.id,
|
||||
'l10n_at_unterhaltspflichten': unterhalt,
|
||||
'l10n_at_sz_anspruch': sz_anspruch,
|
||||
})
|
||||
version.generate_work_entries(
|
||||
datetime.date(2026, 1, 1), datetime.date(2026, 12, 31))
|
||||
return employee
|
||||
|
||||
def _slip(self, employee, monat, berechnen=False):
|
||||
erster = datetime.date(2026, monat, 1)
|
||||
letzter = (erster.replace(day=28)
|
||||
+ datetime.timedelta(days=4)).replace(day=1) \
|
||||
- datetime.timedelta(days=1)
|
||||
slip = self.env['hr.payslip'].create({
|
||||
'name': 'PFA %d/2026' % monat,
|
||||
'employee_id': employee.id,
|
||||
'struct_id': self.structure.id,
|
||||
'date_from': erster,
|
||||
'date_to': letzter,
|
||||
})
|
||||
if berechnen:
|
||||
slip.compute_sheet()
|
||||
slip.action_payslip_done()
|
||||
return slip
|
||||
|
||||
def _pfaendung(self, employee, zustellung, glaubiger='Gläubiger X',
|
||||
art='gewoehnlich'):
|
||||
return self.env['l10n.at.payroll.pfaendung'].create({
|
||||
'employee_id': employee.id,
|
||||
'glaubiger': glaubiger,
|
||||
'zustellungsdatum': zustellung,
|
||||
'pfandung_art': art,
|
||||
'status': 'aktiv',
|
||||
})
|
||||
|
||||
def _total(self, slip, code):
|
||||
return sum(slip.line_ids.filtered(
|
||||
lambda l: l.code == code).mapped('total'))
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Existenzminimum-Maschine (§ 291a EO) — Rechenfälle RF-PFA-1…4
|
||||
# ------------------------------------------------------------------
|
||||
def test_01_laufend_n0(self):
|
||||
"""RF-PFA-1: laufend, n=0, SZ-Anspruch, basis 2.000 € →
|
||||
EX 1.515,60 / pfändbar 484,40."""
|
||||
employee = self._employee(datetime.date(2026, 1, 1), unterhalt=0)
|
||||
slip = self._slip(employee, 3)
|
||||
ex = slip._at_pfaendung_existenzminimum(2000.0, 0, False)
|
||||
self.assertAlmostEqual(ex, 1515.60, places=2)
|
||||
|
||||
def test_02_laufend_n2(self):
|
||||
"""RF-PFA-1b: n=2, basis 2.000 € → EX 1.915,00 / pfändbar 85,00."""
|
||||
employee = self._employee(datetime.date(2026, 1, 1), unterhalt=2)
|
||||
slip = self._slip(employee, 3)
|
||||
ex = slip._at_pfaendung_existenzminimum(2000.0, 2, False)
|
||||
self.assertAlmostEqual(ex, 1915.00, places=2)
|
||||
|
||||
def test_03_hoechst_cap(self):
|
||||
"""RF-PFA-2: basis 6.000 € > HöBG 5.220 € → EX gedeckelt
|
||||
2.481,60 / pfändbar 3.518,40."""
|
||||
employee = self._employee(datetime.date(2026, 1, 1))
|
||||
slip = self._slip(employee, 3)
|
||||
ex = slip._at_pfaendung_existenzminimum(6000.0, 0, False)
|
||||
self.assertAlmostEqual(ex, 2481.60, places=2)
|
||||
|
||||
def test_04_beendigung_lb_pfa_03(self):
|
||||
"""RF-PFA-3 (lb-pfa-03, exakt): Abfertigung 6×6.000, 6 % LSt →
|
||||
basis 33.840, n=2, erhöht, 6 Bezugsmonate → EX 16.684 /
|
||||
pfändbar 17.156."""
|
||||
employee = self._employee(datetime.date(2026, 1, 1), unterhalt=2)
|
||||
slip = self._slip(employee, 3)
|
||||
ex = slip._at_pfaendung_existenzminimum(
|
||||
33840.0, 2, True, bezugsmonate=6)
|
||||
self.assertAlmostEqual(ex, 16684.00, places=2)
|
||||
self.assertAlmostEqual(33840.0 - ex, 17156.00, places=2)
|
||||
|
||||
def test_05_sz_anspruch_vs_erhoht(self):
|
||||
"""RF-PFA-4: ohne SZ-Anspruch (erhöht) höherer EX bei gleicher
|
||||
basis — EX 1.668,20 / pfändbar 331,80."""
|
||||
employee = self._employee(
|
||||
datetime.date(2026, 1, 1), sz_anspruch=False)
|
||||
slip = self._slip(employee, 3)
|
||||
ex = slip._at_pfaendung_existenzminimum(2000.0, 0, True)
|
||||
self.assertAlmostEqual(ex, 1668.20, places=2)
|
||||
self.assertAlmostEqual(2000.0 - ex, 331.80, places=2)
|
||||
|
||||
def test_06_abs_minimum(self):
|
||||
"""RF-PFA-5: absolutes Geldexistenzminimum 2026 = 654 €
|
||||
(½ × AZR 1.308,39, § 292 Abs 4 EO)."""
|
||||
employee = self._employee(datetime.date(2026, 1, 1))
|
||||
slip = self._slip(employee, 3)
|
||||
w = slip._at_pfaendung_werte()
|
||||
self.assertEqual(w['allg'], 1308)
|
||||
self.assertEqual(w['erh'], 1526)
|
||||
self.assertEqual(w['unterhalt'], 261)
|
||||
self.assertEqual(w['hoechst_j'], 5220)
|
||||
self.assertEqual(w['abs_minimum'], 654)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Kostenersatz (§ 292h EO) — RF-PFA-6
|
||||
# ------------------------------------------------------------------
|
||||
def test_07_kostenersatz(self):
|
||||
"""RF-PFA-6: Masse 1.000 € → erste 2 %/max 8 € + weitere
|
||||
1 %/max 4 € = 12,00 €; Variante 'verzicht' → 0."""
|
||||
employee = self._employee(datetime.date(2026, 1, 1))
|
||||
slip = self._slip(employee, 3)
|
||||
self._pfaendung(
|
||||
employee, datetime.date(2026, 2, 1))
|
||||
# Kostenersatz rechnerisch auf Masse 1.000 € (Parameter-Basis).
|
||||
p = slip._l10n_at_param('at_eo_kostenersatz')
|
||||
kosten = min(1000.0 * p['erste_rate'] / 100.0, p['erste_max'])
|
||||
kosten += min((1000.0 - kosten) * p['weitere_rate'] / 100.0,
|
||||
p['weitere_max'])
|
||||
self.assertAlmostEqual(kosten, 12.00, places=2)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# DN-Abzug im Lauf (PFAENDUNG-Regel) — RF-PFA-7
|
||||
# ------------------------------------------------------------------
|
||||
def test_08_pfaendung_regel_keine_pfandung(self):
|
||||
"""RF-PFA-7a: ohne aktive Pfändung keine PFAENDUNG-Zeile."""
|
||||
employee = self._employee(datetime.date(2026, 1, 1))
|
||||
slip = self._slip(employee, 5, berechnen=True)
|
||||
self.assertFalse(self._total(slip, 'PFAENDUNG'))
|
||||
|
||||
def test_09_pfaendung_regel_laufend(self):
|
||||
"""RF-PFA-7b: PFAENDUNG-Zeile = basis(laufend) − EX; Netto wird
|
||||
reduziert."""
|
||||
employee = self._employee(datetime.date(2026, 1, 1), unterhalt=0)
|
||||
self._pfaendung(
|
||||
employee, datetime.date(2026, 4, 1))
|
||||
slip = self._slip(employee, 5, berechnen=True)
|
||||
bruto = slip._at_pfa_bruto(None)
|
||||
svdn = sum(slip._at_pfa_rule_amount(
|
||||
None, c) for c in (
|
||||
'SVDN_KV', 'SVDN_PV', 'SVDN_AV', 'SVDN_AK', 'SVDN_WF'))
|
||||
lst = slip._at_pfa_rule_amount(None, 'LST_LFD')
|
||||
basis = max(0.0, bruto - svdn - lst)
|
||||
ex = slip._at_pfaendung_existenzminimum(
|
||||
basis, 0, not employee._at_sz_anspruch())
|
||||
pfaendbar = round(max(0.0, basis - ex), 2)
|
||||
self.assertGreater(basis, 0.0)
|
||||
self.assertGreater(pfaendbar, 0.0)
|
||||
self.assertAlmostEqual(self._total(slip, 'PFAENDUNG'),
|
||||
pfaendbar, places=2)
|
||||
|
||||
def test_10_rang(self):
|
||||
"""Zwei Pfändungen → Rang 1 (früheres Einlangen) und Rang 2."""
|
||||
employee = self._employee(datetime.date(2026, 1, 1))
|
||||
p1 = self._pfaendung(
|
||||
employee, datetime.date(2026, 3, 1), glaubiger='Früh')
|
||||
p2 = self._pfaendung(
|
||||
employee, datetime.date(2026, 4, 1), glaubiger='Spät')
|
||||
self.assertEqual(p1.rang, 1)
|
||||
self.assertEqual(p2.rang, 2)
|
||||
aktiv = employee._at_pfaendungen_aktiv(datetime.date(2026, 5, 31))
|
||||
self.assertEqual(list(aktiv.ids), [p1.id, p2.id])
|
||||
|
||||
def test_11_status_geloest(self):
|
||||
"""Gelöste Pfändung zählt nicht mehr als aktiv (lb-pfa-08)."""
|
||||
employee = self._employee(datetime.date(2026, 1, 1))
|
||||
p1 = self._pfaendung(
|
||||
employee, datetime.date(2026, 3, 1))
|
||||
p1.action_geloest()
|
||||
self.assertEqual(p1.status, 'geloest')
|
||||
self.assertFalse(
|
||||
employee._at_pfaendungen_aktiv(datetime.date(2026, 5, 31)))
|
||||
@@ -0,0 +1,126 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<!-- ============================================================ -->
|
||||
<!-- AP11: Lohnpfändung/Exekution — Vormerkung (Drittschuldner). -->
|
||||
<!-- ============================================================ -->
|
||||
|
||||
<record id="pfaendung_list_view" model="ir.ui.view">
|
||||
<field name="name">l10n.at.payroll.pfaendung.list</field>
|
||||
<field name="model">l10n.at.payroll.pfaendung</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="Lohnpfändungen">
|
||||
<field name="employee_id"/>
|
||||
<field name="glaubiger"/>
|
||||
<field name="pfandung_art" optional="show"/>
|
||||
<field name="zustellungsdatum"/>
|
||||
<field name="rang"/>
|
||||
<field name="forderungshoehe"
|
||||
widget="monetary"
|
||||
options="{'currency_field': 'currency_id'}"/>
|
||||
<field name="dse_frist" optional="hide"/>
|
||||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
<field name="status" widget="badge"
|
||||
decoration-success="status == 'aktiv'"
|
||||
decoration-info="status == 'gesetzt'"
|
||||
decoration-muted="status == 'geloest'"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="pfaendung_form_view" model="ir.ui.view">
|
||||
<field name="name">l10n.at.payroll.pfaendung.form</field>
|
||||
<field name="model">l10n.at.payroll.pfaendung</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Lohnpfändung / Exekution">
|
||||
<header>
|
||||
<button name="action_aktiv" type="object"
|
||||
string="Aktivieren"
|
||||
invisible="status == 'aktiv'"/>
|
||||
<button name="action_gesetzt" type="object"
|
||||
string="Setzen (aussetzen)"
|
||||
invisible="status == 'gesetzt'"/>
|
||||
<button name="action_geloest" type="object"
|
||||
string="Als gelöst markieren"
|
||||
class="btn-secondary"
|
||||
invisible="status == 'geloest'"/>
|
||||
<field name="status" widget="statusbar"
|
||||
statusbar_visible="aktiv,gesetzt,geloest"/>
|
||||
</header>
|
||||
<sheet>
|
||||
<group>
|
||||
<group string="Drittschuldner / Gläubiger">
|
||||
<field name="employee_id"/>
|
||||
<field name="glaubiger"/>
|
||||
<field name="vollstreckung_traeger"/>
|
||||
<field name="pfandung_art"/>
|
||||
<field name="forderungshoehe"/>
|
||||
<field name="rang" readonly="1"/>
|
||||
<field name="note" optional="show"/>
|
||||
</group>
|
||||
<group string="Fristen & Ablauf">
|
||||
<field name="zustellungsdatum"/>
|
||||
<field name="dse_frist"/>
|
||||
<field name="auszahlung_frist"/>
|
||||
<field name="zahlungssperre"/>
|
||||
<field name="loesungsdatum"
|
||||
invisible="status != 'geloest'"/>
|
||||
<field name="verwalter"/>
|
||||
<field name="verwalter_name"
|
||||
invisible="not verwalter"/>
|
||||
<field name="kostenersatz_variante"/>
|
||||
<field name="company_id"
|
||||
groups="base.group_multi_company"/>
|
||||
</group>
|
||||
</group>
|
||||
<group string="Bemerkung">
|
||||
<field name="note"
|
||||
nolabel="1"
|
||||
placeholder="Gläubiger-Adressat, Verwalter-Kontakt, Besonderheiten der Exekutionsbewilligung …"/>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_pfaendung" model="ir.actions.act_window">
|
||||
<field name="name">Lohnpfändungen / Exekutionen</field>
|
||||
<field name="res_model">l10n.at.payroll.pfaendung</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_smiling_face">
|
||||
Erste Lohnpfändung erfassen
|
||||
</p>
|
||||
<p>
|
||||
Pfändungsvormerkung (Drittschuldner) nach Einlangen der
|
||||
Exekutionsbewilligung: Gläubiger, Zustellungsdatum,
|
||||
Rang, Fristen (Drittschuldnererklärung § 301 EO,
|
||||
Zahlungssperre § 304 EO) und Kostenersatz (§ 292h/§ 302 EO).
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="menu_l10n_at_payroll_pfaendung"
|
||||
name="Lohnpfändung (Exekution)"
|
||||
parent="hr_work_entry_enterprise.menu_hr_payroll_root"
|
||||
action="action_pfaendung" sequence="40"/>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- Pfändungsrelevante Mitarbeiter-/Vertragsstammdaten -->
|
||||
<!-- (Unterhaltspflichten, SZ-Anspruch) am Payroll-Gruppenblock. -->
|
||||
<!-- ============================================================ -->
|
||||
<record id="payroll_hr_employee_view_form_pfaendung" model="ir.ui.view">
|
||||
<field name="name">hr.employee.form.pfaendung</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="pfaendung_stammdaten"
|
||||
string="Lohnpfändung (Stammdaten)"
|
||||
groups="hr_payroll.group_hr_payroll_user">
|
||||
<field name="l10n_at_unterhaltspflichten"/>
|
||||
<field name="l10n_at_sz_anspruch"/>
|
||||
</group>
|
||||
</group>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -0,0 +1,325 @@
|
||||
# Plan — Restlücken aus dem Wissensbasis-Abgleich (AP11 ff.)
|
||||
|
||||
**Status:** Freigegeben (2026-09-12, Nutzer). Fortsetzung der
|
||||
Arbeitspaket-Reihe AP0–AP10 (2026-09-12, GP1–GP9 final). **AP8 (AEB
|
||||
SART 70/75/76) läuft parallel in einer eigenen Session** und ist hier
|
||||
nur als Restabgleich geführt — keine Doppelumsetzung.
|
||||
|
||||
Ergänzt `IMPLEMENTIERUNGSPLAN-Privat.md` (M8 final) um die fachlichen
|
||||
Restlücken aus dem Abgleich **Wissensbasis (601 Einträge, 69 Cluster,
|
||||
`wissensbasis/INDEX.md`, Stand 2026-09-11) ↔ Modulstand**.
|
||||
|
||||
- **Stand:** 2026-09-12 · Branch `main`
|
||||
- **Module:** Kern `l10n_at_hr_payroll` 19.0.6.0.0 ·
|
||||
Privat `l10n_at_hr_payroll_private` 19.0.9.3.0 ·
|
||||
GemBG `l10n_at_gemeinde_payroll` 19.0.1.6.0
|
||||
- **Verbindlichkeits-Regime:** `RECHTSQUELLEN-Privat.md` verbindlich;
|
||||
KB-Einträge (Lexis/WIKU) Sekundärquelle mit ID + Stand. Werte
|
||||
niemals aus Trainingswissen — je Paket RIS-Beschaffung als
|
||||
**Baubeginn-Gate** (Muster `.firecrawl/ris/` + `_README.md`, inkl.
|
||||
Kurztitel-Fallen; lizenzierte Quellen `.lexis360/`/`.wiku/` bei
|
||||
Fehlen beim Nutzer anfordern, nie re-procuren).
|
||||
|
||||
## 1. Reevaluierung (2026-09-12)
|
||||
|
||||
Die Gap-Analyse von 2026-09-12 (Cluster-Abgleich INDEX.md gegen
|
||||
GP1–GP9) wurde gegen den aktuellen Code-Stand neu aufgezogen. Die
|
||||
AP0–AP10-Phase hat folgende Lücken geschlossen (148/148 Tests grün):
|
||||
|
||||
| Frühere Lücke | Umsetzung |
|
||||
|---|---|
|
||||
| Krankenstand-EFZ/Teilentgelt | **AP4**: `models/efzg.py` (Stufenkontingente je Arbeitsjahr, Krank-/AU-Topf getrennt), `TEIL_ENTGELT_HALB`/`KRANKENTGELT_PHASE`, fiktive BV § 7 Abs 3 BMSVG; EFZG GesNr 10008308 § 2 + AngG § 8 Abs 1/2/2a RIS-verifiziert |
|
||||
| Mutterschutz/Wochengeld | **AP7**: `models/mutterschutz.py`, `MSCH_ENTGELT` (Entgelt-Stopp), volle §-162-Abs-3-Rechnung, fiktive BV § 7 Abs 4; Rollenklärung: Wochengeld = KV-Leistung (ÖGK zahlt) |
|
||||
| „U-Bahn-Steuer" (Wr. DG-Abgabe) | **AP3**: `models/wien_dag.py`, Regel `WIEN_DAG` (WDGAG LrW GesNr 20000133: 2 €/Woche § 5, Befreiungen § 3, Fristen § 6/§ 31.3., FI 6663) |
|
||||
| mBGM-BSGR/ERGB/Audit-Befunde | **AP0**: TASY-Katalog-Modelle, Katalog vor `at_mbgm_tarif`, B044/B045- und B010/B030-Korrektur, ERGB real belegt, Dienstgebergruppe, NSchAB-Umbenennung |
|
||||
| Abgangsursachen (AGRD) | **AP1**: Selection D.22 (33 Codes) + Seed, hr-Defaults vorverbunden |
|
||||
| Abwesenheiten | **AP5**: hr_holidays Pflicht-Dependence, AT-Urlaubsarten, WETs `ATP_URLAUB/KRANK/KARENZ/AU/MSCH` |
|
||||
| Lohnkonto/GPLB-Nähe | **AP2a–d**: Jahreslohnkonto je DN + Betrieb, DG-Gesamtkosten-Report, Buchungsbeleg, Monatsjournal-DG-Block |
|
||||
| Vordienstzeiten/Einstufung | **AP9**: `vwgj_ab`/VWGJ-Stufen + Compute (Privat), BDA-Vorschlag (GemBG § 67) |
|
||||
| GemBG-Latenzfehler | **AP10**: Modul erstmals installierbar (9 Fixes) |
|
||||
|
||||
**Offen aus der Vorphase:** AP8 (AEB — parallel) und dokumentierte
|
||||
AP4/AP7-Reste: § 8 Abs 9 AngG Kalenderjahr-Umstellung, ÜStd-/
|
||||
Provisionsdurchschnitte im EFZ-Referenzzeitraum, SZ-Kürzung
|
||||
„kann"-Regel; Sonderwochengeld § 163 ASVG, Ruhen § 166 ASVG,
|
||||
Wochengeld-SZ-% (⚠ Satzungsverifikation offen), Karenz-Referenzmonat
|
||||
§ 7 Abs 4 Z 1–3 BMSVG.
|
||||
|
||||
## 2. Verbleibende Lücken (am Code verifiziert 2026-09-12)
|
||||
|
||||
Grep-Verifikation über `addons/` (py/xml): 0 Feature-Treffer je
|
||||
Thema; die Sachbezug/Pendler-Treffer sind Randnotizen (Umwidmungs-
|
||||
Kommentar SV-Kern; `verkehrsabsetzbetrag_pendler` in den Lohnsteuer-
|
||||
Absetzbeträgen — kein Pendlerpauschalen-Feature).
|
||||
|
||||
1. **Lohnpfändung/Exekution** (lb-pfa-01…14, wk-pfa-01)
|
||||
2. **Sachbezüge & freiwillige Sozialleistungen** (lb-sac-01…21,
|
||||
lb-ges-01/02, lb-fir-03…06)
|
||||
3. **Reisekosten & Pendlerförderung** (lb-rei-01…09, lb-pen-01…09,
|
||||
wk-rei-01) inkl. Telearbeitspauschale (C7-Gate)
|
||||
4. **Endabrechnungs-Zyklus**: Urlaubsersatzleistung (lb-url-09/15/16),
|
||||
Abgangsentschädigung/Vergleich/Prämien bei Beendigung
|
||||
(lb-end-04/05/06/08…22), Aufrechnung Vorschüsse/Darlehen
|
||||
(lb-lvr-01/02), Zeitschulden/Zeitguthaben (lb-end-09/10)
|
||||
5. **Abfertigung Alt**-Bezugszeile (lb-end-01/02, lb-kar-01)
|
||||
6. **Karenz-/Familien-Reste**: SV-An/-Abmeldung Karenz, Elternteilzeit
|
||||
(lb-elt-03), Bildungs-/Pflege-/Hospizkarenz (lb-kzs-01/02/06/07),
|
||||
Papamonat/Familienzeitbonus (lb-pap-01/02)
|
||||
7. **Altersteilzeit** (lb-atz-01…15) — Option, Scope-Gate
|
||||
8. **Kurzarbeit** (lb-azm-04/05/06) — Option, Scope-Gate
|
||||
9. **Mitarbeiterbeteiligung/Betriebspension/Zukunftssicherung**
|
||||
(lb-vor-01/04/05/07…11, wk-akt-12) — Option, Scope-Gate
|
||||
10. **Präsenzdienst-Fiktion** § 7 Abs 2 BMSVG (lb-prd-05/06)
|
||||
11. **Dienstverhinderungs-Sonderfälle** (lb-dvh-01…04: Epidemiegesetz,
|
||||
AG-seitige Verhinderung, Streik)
|
||||
12. **Entsendung/grenzüberschreitend** (lb-grz-01…08, lb-lsd-01/03,
|
||||
lb-lvr-03) — Option, Scope-Gate
|
||||
13. **Status-Sonderfälle**: GF/Vorstand (lb-gsf-02/03/05, lb-vst-03,
|
||||
wk-gsf-01), Ferialpraktikanten (lb-pra-01…04)
|
||||
14. **Feiertags-EFZ AngG** (lb-efz-01/02)
|
||||
15. **Meldewesen-Reste**: L17 + § 109a/§ 109b-Fixsätze (Fristen ✅),
|
||||
Schwerarbeitsmeldung § 74 ASVG + NSchG-Monatszähler
|
||||
(lb-swa-01…05, lb-nsc-01…03)
|
||||
16. **Lohnsteuer-Reste quer**: FB+/ANV-Automatik, Geringfügigen-
|
||||
Jahreszahlung (2,5 %), Fallweise-GFG je Beschäftigungstag,
|
||||
Prämien-Anspruchsprinzip-Aufrollung (A3-blockiert),
|
||||
2027-Werte (C7-Gate)
|
||||
|
||||
## 3. Konventionen (bindend je Paket)
|
||||
|
||||
- **Kern vs. Privat:** sektorneutral Engine-/FinOnl-Meldewesen →
|
||||
Kern `l10n_at_hr_payroll` (Muster L16); Produkt-Features auf
|
||||
Kern-Hooks → Privatmodul (Muster AP4/AP7). Kein VRV-Bezug in
|
||||
diesem Repo.
|
||||
- **Werte-Disziplin:** alle €/%/Grenzwerte aus RIS-Verifikation oder
|
||||
KB-Eintrag mit ID + Stand; `hr.rule.parameter` versioniert
|
||||
(date_from), keine Literale in Regelcode; Jahreswerte als
|
||||
Parameter (Muster `at_efz_stufen`).
|
||||
- **Abwesenheiten über WETs/Urlaubsarten** (hr_holidays-Infrastruktur
|
||||
AP5), nie über Parallel-Flags.
|
||||
- **Validation-Gates:** Rechenfälle-Doc `docs/rechenfaelle/<thema>.md`
|
||||
→ Fixture-Tests → frischer `-i`-Lauf aller 3 Module auf Wegwerf-DB
|
||||
→ py/XML-Sweep → Versionsbump → MEMORY/RUNBOOK-Nachzug.
|
||||
- **Security/Multi-Company:** `ir.model.access` je neuem Model +
|
||||
Record-Rules nach GP9-Muster; neue Models ohne Company-Bindung nur,
|
||||
wo fachlich global (Muster KV-Katalog).
|
||||
- **Session-Koordination:** AP8 läuft parallel — keine Schreib-
|
||||
Zugriffe auf das Privatmodul ohne Abstimmung; Paket-Reihenfolge so
|
||||
wählen, dass Dateikonflikte vermieden werden (AP11 nutzt eigene
|
||||
neue Dateien).
|
||||
|
||||
## 4. Arbeitspakete
|
||||
|
||||
### AP8 — AEB (Arbeits-/Entgeltbestätigungen) — **parallel in Umsetzung**
|
||||
|
||||
SART 70/75/76 (Kranken-/Wochengeld-Bestätigungen) auf dem ELDA-
|
||||
Framework (Kern `elda.py`), Daten aus `efzg.py`/`mutterschutz.py`.
|
||||
Kein Bestandteil dieses Bauplans außer Restabgleich (Datenlieferung
|
||||
EFZ/Wochengeld in `models/efzg.py`/`mutterschutz.py` bleibt AP8
|
||||
erhalten). Nach Fertigstellung: Abgleich gegen Plan-Annahmen +
|
||||
Test-Integration.
|
||||
|
||||
### AP11 — Lohnpfändung (12–18 PT)
|
||||
|
||||
- **Ziel:** Pfändung/Exekution als laufender DN-Abzug mit Existenz-
|
||||
minimum-Rechnung und Behörden-Workflow.
|
||||
- **Umsetzung (Privatmodul):** `models/pfaendung.py` —
|
||||
`l10n.at.payroll.pfaendung` (Gläubiger, Gericht/Beschluss, Zustel-
|
||||
lungsdatum, Töpfe LZP/13./14./Beendigungsleistungen, Status);
|
||||
Regel `PFAENDUNG` (DN-Abzug); Parameter `at_eo_grundbetrag` (AZR-
|
||||
Werte je Jahr, versioniert); § 304 EO-Zahlungssperre 4 Wochen ab
|
||||
Zustellung; Report **Drittschuldnererklärung**; Kostenersatz-Bear-
|
||||
beitungsgebühr (DN); Einbehalt ab nächster Abrechnung nach
|
||||
Zustellung.
|
||||
- **Rechtsanker:** EO §§ 291a/291b/292/293/304 **RIS-Gate vor Bau**;
|
||||
AZR-Werte ✅ (2026: allg. Grundbetrag 1.308 €, erhöht 1.526 €,
|
||||
Höchst-BG 5.220 €; Unterhaltsgrundbetrag 261 € × max 5 — A9, RIS
|
||||
11.09.2026); KB lb-pfa-01…14 (Stände 2026-07), wk-pfa-01.
|
||||
- **Tests:** Rechenfälle `pfaendung.md` — Existenzminimum je Topf,
|
||||
13./14.-Topf, Beendigungstopf (lb-pfa-03), Vorschüsse/Nachzahlungen
|
||||
(lb-pfa-12), Pfändungsende (lb-pfa-08), Sachbezugspfändung
|
||||
(lb-pfa-14 — Schnittstelle AP12).
|
||||
|
||||
### AP12 — Sachbezüge & freiwillige Sozialleistungen (14–20 PT)
|
||||
|
||||
- **Ziel:** Häufigste Sachbezugsarten abrechenbar mit korrekter SV-/
|
||||
LSt-Kategorisierung und Jahreskumulativen.
|
||||
- **Umsetzung (Privatmodul):** Sachbezugs-Arten als Input-Typen +
|
||||
Regeln `SB_*` (Kategorie AT sonstig/lfd je Steuer- und SV-Behand-
|
||||
lung); Parameter für Grenzwerte je Art (versioniert); Kern-Hook
|
||||
§-49-Abs-3-Jahreskumulative (Z 12/Z 31) ist vorhanden — anbinden;
|
||||
Dienstwagen-Privatnutzung (pauschale Bewertung + Kleinwohnungs-
|
||||
Sonderfall SBV § 2 Abs 7a), Mahlzeiten, Jobticket (Verzahnung
|
||||
AP13), Handy/digitale Arbeitsmittel, Parkplatz, Bonusmeilen,
|
||||
Betriebsveranstaltungen, Geschenke/Weihnachtsgeschenke.
|
||||
- **Rechtsanker:** B2 ✅ (SBV § 2 Abs 7a: 35/45 m², 35 % Abschlag);
|
||||
lb-sac-01…21 + lb-ges-01/02 je Art (Grenzwerte aus Quelle mit
|
||||
Stand, sonst RIS-Gate; lb-sac-18 Parkplatz ⚠ ohne §-Beleg — RIS
|
||||
klären); SV-/LSt-Systematik lb-sva-03/04.
|
||||
- **Tests:** Rechenfälle `sachbezuege.md` — je Art ein Eckfall,
|
||||
Jahreskumulative-Übergang, Dienstwagen + Kleinwohnung, Jobticket
|
||||
mit AP13-Kopplung.
|
||||
|
||||
### AP13 — Reisekosten & Pendlerförderung (10–16 PT)
|
||||
|
||||
- **Ziel:** Tag-/Nächtigungsgelder, Kilometergeld, Familienheimfahrten
|
||||
und Pendlerpauschale abrechenbar; SV-Beitragsfreiheit nach § 26
|
||||
EStG-Kopplung.
|
||||
- **Umsetzung (Privatmodul):** Input-Typen (Taggeld In-/Ausland,
|
||||
Kilometergeld, Nächtigung, Familienheimfahrt, Pendlerpauschale,
|
||||
Pendlereuro) + Regeln `REISE_*`/`PENDLERPAUSCH`; Parameter
|
||||
`at_taggeld_*`/`at_km_geld` (Tages-/KM-Sätze je Jahr, versioniert);
|
||||
SV-Freiheits-Flag an den Regeln (Kopplung § 26 EStG iVm § 49 ASVG);
|
||||
Telearbeitspauschale erst nach 2027-Verkündung (**C7-Gate**).
|
||||
- **Rechtsanker:** lb-rei-01…09, lb-pen-01…09 (Werte mit Stand je
|
||||
Quelle); § 26-Kopplung lb-sva-04 ⚠; RIS-Gate für Tag-/KM-Geld-
|
||||
Verordnungswerte.
|
||||
- **Tests:** Rechenfälle `reisekosten.md` — Taggeld In/Ausland,
|
||||
Kilometergeld, SV-frei vs. -pflichtig (§-26-Grenze), Pendler-
|
||||
pauschale aliquot, Pendlereuro.
|
||||
|
||||
### AP14 — Endabrechnungs-Zyklus (12–18 PT)
|
||||
|
||||
- **Ziel:** Beendigungs-Abrechnung aus einem Guss: Urlaubsersatz-
|
||||
leistung, offene SZ, Vergleiche/Abgangsentschädigungen, Auf-
|
||||
rechnung, Zeitschulden.
|
||||
- **Umsetzung (Privatmodul):** Wizard `l10n.at.payroll.endabrechnung.
|
||||
wizard` (Erweiterung neben dem BV-Wizard, zieht offene Posten
|
||||
zusammen); Regeln `URL_ERSATZ_LFD`/`URL_ERSATZ_SZ` (Zwei-Kompo-
|
||||
nenten, § 67 Abs 8 lit d; SV § 11 Abs 2 ASVG-Verlängerung),
|
||||
`ABGANG_*`/`VERGLEICH_*` (§-67-Abs-3/6 über Kern-Flag
|
||||
`l10n_at_s67_abs` ✅), Prämien bei Beendigung, Aufrechnung
|
||||
Vorschüsse/Darlehen (Rückzahlungsmodus lb-end-06, lb-lvr-01/02),
|
||||
Zeitguthaben/-schulden-Abrechnung.
|
||||
- **Rechtsanker:** lb-url-09/15/16, lb-son-05, lb-end-04/05/06/08…22;
|
||||
§ 67 Abs 8 lit d EStG + § 11 Abs 2 ASVG RIS-Gate.
|
||||
- **Tests:** Rechenfälle `endabrechnung.md` — Urlaubsersatzleistung
|
||||
2 Komponenten, §-11-Abs-2-SV-Fall, Vergleich Günstiger § 67 Abs 3,
|
||||
Aufrechnung gegen SZ (pfändungsfest? — Schnittstelle AP11),
|
||||
Zeitguthaben.
|
||||
|
||||
### AP15 — Abfertigung Alt (6–10 PT)
|
||||
|
||||
- **Ziel:** Bezugszeile für Alt-Abfertigungen (Dienstjahres-Staffel)
|
||||
mit korrekter LSt (Günstiger-Prinzip) und SV-Freiheit.
|
||||
- **Umsetzung (Privatmodul):** Regel `ABF_ALT` auf eigener Zeile +
|
||||
Dienstjahres-Staffel (Parameter, versioniert); LSt über Kern-Flag
|
||||
`l10n_at_s67_abs` ✅ (§ 67 Abs 3 Günstiger vs. 6 %, A5 ✅), SV-frei
|
||||
§ 49 Abs 3 Z 7 ✅; Sonderfall Dienstverhältnisende während Karenz
|
||||
(lb-kar-01); Anbindung an den AP14-Wizard.
|
||||
- **Rechtsanker:** lb-end-01/02, lb-kar-01; **Staffelwerte RIS/KB-Gate**
|
||||
(keine Trainingswerte; Altsystem-Tabellenwerk per RIS beschaffen).
|
||||
- **Tests:** Rechenfälle — Günstiger-Vergleich je Staffelstufe,
|
||||
Karenz-Ende-Fall, Versteuerung mit §-67-Aufrollung.
|
||||
|
||||
### AP16 — Karenz-/Familien-Reste (12–18 PT) — **Scope-Gate Pilotmandant (8.13)**
|
||||
|
||||
- **Ziel:** SV-Meldewesen und Entgeltfolgen der Karenz-Familie
|
||||
schließen (AP5 hat die WET-Grundlage `ATP_KARENZ` geliefert).
|
||||
- **Umsetzung (Privatmodul):** VR-Events (An-/Abmeldung) aus Karenz-
|
||||
Wochen über die bestehende `l10n.at.payroll.vr.event`-Registry;
|
||||
Elternteilzeit-Entgeltregel; WETs/Urlaubsarten für Bildungs-/
|
||||
Pflege-/Hospizkarenz inkl. Entgeltfolgen; Papamonat/Familienzeit-
|
||||
bonus (FamZeitbG); Karenz-Referenzmonat § 7 Abs 4 Z 1–3 BMSVG für
|
||||
die fiktive BV; Sonderwochengeld § 163/Ruhen § 166 (AP7-Optionen).
|
||||
- **Rechtsanker:** lb-kar-01…06, lb-elt-01…06, lb-kzs-01…07,
|
||||
lb-pap-01/02, lb-kbg-06; MSchG/VKG/KBGG/APSG/FamZeitbG per RIS-Gate
|
||||
(Beschaffungsliste RQ-Privat Abschn. 11 enthält den Block bereits).
|
||||
- **Tests:** Rechenfälle `karenz.md` — Ab-/Anmeldung um Karenz,
|
||||
Elternteilzeit-Anteil, Referenzmonat-BV.
|
||||
- **Gate:** Umsetzung erst nach Pilotmandats-Entscheidung 8.13
|
||||
(Mutterschutz-/Karenz-Block).
|
||||
|
||||
### AP17 — Meldewesen-Reste (10–16 PT)
|
||||
|
||||
- **Ziel:** L17 + § 109a/§ 109b-Fixsätze und die Geringfügigen-
|
||||
Jahreszahlung im mBGM.
|
||||
- **Umsetzung:** Kern `models/lohnzettel_l17.py` (FinOnl-Fixsatz
|
||||
neben `lohnzettel_l16.py`) + §-109a/§-109b-Meldungen (Fristen im
|
||||
Fristenkalender ✅; Prüfkataloge + DM-ORG ✅ beschafft in
|
||||
`.firecrawl/ris/`); Privatmodul: mBGM-Erweiterung Geringfügigen-
|
||||
Jahreszahlung (DG-Zuschlag 2,5 %).
|
||||
- **Rechtsanker:** DM-ORG 42.7.0 ✅; § 51 Abs 4 Z 2 ASVG RIS-Gate
|
||||
(Wert nur aus Quelle); lb-lvr-06, lb-bes-06.
|
||||
- **Tests:** Fixsatz-Goldenfiles (Muster `test_elda_l16`), Jahres-
|
||||
zahlungs-Rechenfall.
|
||||
|
||||
### AP18 — Schwerarbeit/NSchG-Vollmechanik (6–12 PT) — Beschaffungsgate
|
||||
|
||||
- **Ziel:** NSchG-Monatszähler und Schwerarbeitsmeldung.
|
||||
- **Umsetzung (Privatmodul):** Zähler auf den Nacht-WETs
|
||||
(`ATP_ATT_NACHT` ✅) für das NSchG-Monatskriterium; Schwerarbeits-
|
||||
meldung § 74 Abs 3 ASVG auf dem ELDA-Framework (E.22-Struktur ✅
|
||||
vorhanden).
|
||||
- **Rechtsanker:** lb-nsc-01…03, lb-swa-01…05; NSchAB 3,80 % ✅
|
||||
(TASY, AP0); **ASchV/Schwerarbeits-Volltexte fehlen in KB und RQ —
|
||||
Beschaffung vor Bau** (Batch-6/7-Liste im RUNBOOK).
|
||||
- **Tests:** Zähler-Rechenfälle (Monatsgrenze), Meldungs-Fixsatz.
|
||||
|
||||
### AP19 — Status- & Verhinderungs-Sonderfälle (8–14 PT)
|
||||
|
||||
- **Ziel:** Sonderstatus sauber abbilden statt über Workarounds.
|
||||
- **Umsetzung (Privatmodul):** SV-Status-Flag GF/Vorstand (Behand-
|
||||
lung nach Organ-/AN-Zuordnung), Ferialpraktikanten-Status; Feier-
|
||||
tags-EFZ AngG (prüfen, ob Odoo-Public-Holiday-WET + `is_paid`-Muster
|
||||
der AP5-Abwesenheiten genügt — dann kleine Regel, sonst eigene
|
||||
Mechanik); Dienstverhinderungs-WETs (Epidemiegesetz, AG-seitige
|
||||
Verhinderung, Streik); Präsenzdienst-Fiktion § 7 Abs 2 BMSVG als
|
||||
Aufsatz auf den `_at_bv_basis_laufen`-Hook (AP4/AP7-Infrastruktur).
|
||||
- **Rechtsanker:** lb-gsf-02/03/05, lb-vst-03, wk-gsf-01, lb-pra-03,
|
||||
lb-efz-01/02, lb-dvh-01…04, lb-prd-05/06 — je RIS-Gate (lb-dvh-01
|
||||
§-Zitat ❓, lb-dvh-04 ❓ bewusst offen — dokumentiert lassen).
|
||||
- **Tests:** je Status ein Rechenfall (GF-Nicht-AN, Praktikant-
|
||||
Geringfügigkeit, Feiertagsentgelt, Präsenzdienst-BV-Fiktion).
|
||||
|
||||
### AP20+ — Optionen (40–70 PT) — je Scope-Gate Pilotmandant (P1/P10)
|
||||
|
||||
| Option | KB-Anker | Vorarbeit |
|
||||
|---|---|---|
|
||||
| Altersteilzeit | lb-atz-01…15 | AMS-Primärquellen ✅ (`.additional_docs/`), Ersatzquote 27,5 % ✅ (A4); A4-Rest All-In-Oberwert klären |
|
||||
| Kurzarbeit | lb-azm-04/05/06 | Lehrlinge-Frage ✅ (B1); KUG = AMS-Leistung, DG rechnet Entgeltausfall |
|
||||
| Mitarbeiterbeteiligung/Zukunftssicherung | lb-vor-01/07…11, wk-akt-12 | §-108a-Modell RV-Stand ⚠ (wk-akt-12) — Endfassung abwarten |
|
||||
| Entsendung/183-Tage | lb-grz-01…08, lb-lsd-01/03 | ⚠-Familie lb-rei-01/02/08 (Finanz vs. UFS) vor Umsetzung klären |
|
||||
|
||||
### Quer-Lohnsteuerreste (in AP14/AP17/AP16 einschiebbar)
|
||||
|
||||
FB+/ANV-Automatik (negatives LSt-Ergebnis → ANV-Berechnung statt
|
||||
Hinweis; Kind-Modell ✅ wartet die Daten), Fallweise-GFG je
|
||||
Beschäftigungstag (lb-bes-02/03), Prämien-Anspruchsprinzip-Aufrollung
|
||||
(lb-prm-04/06; blockiert durch A3 „Formel 7" — konservativer
|
||||
GP3-Default bleibt), 2027-Werte erst nach Verkündung (C7).
|
||||
|
||||
## 5. Reihenfolge & Summen
|
||||
|
||||
Empfehlung: **AP8 (parallel) → AP11 → AP12 → AP13 → AP14 → AP15 →
|
||||
AP16 (Gate) → AP17 → AP18 (Gate Beschaffung) → AP19 → AP20+ (Gates).**
|
||||
|
||||
- Σ AP11–AP19: **≈ 90–142 PT** (Expertenschätzung ±20 %)
|
||||
- AP20+-Optionen: ≈ 40–70 PT, nur nach Freigabe
|
||||
|
||||
## 6. Risiken & Abhängigkeiten
|
||||
|
||||
1. **A3 „Formel 7"** blockiert weiterhin die Prämienoptimierung
|
||||
(konservativer GP3-Default umgesetzt) — kein Blocker für AP11–AP19.
|
||||
2. **C7-Gates:** alle 2027er-Werte (ALV-Entfall, DAG 23 %, Tele-
|
||||
arbeitspauschale, SV-Werte) erst nach Verkündung/Verordnung.
|
||||
3. **KB-D-Lücken:** für Pfändungs-SZ-Details und Telearbeitspauschale
|
||||
gibt es keine Briefings — RIS-Beschaffung bzw. Nutzer-Beleg;
|
||||
Mustervereinbarung Darlehen (AP14) außerhalb der Engine.
|
||||
4. **Scope-Gates Pilotmandant:** AP16 (Karenz-Block) und AP20+-
|
||||
Optionen (Prämissen P1/P10) — keine stille Scope-Erweiterung.
|
||||
5. **Session-Koordination:** AP8 schreibt parallel am Privatmodul —
|
||||
Paket-Starts abstimmen, Merge-Konflikte vermeiden.
|
||||
6. **Wochengeld-SZ-%** bleibt ⚠ bis Satzungsverifikation (AP7-Rest).
|
||||
7. **Verifikationsdisziplin:** jede §-Angabe in diesem Plan ist ein
|
||||
Anker mit Gate — nichts davon ohne RIS/Quellenbeleg in Code
|
||||
fließen („Verify before assuming").
|
||||
|
||||
## 7. Freigabestatus
|
||||
|
||||
- **(a)** Plan inhaltlich freigegeben (Nutzer, 2026-09-12).
|
||||
- **(b)** Persistiert als dieses Dokument (`PLAN-Restluecken-AP11.md`);
|
||||
Updates je Paket-Fertigstellung (Status-Spalte je AP nachziehen).
|
||||
- **(c)** AP8 läuft parallel in eigener Session; dieses Dokument führt
|
||||
AP8 nur als Restabgleich — nach Fertigstellung Abgleich gegen die
|
||||
Plan-Annahmen und Test-Integration nachziehen.
|
||||
Reference in New Issue
Block a user