[IMP] l10n_at_hr_payroll_private: GP2 Entgelt-Engine (structures, WETs, rules, Rechenfall tests)

This commit is contained in:
2026-09-11 20:53:38 +02:00
parent 80bcf8626b
commit 51ff857414
12 changed files with 1308 additions and 25 deletions
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Manuelle Eingaben (GP2): Prämien/Ausgleichszahlungen ohne -->
<!-- eigene Work-Entry-Quelle; Zuschläge mit Quelle laufen über -->
<!-- die WET-Regeln. -->
<record id="input_type_atp_pramie" model="hr.payslip.input.type">
<field name="name">Prämie/Ausgleich (manuell)</field>
<field name="code">ATP_PRAMIE</field>
</record>
</odoo>
@@ -39,4 +39,6 @@ kv2748_wert_sz14_dezember,l10n_at_hr_payroll_private.kv_si_2748,sz14_no_urlaub_m
kv2748_wert_ue_fenster_von,l10n_at_hr_payroll_private.kv_si_2748,ue_fenster_von,ÜStd-Zuschlag 100 % ab Uhr (§ 5 Abs 2),20,2026-01-01
kv2748_wert_ue_fenster_bis,l10n_at_hr_payroll_private.kv_si_2748,ue_fenster_bis,ÜStd-Zuschlag 100 % bis Uhr (§ 5 Abs 2),6,2026-01-01
kv2748_wert_nacht_von,l10n_at_hr_payroll_private.kv_si_2748,nacht_sv_fenster_von,Nacht-Sondervergütung ab Uhr (§ 6),22,2026-01-01
kv2748_wert_nacht_bis,l10n_at_hr_payroll_private.kv_si_2748,nacht_sv_fenster_bis,Nacht-Sondervergütung bis Uhr (§ 6),6,2026-01-01
kv2748_wert_nacht_bis,l10n_at_hr_payroll_private.kv_si_2748,nacht_sv_fenster_bis,Nacht-Sondervergütung bis Uhr (§ 6),6,2026-01-01
kv2203_wert_sz_aliquote,l10n_at_hr_payroll_private.kv_si_2203,sz_aliquote_methode,Aliquotierung 13./14. bei Ein-/Austritt (§ 11 Abs 4),'kalendertage',2026-01-01
kv2748_wert_sz_aliquote,l10n_at_hr_payroll_private.kv_si_2748,sz_aliquote_methode,Aliquotierung 13./14. bei Ein-/Austritt (§ 11 Abs 4),'kalendertage',2026-01-01
1 id kv_id:id code name parameter_value date_from
39 kv2748_wert_ue_fenster_von l10n_at_hr_payroll_private.kv_si_2748 ue_fenster_von ÜStd-Zuschlag 100 % ab Uhr (§ 5 Abs 2) 20 2026-01-01
40 kv2748_wert_ue_fenster_bis l10n_at_hr_payroll_private.kv_si_2748 ue_fenster_bis ÜStd-Zuschlag 100 % bis Uhr (§ 5 Abs 2) 6 2026-01-01
41 kv2748_wert_nacht_von l10n_at_hr_payroll_private.kv_si_2748 nacht_sv_fenster_von Nacht-Sondervergütung ab Uhr (§ 6) 22 2026-01-01
42 kv2748_wert_nacht_bis l10n_at_hr_payroll_private.kv_si_2748 nacht_sv_fenster_bis Nacht-Sondervergütung bis Uhr (§ 6) 6 2026-01-01
43 kv2203_wert_sz_aliquote l10n_at_hr_payroll_private.kv_si_2203 sz_aliquote_methode Aliquotierung 13./14. bei Ein-/Austritt (§ 11 Abs 4) 'kalendertage' 2026-01-01
44 kv2748_wert_sz_aliquote l10n_at_hr_payroll_private.kv_si_2748 sz_aliquote_methode Aliquotierung 13./14. bei Ein-/Austritt (§ 11 Abs 4) 'kalendertage' 2026-01-01
@@ -0,0 +1,347 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- ============================================================ -->
<!-- GP2 Regelinventar AT-Privatwirtschaft (Plan 4.2). -->
<!-- Datenquelle ausschließlich kv.wert (Vorlauf 2026-09-11 -->
<!-- verifiziert) bzw. Kern-Rule-Parameter — keine Literale in -->
<!-- Regelcode. §-Anker je Code s. RECHTSQUELLEN-Privat 2/3 und -->
<!-- docs/rechenfaelle/. -->
<!-- ============================================================ -->
<!-- KVGEO: KV-Gruppenentgelt § 17 KV (Stufen-Lookup zum Stichtag, -->
<!-- Überzahlung überschreibt, Teilzeit via work_time_rate, -->
<!-- Prorate Ein-/Austritt § 17 Abs 2 als Arbeitstage-Faktor). -->
<record id="rule_kvgeo" model="hr.salary.rule">
<field name="name">KV-Gruppenentgelt (§ 17 KV)</field>
<field name="code">KVGEO</field>
<field name="sequence" eval="1"/>
<field name="category_id" ref="l10n_at_hr_payroll_private.category_atp_brutto"/>
<field name="struct_id" ref="l10n_at_hr_payroll_private.structure_at_privat"/>
<field name="condition_select">python</field>
<field name="condition_python">result = bool(employee.kv_id and employee.kv_gruppe_id and (employee.ueberzahlung or employee.erfahrungsstufe))</field>
<field name="amount_select">code</field>
<field name="amount_python_compute"><![CDATA[
# § 17 KV: Stufen-Mindestgrundgehalt zum Stichtag; individuelle
# Überzahlung überschreibt den Katalogwert (Basis = Vollzeit-
# Katalogwert × work_time_rate). Prorate Ein-/Austritt § 17 Abs 2:
# Arbeitstage-Faktor aus dem Ressourcenkalender (user decision;
# für MoSa-Kalender identisch mit ÷26 × Werktage).
result = payslip._at_kv_gehalt_basis() * payslip._at_arbeitstage_faktor()
]]></field>
</record>
<!-- NACHT_SV: Sondervergütung Nachtarbeit § 6 KV (226, €/h, -->
<!-- kumulativ zu allen Vergütungen; Höchstzuschlag § 5 Abs 7 -->
<!-- betrifft nur Zuschläge, nicht diese Vergütung). -->
<record id="rule_nacht_sv" model="hr.salary.rule">
<field name="name">Nacht-Sondervergütung (§ 6 KV, 226 Uhr)</field>
<field name="code">NACHT_SV</field>
<field name="sequence" eval="2"/>
<field name="category_id" ref="l10n_at_hr_payroll_private.category_atp_brutto"/>
<field name="struct_id" ref="l10n_at_hr_payroll_private.structure_at_privat"/>
<field name="condition_select">python</field>
<field name="condition_python">result = payslip._at_nacht_sv_stunden() > 0</field>
<field name="amount_select">code</field>
<field name="amount_python_compute"><![CDATA[
# § 6 KV: €-Satz je Nachtstunde (226), unabhängig von Gehalt,
# Werk-/Sonn-/Feiertag und Zuschlagsstatus; Version je date_from
# (2748: 2,71 € ab 2026, 2,76 € ab 2027).
satz = float(employee.kv_id._wert('nacht_sonderverguetung', payslip.date_to))
result = payslip._at_nacht_sv_stunden() * satz
]]></field>
</record>
<!-- Überstunden § 5 KV: Grundvergütung + Zuschlag; Basis = -->
<!-- Monatsgehalt ÷ Teilungsfaktor (2203: 150; 2748: 143). -->
<record id="rule_ue_std" model="hr.salary.rule">
<field name="name">Überstunden Tag (§ 5 Abs 2 KV)</field>
<field name="code">UE_STD</field>
<field name="sequence" eval="10"/>
<field name="category_id" ref="l10n_at_hr_payroll_private.category_atp_brutto"/>
<field name="struct_id" ref="l10n_at_hr_payroll_private.structure_at_privat"/>
<field name="condition_select">python</field>
<field name="condition_python">result = 'ATP_UE_STD' in worked_days and worked_days['ATP_UE_STD'].number_of_hours > 0</field>
<field name="amount_select">code</field>
<field name="amount_python_compute"><![CDATA[
# Grundstunde ÷ Teilungsfaktor ÜStd (§ 5 Abs 6 KV) + Zuschlag
# Tag (kv.wert ue_zuschlag_tag). Höchstzuschlag § 5 Abs 7 über
# die WET-Klassifikation (je Stunde genau ein Zuschlagstyp).
grund = payslip._at_grundstunde_ue()
zuschlag = float(employee.kv_id._wert('ue_zuschlag_tag', payslip.date_to))
stunden = worked_days['ATP_UE_STD'].number_of_hours
result = stunden * grund * (1 + zuschlag / 100.0)
]]></field>
</record>
<record id="rule_ue_nacht" model="hr.salary.rule">
<field name="name">Überstunden Nacht 226 (§ 5 Abs 2 KV)</field>
<field name="code">UE_NACHT</field>
<field name="sequence" eval="11"/>
<field name="category_id" ref="l10n_at_hr_payroll_private.category_atp_brutto"/>
<field name="struct_id" ref="l10n_at_hr_payroll_private.structure_at_privat"/>
<field name="condition_select">python</field>
<field name="condition_python">result = 'ATP_UE_NACHT' in worked_days and worked_days['ATP_UE_NACHT'].number_of_hours > 0</field>
<field name="amount_select">code</field>
<field name="amount_python_compute"><![CDATA[
# 100 %-Zuschlag-Fenster 206 (kv.wert ue_fenster_von/bis);
# hier der Anteil 226 (€-Sondervergütung läuft über NACHT_SV).
grund = payslip._at_grundstunde_ue()
zuschlag = float(employee.kv_id._wert('ue_zuschlag_nacht', payslip.date_to))
stunden = worked_days['ATP_UE_NACHT'].number_of_hours
result = stunden * grund * (1 + zuschlag / 100.0)
]]></field>
</record>
<record id="rule_ue_nacht_frueh" model="hr.salary.rule">
<field name="name">Überstunden 2022 Uhr (§ 5 Abs 2 KV)</field>
<field name="code">UE_NACHT_FRUEH</field>
<field name="sequence" eval="12"/>
<field name="category_id" ref="l10n_at_hr_payroll_private.category_atp_brutto"/>
<field name="struct_id" ref="l10n_at_hr_payroll_private.structure_at_privat"/>
<field name="condition_select">python</field>
<field name="condition_python">result = 'ATP_UE_NACHT_FRUEH' in worked_days and worked_days['ATP_UE_NACHT_FRUEH'].number_of_hours > 0</field>
<field name="amount_select">code</field>
<field name="amount_python_compute"><![CDATA[
# Randfenster 2022 des 100 %-Zuschlags — keine €-Sondervergütung
# (Abgrenzung RF-Z6: Zuschlag ja, Nacht-SV nein).
grund = payslip._at_grundstunde_ue()
zuschlag = float(employee.kv_id._wert('ue_zuschlag_nacht', payslip.date_to))
stunden = worked_days['ATP_UE_NACHT_FRUEH'].number_of_hours
result = stunden * grund * (1 + zuschlag / 100.0)
]]></field>
</record>
<record id="rule_ue_sofei" model="hr.salary.rule">
<field name="name">Sonn-/Feiertagsüberstunden Tag (§ 5 Abs 4/5 KV)</field>
<field name="code">UE_SOFEI</field>
<field name="sequence" eval="13"/>
<field name="category_id" ref="l10n_at_hr_payroll_private.category_atp_brutto"/>
<field name="struct_id" ref="l10n_at_hr_payroll_private.structure_at_privat"/>
<field name="condition_select">python</field>
<field name="condition_python">result = 'ATP_UE_SOFEI' in worked_days and worked_days['ATP_UE_SOFEI'].number_of_hours > 0</field>
<field name="amount_select">code</field>
<field name="amount_python_compute"><![CDATA[
# SonntagsüStd § 5 Abs 4, FeiertagsüStd ab erster Stunde § 5 Abs 5
# iVm ARG — Zuschlag je kv.wert so_feiertag_zuschlag.
grund = payslip._at_grundstunde_ue()
zuschlag = float(employee.kv_id._wert('so_feiertag_zuschlag', payslip.date_to))
stunden = worked_days['ATP_UE_SOFEI'].number_of_hours
result = stunden * grund * (1 + zuschlag / 100.0)
]]></field>
</record>
<record id="rule_ue_sofei_nacht" model="hr.salary.rule">
<field name="name">Sonn-/Feiertagsüberstunden Nacht 226 (§ 5 Abs 4/5 KV)</field>
<field name="code">UE_SOFEI_NACHT</field>
<field name="sequence" eval="14"/>
<field name="category_id" ref="l10n_at_hr_payroll_private.category_atp_brutto"/>
<field name="struct_id" ref="l10n_at_hr_payroll_private.structure_at_privat"/>
<field name="condition_select">python</field>
<field name="condition_python">result = 'ATP_UE_SOFEI_NACHT' in worked_days and worked_days['ATP_UE_SOFEI_NACHT'].number_of_hours > 0</field>
<field name="amount_select">code</field>
<field name="amount_python_compute"><![CDATA[
# Zuschlagskumulation So/Fei × Nacht: nur der höchste Zuschlag
# (§ 5 Abs 7) = 100 % einmal (Höchstzuschlag über WET-Klasse);
# €-Sondervergütung separat über NACHT_SV.
grund = payslip._at_grundstunde_ue()
zuschlag = float(employee.kv_id._wert('so_feiertag_zuschlag', payslip.date_to))
stunden = worked_days['ATP_UE_SOFEI_NACHT'].number_of_hours
result = stunden * grund * (1 + zuschlag / 100.0)
]]></field>
</record>
<!-- Mehrarbeit: Band KV-NAZ→40 h § 4a (nur KVs mit verkürzter -->
<!-- NAZ, z. B. 2748: 38,5→40; Basis ÷ Teilungsfaktor Norm). -->
<record id="rule_mehr_vz" model="hr.salary.rule">
<field name="name">Mehrarbeit Band KV-NAZ→40 h (§ 4a KV)</field>
<field name="code">MEHR_VZ</field>
<field name="sequence" eval="20"/>
<field name="category_id" ref="l10n_at_hr_payroll_private.category_atp_brutto"/>
<field name="struct_id" ref="l10n_at_hr_payroll_private.structure_at_privat"/>
<field name="condition_select">python</field>
<field name="condition_python"><![CDATA[
result = 'ATP_MEHR_VZ' in worked_days and worked_days['ATP_MEHR_VZ'].number_of_hours > 0 and bool(employee.kv_id._wert('mehrarbeit_zuschlag', payslip.date_to, raise_if_not_found=False))
]]></field>
<field name="amount_select">code</field>
<field name="amount_python_compute"><![CDATA[
# § 4a KV: Zuschlag auf die Normalstunde (Basis ÷ Teilungsfaktor
# Normalstunde, 2748: ÷ 167); zählt nicht gegen das ÜStd-Ausmaß;
# Stunden mit > 50 %-Zuschlag sind ÜStd (§ 4a letzter Satz —
# Klassifikation stromauf).
grund = payslip._at_grundstunde_norm()
zuschlag = float(employee.kv_id._wert('mehrarbeit_zuschlag', payslip.date_to))
stunden = worked_days['ATP_MEHR_VZ'].number_of_hours
result = stunden * grund * (1 + zuschlag / 100.0)
]]></field>
</record>
<record id="rule_mehr_vz_nacht" model="hr.salary.rule">
<field name="name">Mehrarbeit Band Nacht 226 (§ 4a KV)</field>
<field name="code">MEHR_VZ_NACHT</field>
<field name="sequence" eval="21"/>
<field name="category_id" ref="l10n_at_hr_payroll_private.category_atp_brutto"/>
<field name="struct_id" ref="l10n_at_hr_payroll_private.structure_at_privat"/>
<field name="condition_select">python</field>
<field name="condition_python"><![CDATA[
result = 'ATP_MEHR_VZ_NACHT' in worked_days and worked_days['ATP_MEHR_VZ_NACHT'].number_of_hours > 0 and bool(employee.kv_id._wert('mehrarbeit_zuschlag', payslip.date_to, raise_if_not_found=False))
]]></field>
<field name="amount_select">code</field>
<field name="amount_python_compute"><![CDATA[
grund = payslip._at_grundstunde_norm()
zuschlag = float(employee.kv_id._wert('mehrarbeit_zuschlag', payslip.date_to))
stunden = worked_days['ATP_MEHR_VZ_NACHT'].number_of_hours
result = stunden * grund * (1 + zuschlag / 100.0)
]]></field>
</record>
<!-- Mehrarbeit Teilzeit § 19d AZG: gesetzlich 25 % (Kern- -->
<!-- Parameter); KV-Abweichung über kv.wert code -->
<!-- mehrarbeit_teilzeit_zuschlag (user decision: KV vor Gesetz). -->
<record id="rule_mehr_tz" model="hr.salary.rule">
<field name="name">Mehrarbeit Teilzeit (§ 19d AZG)</field>
<field name="code">MEHR_TZ</field>
<field name="sequence" eval="22"/>
<field name="category_id" ref="l10n_at_hr_payroll_private.category_atp_brutto"/>
<field name="struct_id" ref="l10n_at_hr_payroll_private.structure_at_privat"/>
<field name="condition_select">python</field>
<field name="condition_python">result = 'ATP_MEHR_TZ' in worked_days and worked_days['ATP_MEHR_TZ'].number_of_hours > 0</field>
<field name="amount_select">code</field>
<field name="amount_python_compute"><![CDATA[
# § 19d AZG: Stunden über Teilzeit-Ausmaß bis Vollzeit-NAZ;
# Zuschlag 25 % gesetzlich (Kern-Parameter), KV kann besser
# stellen (kv.wert-Code mehrarbeit_teilzeit_zuschlag).
grund = payslip._at_grundstunde_norm()
kv_zuschlag = employee.kv_id._wert('mehrarbeit_teilzeit_zuschlag', payslip.date_to, raise_if_not_found=False)
zuschlag = float(kv_zuschlag) if kv_zuschlag else float(payslip._rule_parameter('at_mehrarbeit_teilzeit_zuschlag'))
stunden = worked_days['ATP_MEHR_TZ'].number_of_hours
result = stunden * grund * (1 + zuschlag / 100.0)
]]></field>
</record>
<record id="rule_mehr_tz_nacht" model="hr.salary.rule">
<field name="name">Mehrarbeit Teilzeit Nacht 226 (§ 19d AZG)</field>
<field name="code">MEHR_TZ_NACHT</field>
<field name="sequence" eval="23"/>
<field name="category_id" ref="l10n_at_hr_payroll_private.category_atp_brutto"/>
<field name="struct_id" ref="l10n_at_hr_payroll_private.structure_at_privat"/>
<field name="condition_select">python</field>
<field name="condition_python">result = 'ATP_MEHR_TZ_NACHT' in worked_days and worked_days['ATP_MEHR_TZ_NACHT'].number_of_hours > 0</field>
<field name="amount_select">code</field>
<field name="amount_python_compute"><![CDATA[
grund = payslip._at_grundstunde_norm()
kv_zuschlag = employee.kv_id._wert('mehrarbeit_teilzeit_zuschlag', payslip.date_to, raise_if_not_found=False)
zuschlag = float(kv_zuschlag) if kv_zuschlag else float(payslip._rule_parameter('at_mehrarbeit_teilzeit_zuschlag'))
stunden = worked_days['ATP_MEHR_TZ_NACHT'].number_of_hours
result = stunden * grund * (1 + zuschlag / 100.0)
]]></field>
</record>
<!-- URL_UE: Ø regelmäßige Überstunden im Urlaubsmonat (§ 2 Abs 2 -->
<!-- GKV iVm § 5 Abs 11 (2203) / Abs 10 (2748): regelmäßig = -->
<!-- ≥ 7 der letzten 12 Kalendermonate; Durchschnitt 12 Monate). -->
<record id="rule_url_ue" model="hr.salary.rule">
<field name="name">Urlaubsentgelt: Ø regelmäßige Überstunden (§ 6 UrlG)</field>
<field name="code">URL_UE</field>
<field name="sequence" eval="30"/>
<field name="category_id" ref="l10n_at_hr_payroll_private.category_atp_brutto"/>
<field name="struct_id" ref="l10n_at_hr_payroll_private.structure_at_privat"/>
<field name="condition_select">python</field>
<field name="condition_python"><![CDATA[
result = payslip._at_urlaub_ue_avg()[0] and any(wd.work_entry_type_id.is_leave for wd in payslip.worked_days_line_ids)
]]></field>
<field name="amount_select">code</field>
<field name="amount_python_compute"><![CDATA[
# § 6 UrlG Abs 2: laufendes (monatsweises) Entgelt bleibt
# unverändert (KVGEO läuft weiter); zusätzlich der monatliche
# Durchschnitt der regelmäßigen ÜStd, anteilig nach
# Urlaubstagen ÷ Arbeitstage des Monats (Konvention RF-U2).
regelmaessig, avg = payslip._at_urlaub_ue_avg()
urlaubstage = sum(wd.number_of_days for wd in payslip.worked_days_line_ids if wd.work_entry_type_id.is_leave)
arbeitstage = payslip._at_arbeitstage(payslip.date_from, payslip.date_to)
result = avg * urlaubstage / arbeitstage if arbeitstage and regelmaessig else 0.0
]]></field>
</record>
<!-- PRAM: manuelle Prämien/Ausgleichszahlungen (Input). -->
<record id="rule_pram" model="hr.salary.rule">
<field name="name">Prämie/Ausgleich (manuell)</field>
<field name="code">PRAM</field>
<field name="sequence" eval="40"/>
<field name="category_id" ref="l10n_at_hr_payroll_private.category_atp_brutto"/>
<field name="struct_id" ref="l10n_at_hr_payroll_private.structure_at_privat"/>
<field name="condition_select">python</field>
<field name="condition_python">result = 'ATP_PRAMIE' in inputs and inputs['ATP_PRAMIE'].amount</field>
<field name="amount_select">code</field>
<field name="amount_python_compute"><![CDATA[
result = inputs['ATP_PRAMIE'].amount
]]></field>
</record>
<!-- B13/B14: 13./14. Bezug § 11 KV — begünstigte Bezüge iSd -->
<!-- § 67 EStG (Markierung/Sechstel-Engine folgt GP3 im Kern). -->
<record id="rule_b13" model="hr.salary.rule">
<field name="name">13. Bezug (§ 11 KV)</field>
<field name="code">B13</field>
<field name="sequence" eval="50"/>
<field name="category_id" ref="l10n_at_hr_payroll_private.category_atp_brutto"/>
<field name="struct_id" ref="l10n_at_hr_payroll_private.structure_at_privat"/>
<field name="condition_select">python</field>
<field name="condition_python">result = payslip._at_b13_faellig()</field>
<field name="amount_select">code</field>
<field name="amount_python_compute"><![CDATA[
# Basis je kv.wert sz13_basis_monat: 'auszahlung' (2203) =
# Monatsgehalt des Auszahlungsmonats; 'november' (2748) =
# November-Gehalt (§ 11 Abs 2). Aliquote Ein-/Austritt § 11
# Abs 4 über sz_aliquote_methode ('kalendertage': KT/365;
# 'monate': volle Monate/12); Mischberechnung Beschäftigungs-
# ausmaß (2203) über die Jahres-Versionen.
basis_datum = payslip._at_sz_basis_datum('sz13_basis_monat')
result = payslip._at_sz_basis(basis_datum) * payslip._at_sz_dienstzeit_faktor()
]]></field>
</record>
<record id="rule_b14" model="hr.salary.rule">
<field name="name">14. Bezug / Urlaubszuschuss (§ 11 KV)</field>
<field name="code">B14</field>
<field name="sequence" eval="51"/>
<field name="category_id" ref="l10n_at_hr_payroll_private.category_atp_brutto"/>
<field name="struct_id" ref="l10n_at_hr_payroll_private.structure_at_privat"/>
<field name="condition_select">python</field>
<field name="condition_python">result = payslip._at_b14_faellig()</field>
<field name="amount_select">code</field>
<field name="amount_python_compute"><![CDATA[
# Fälligkeit § 11 Abs 3: bei Urlaubsantritt (R1: erstfälliger
# Urlaubsmonat, längster-Teil-Verfeinerung dokumentiert), ohne
# Urlaub mit der Dezember-Abrechnung (sz14_no_urlaub_monat),
# bei Austritt aliquote Endabrechnung; ohne KV-Code Fallback
# Juni (user decision). Basis = Auszahlungsmonat-Gehalt.
result = payslip._at_sz_basis(payslip.date_to) * payslip._at_sz_dienstzeit_faktor()
]]></field>
</record>
<record id="rule_b14_rueck" model="hr.salary.rule">
<field name="name">14. Bezug Rückverrechnung bei Austritt (§ 11 Abs 4 KV)</field>
<field name="code">B14_RUECK</field>
<field name="sequence" eval="52"/>
<field name="category_id" ref="l10n_at_hr_payroll_private.category_atp_brutto"/>
<field name="struct_id" ref="l10n_at_hr_payroll_private.structure_at_privat"/>
<field name="condition_select">python</field>
<field name="condition_python"><![CDATA[
austritt = version.contract_date_end and payslip.date_from <= version.contract_date_end <= payslip.date_to
result = bool(austritt and payslip._at_sz_bereits_bezahlt('B14'))
]]></field>
<field name="amount_select">code</field>
<field name="amount_python_compute"><![CDATA[
# § 11 Abs 4, 2. Satz: bei Austritt nach vollem B14-Bezug ist
# der anteilige Überbezug in der Endabrechnung abzuziehen.
bezahlt = payslip._at_sz_bereits_bezahlt('B14')
result = bezahlt * (payslip._at_sz_dienstzeit_faktor() - 1.0)
]]></field>
</record>
<!-- NETTO (Kategoriensummen; DN-Abzüge folgen GP3/GP4). -->
<record id="rule_netto" model="hr.salary.rule">
<field name="name">Netto</field>
<field name="code">NETTO</field>
<field name="sequence" eval="100"/>
<field name="category_id" ref="l10n_at_hr_payroll_private.category_atp_netto"/>
<field name="struct_id" ref="l10n_at_hr_payroll_private.structure_at_privat"/>
<field name="condition_select">none</field>
<field name="amount_select">code</field>
<field name="amount_python_compute"><![CDATA[
result = categories['ATP_BRUTTO'] - categories['ATP_ABZUG']
]]></field>
</record>
</odoo>
@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- ============================================================ -->
<!-- Structure-Type + Structure + Kategorien (Plan 4.2: EINE -->
<!-- Structure je Typ; Angestellte/Arbeiter laufen über die -->
<!-- Katalog-Mechanik lohn_basis + kv.wert, nicht über mehrere -->
<!-- Structures). -->
<!-- ============================================================ -->
<record id="structure_type_at_privat" model="hr.payroll.structure.type">
<field name="name">AT Privatwirtschaft (KV)</field>
<field name="default_schedule_pay">monthly</field>
</record>
<record id="structure_at_privat" model="hr.payroll.structure">
<field name="name">AT Privatwirtschaft Angestellte (KV)</field>
<field name="code">AT_PRIVAT</field>
<field name="type_id" ref="l10n_at_hr_payroll_private.structure_type_at_privat"/>
<field name="country_id" ref="base.at"/>
<field name="use_worked_day_lines" eval="True"/>
</record>
<!-- default_struct nach der Structure setzen (gleicher Record-Update) -->
<record id="structure_type_at_privat" model="hr.payroll.structure.type">
<field name="default_struct_id" ref="l10n_at_hr_payroll_private.structure_at_privat"/>
</record>
<!-- Kategorien (country-Konsistenz zur Structure: base.at) -->
<record id="category_atp_brutto" model="hr.salary.rule.category">
<field name="name">AT Privat Brutto (Entgelt)</field>
<field name="code">ATP_BRUTTO</field>
<field name="country_id" ref="base.at"/>
</record>
<record id="category_atp_abzug" model="hr.salary.rule.category">
<field name="name">AT Privat Abzüge (DN; GP3/GP4)</field>
<field name="code">ATP_ABZUG</field>
<field name="country_id" ref="base.at"/>
</record>
<record id="category_atp_netto" model="hr.salary.rule.category">
<field name="name">AT Privat Netto</field>
<field name="code">ATP_NETTO</field>
<field name="country_id" ref="base.at"/>
</record>
<record id="category_atp_dg" model="hr.salary.rule.category">
<field name="name">AT Privat Dienstgeber (ohne Nettoeffekt; GP4+)</field>
<field name="code">ATP_DG</field>
<field name="country_id" ref="base.at"/>
</record>
</odoo>
@@ -0,0 +1,106 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- ============================================================ -->
<!-- AT-Privatwirtschaft Work-Entry-Types (GP2). -->
<!-- Fenster-Matrix (Vorlauf 2026-09-11, drei Nachtfenster): -->
<!-- ÜStd-Zuschlag 100 % = 206 (§ 5 Abs 2 KV), Nacht-Sonder- -->
<!-- vergütung = 226 (§ 6 KV, via l10n_at_nacht_sv-Flag, kumu- -->
<!-- lativ zu allen Vergütungen), AZG-Nacht 225 (§ 12a) nur -->
<!-- statusrelevant — keine Zahlung. Klassifikation der 100 %- -->
<!-- Sonderbedingungen 2748 (3. ÜStd ab 19 Uhr, post-Nachtschicht,-->
<!-- 24./31. 12.) erfolgt stromauf in der Zeiterfassung; die -->
<!-- Regeln zahlen je Typ. -->
<!-- ============================================================ -->
<!-- Normalarbeitsstunden in der Nachtzeit 226 (KV § 6: €-Sondervergütung) -->
<record id="wett_att_nacht" model="hr.work.entry.type">
<field name="name">Nachtarbeit (NAZ 226 Uhr)</field>
<field name="color">1</field>
<field name="display_code">ATN</field>
<field name="code">ATP_ATT_NACHT</field>
<field name="country_id" ref="base.at"/>
<field name="l10n_at_nacht_sv" eval="True"/>
</record>
<!-- Überstunden -->
<record id="wett_ue_std" model="hr.work.entry.type">
<field name="name">Überstunde Tag (50 %)</field>
<field name="color">4</field>
<field name="display_code">UST</field>
<field name="code">ATP_UE_STD</field>
<field name="country_id" ref="base.at"/>
<field name="is_extra_hours" eval="True"/>
</record>
<record id="wett_ue_nacht" model="hr.work.entry.type">
<field name="name">Überstunde Nacht 226 Uhr (100 % + Sondervergütung)</field>
<field name="color">4</field>
<field name="display_code">UNA</field>
<field name="code">ATP_UE_NACHT</field>
<field name="country_id" ref="base.at"/>
<field name="is_extra_hours" eval="True"/>
<field name="l10n_at_nacht_sv" eval="True"/>
</record>
<record id="wett_ue_nacht_frueh" model="hr.work.entry.type">
<field name="name">Überstunde 2022 Uhr (100 %, ohne Sondervergütung)</field>
<field name="color">4</field>
<field name="display_code">UNF</field>
<field name="code">ATP_UE_NACHT_FRUEH</field>
<field name="country_id" ref="base.at"/>
<field name="is_extra_hours" eval="True"/>
</record>
<record id="wett_ue_sofei" model="hr.work.entry.type">
<field name="name">Sonn-/Feiertagsüberstunde Tag (100 %)</field>
<field name="color">4</field>
<field name="display_code">USF</field>
<field name="code">ATP_UE_SOFEI</field>
<field name="country_id" ref="base.at"/>
<field name="is_extra_hours" eval="True"/>
</record>
<record id="wett_ue_sofei_nacht" model="hr.work.entry.type">
<field name="name">Sonn-/Feiertagsüberstunde Nacht 226 (100 % + Sondervergütung)</field>
<field name="color">4</field>
<field name="display_code">USN</field>
<field name="code">ATP_UE_SOFEI_NACHT</field>
<field name="country_id" ref="base.at"/>
<field name="is_extra_hours" eval="True"/>
<field name="l10n_at_nacht_sv" eval="True"/>
</record>
<!-- Mehrarbeit: KV-Vollzeit-Band § 4a (2748: 38,5→40 h) -->
<record id="wett_mehr_vz" model="hr.work.entry.type">
<field name="name">Mehrarbeit Band KV-NAZ→40 h (§ 4a, 50 %)</field>
<field name="color">5</field>
<field name="display_code">MVZ</field>
<field name="code">ATP_MEHR_VZ</field>
<field name="country_id" ref="base.at"/>
<field name="is_extra_hours" eval="True"/>
</record>
<record id="wett_mehr_vz_nacht" model="hr.work.entry.type">
<field name="name">Mehrarbeit Band Nacht 226 (§ 4a, 50 % + Sondervergütung)</field>
<field name="color">5</field>
<field name="display_code">MVN</field>
<field name="code">ATP_MEHR_VZ_NACHT</field>
<field name="country_id" ref="base.at"/>
<field name="is_extra_hours" eval="True"/>
<field name="l10n_at_nacht_sv" eval="True"/>
</record>
<!-- Mehrarbeit Teilzeit § 19d AZG (25 % gesetzlich; KV-Abweichung möglich) -->
<record id="wett_mehr_tz" model="hr.work.entry.type">
<field name="name">Mehrarbeit Teilzeit (§ 19d AZG)</field>
<field name="color">5</field>
<field name="display_code">MTZ</field>
<field name="code">ATP_MEHR_TZ</field>
<field name="country_id" ref="base.at"/>
<field name="is_extra_hours" eval="True"/>
</record>
<record id="wett_mehr_tz_nacht" model="hr.work.entry.type">
<field name="name">Mehrarbeit Teilzeit Nacht 226 (§ 19d + Sondervergütung)</field>
<field name="color">5</field>
<field name="display_code">MTN</field>
<field name="code">ATP_MEHR_TZ_NACHT</field>
<field name="country_id" ref="base.at"/>
<field name="is_extra_hours" eval="True"/>
<field name="l10n_at_nacht_sv" eval="True"/>
</record>
</odoo>