diff --git a/addons/l10n_at_hr_payroll_private/__manifest__.py b/addons/l10n_at_hr_payroll_private/__manifest__.py
index e08c155..a16300b 100644
--- a/addons/l10n_at_hr_payroll_private/__manifest__.py
+++ b/addons/l10n_at_hr_payroll_private/__manifest__.py
@@ -1,7 +1,7 @@
# Part of the odoo-at-payroll project. License: LGPL-3.
{
'name': 'Österreich - Privatwirtschaft Personalverrechnung (General-AT)',
- 'version': '19.0.2.0.0',
+ 'version': '19.0.3.0.0',
'author': 'ixsol / gem360',
'website': 'https://gem360.at',
'category': 'Human Resources/Payroll',
@@ -52,6 +52,7 @@ gem360-Brücke).
'data/work_entry_types_private.xml',
'data/input_types_private.xml',
'data/salary_rules_private.xml',
+ 'data/salary_rules_lst_private.xml',
'views/kv_views.xml',
],
'countries': ['at'],
diff --git a/addons/l10n_at_hr_payroll_private/data/salary_rules_lst_private.xml b/addons/l10n_at_hr_payroll_private/data/salary_rules_lst_private.xml
new file mode 100644
index 0000000..ff7bd62
--- /dev/null
+++ b/addons/l10n_at_hr_payroll_private/data/salary_rules_lst_private.xml
@@ -0,0 +1,107 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ SV-DN Krankenversicherung (§ 62 Z 1 iVm § 16 Abs 3)
+ SVDN_KV
+
+
+
+ python
+ result = payslip._l10n_at_svdn('kv', result_rules) > 0
+ code
+ neutral
+
+
+
+ SV-DN Pensionsversicherung (§ 62 Z 1 iVm § 16 Abs 3)
+ SVDN_PV
+
+
+
+ python
+ result = payslip._l10n_at_svdn('pv', result_rules) > 0
+ code
+
+
+
+ SV-DN Arbeitslosenversicherung (§ 62 Z 1 iVm § 16 Abs 3)
+ SVDN_AV
+
+
+
+ python
+ result = payslip._l10n_at_svdn('av', result_rules) > 0
+ code
+
+
+
+ AK-Umlage DN (§ 61 AKG; § 62 Z 3)
+ SVDN_AK
+
+
+
+ python
+ result = payslip._l10n_at_svdn('ak', result_rules) > 0
+ code
+
+
+
+ WF-Beitrag DN (§ 62 Z 5; Wien 0,75 % via Company-Flag)
+ SVDN_WF
+
+
+
+ python
+ result = payslip._l10n_at_svdn('wf', result_rules) > 0
+ code
+
+
+
+
+
+ Lohnsteuer laufend (§ 66, kumulativ mit Rollung)
+ LST_LFD
+
+
+
+ python
+ result = payslip._l10n_at_lst_laufend(result_rules) > 0
+ code
+
+
+
+ Lohnsteuer sonstige Bezüge (§ 67 Fixsätze, Aufrollung)
+ LST_SZ
+
+
+
+ python
+ result = payslip._l10n_at_lst_sonstige(result_rules) > 0
+ code
+
+
+
\ No newline at end of file
diff --git a/addons/l10n_at_hr_payroll_private/data/salary_rules_private.xml b/addons/l10n_at_hr_payroll_private/data/salary_rules_private.xml
index 41a3d88..e666bf6 100644
--- a/addons/l10n_at_hr_payroll_private/data/salary_rules_private.xml
+++ b/addons/l10n_at_hr_payroll_private/data/salary_rules_private.xml
@@ -19,6 +19,7 @@
python
result = bool(employee.kv_id and employee.kv_gruppe_id and (employee.ueberzahlung or employee.erfahrungsstufe))
+ laufend
code
python
result = payslip._at_nacht_sv_stunden() > 0
code
+ laufend
+ szfn
+
@@ -62,14 +67,17 @@ result = payslip._at_nacht_sv_stunden() * satz
python
result = 'ATP_UE_STD' in worked_days and worked_days['ATP_UE_STD'].number_of_hours > 0
code
+ laufend
+ ue_std
+
@@ -81,13 +89,17 @@ result = stunden * grund * (1 + zuschlag / 100.0)
python
result = 'ATP_UE_NACHT' in worked_days and worked_days['ATP_UE_NACHT'].number_of_hours > 0
code
+ laufend
+ szfn
+
@@ -99,13 +111,16 @@ result = stunden * grund * (1 + zuschlag / 100.0)
python
result = 'ATP_UE_NACHT_FRUEH' in worked_days and worked_days['ATP_UE_NACHT_FRUEH'].number_of_hours > 0
code
+ laufend
+ szfn
+
@@ -117,13 +132,16 @@ result = stunden * grund * (1 + zuschlag / 100.0)
python
result = 'ATP_UE_SOFEI' in worked_days and worked_days['ATP_UE_SOFEI'].number_of_hours > 0
code
+ laufend
+ szfn
+
@@ -135,14 +153,17 @@ result = stunden * grund * (1 + zuschlag / 100.0)
python
result = 'ATP_UE_SOFEI_NACHT' in worked_days and worked_days['ATP_UE_SOFEI_NACHT'].number_of_hours > 0
code
+ laufend
+ szfn
+
@@ -159,15 +180,18 @@ result = stunden * grund * (1 + zuschlag / 100.0)
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))
]]>
code
+ laufend
+ ue_std
+
50 %-Zuschlag sind ÜStd (§ 4a letzter Satz —
-# Klassifikation stromauf).
+# Normalstunde, 2748: ÷ 167); zählt nicht gegen das ÜStd-Ausmaß.
+# LSt: Differenzstunden-Zuschläge abgabenrechtlich wie ÜStd-Zuschläge
+# (LStR Rz 1146) → § 68 Abs 2-Bucket (Regel-Flag ue_std).
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)
+result = grund * (1 + zuschlag / 100.0)
+result_qty = worked_days['ATP_MEHR_VZ'].number_of_hours
]]>
@@ -181,11 +205,14 @@ result = stunden * grund * (1 + zuschlag / 100.0)
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))
]]>
code
+ laufend
+ ue_std
+
@@ -201,15 +228,18 @@ result = stunden * grund * (1 + zuschlag / 100.0)
python
result = 'ATP_MEHR_TZ' in worked_days and worked_days['ATP_MEHR_TZ'].number_of_hours > 0
code
+ laufend
@@ -221,12 +251,13 @@ result = stunden * grund * (1 + zuschlag / 100.0)
python
result = 'ATP_MEHR_TZ_NACHT' in worked_days and worked_days['ATP_MEHR_TZ_NACHT'].number_of_hours > 0
code
+ laufend
@@ -243,6 +274,7 @@ result = stunden * grund * (1 + zuschlag / 100.0)
+ laufend
code
python
result = 'ATP_PRAMIE' in inputs and inputs['ATP_PRAMIE'].amount
+ laufend
code
python
result = payslip._at_b13_faellig()
+ sonstig
code
python
result = payslip._at_b14_faellig()
+ sonstig
code
+ sonstig
code
2.615 ✓
+ pauschal → LST_SZ = (1.707,87 − 620) × 6 % = **65,27 €**.
+
+## RF-L2: Kumulative Rollung (Zuflussprinzip)
+
+- Januar LST_LFD 67,12; Februar: JBG unverändert → Soll kumuliert
+ 134,24 − bereits 67,12 = **67,12** — Korrekturen Vormonat
+ (Nachzahlung, Gehaltsänderung) rollen automatisch in den
+ Folgelauf (§ 66 kumulativ = Rollungsprinzip § 77 Abs 3).
+
+## RF-L3: Günstig-Aufrollung bei zweiter SZ
+
+- Okt (nur laufend), Nov B13 (LST_SZ 65,27 bezahlt), Dez B14
+ (Dezember-Fallback ohne Urlaub): Jahres-SZ-Bemessung
+ 2 × 1.707,87 = 3.415,74 ≤ Sechstel 4.118,82 → Soll-Jahr
+ (3.415,74 − 620) × 6 % = 167,74 − 65,27 = **LST_SZ(Dez) 102,47 €**
+ (Neuberechnung bei jeder SZ-Zahlung, lb-son-04).
+
+## RF-L4: Sechstel-Freigrenze (2.615 €) — SZ tarifisch
+
+- Laufend 500 €/Monat: Sechstel = 500 × 60/30 = 1.000 ≤ 2.615 →
+ B14 läuft tarifisch über LST_LFD (LST_SZ = 0).
+
+## RF-L5: Sechstel-Überschuss (§ 67 Abs 10)
+
+- Laufend 2.000 €/Monat (Sechstel 4.000), SZ 6.000 €:
+ SV-SZ = 6.000 × 17,07 % = 1.024,20 → Bemessung 4.975,80;
+ 4.000 im Sechstel → Fixsteuer (4.000 − 620) × 6 % = **202,80 €**;
+ Überschuss 975,80 läuft dem laufenden Arbeitslohn des
+ Auszahlungsmonats zu (LSt-lfd-Kette).
+
+## RF-L6: § 68-Freibeträge (Buckets über Regel-Flags)
+
+- **SZFN (Abs 1):** Zuschlagsanteile der 100 %-Zuschläge +
+ Nacht-€-Sondervergütung bis 400 € (600 € bei überwiegender
+ Nachtarbeit — Vertrags-Flag) steuerfrei.
+- **ÜStd (Abs 2 iVm § 124b Z 440 idF BGBl I 4/2026):** 50 %-Klasse
+ (UE_STD, MEHR_VZ-Differenzstunden, LStR Rz 1146): erste 15 ÜStd,
+ max 170 € (2026; ab 2027 10/120 — versioniert). Beispiel:
+ 2 ÜStd à 13,73 € + 50 % Zuschlag = 41,19 → davon 33,33 % = 13,73 €
+ steuerfrei. Teilzeit-Mehrarbeit § 19d: **kein** Freibetrag
+ (voll steuerpflichtig).
+- Feiertagsentgelt: § 124b Z 492/Z 494 (idF BGBl I 43/2026) —
+ Wirkungsabgrenzung GP6/Feiertagsengine.
+
+## RF-L7: Absetzbeträge 2026 (Reform — AN-AB entfallen)
+
+- Verkehrsabsetzbetrag **496 €** (Pendler-Variante 853 € bei
+ Einkommen ≤ 15.069 — R1-Option mit Pendlerdaten; Zuschlag 804 €
+ nur Veranlagung).
+- AVB/AVAB: Kind 1 **612 €**, 2 Kinder **828 €**, je weiteres
+ **+273 €** (Vertrags-Feld + Kinderzahl; AVB-Partnergrenze
+ 7.411 € mandantenseitig nachgewiesen).
+- Arbeitnehmerabsetzbetrag: **entfallen** (im Tarif integriert —
+ Steuerreform; die historische 60 %-Werbungskosten-Regelung war
+ ohnehin nur Veranlagungsmechanik: monatlich liefen SV-Beiträge
+ stets voll über § 62 Z 1 iVm § 16 Abs 3).
+
+## RF-L8: Nachzahlungs-/Aufrollungs-Workflow (GP3+GP8)
+
+- Unterjährige Korrektur = Nachzahlungslinie im Zuflussmonat —
+ kumulative Engine bucht die Differenz automatisch (kein Storno).
+- Echter Datenfehler: Odoo-Refund + Neuberechnung (YTD-Helfer
+ refundbewusst).
+- **SV je Beitragsmonat (Anspruchsprinzip)** mit historischen
+ versionierten Sätzen/HBG + **Nachtrags-mBGM** (Storno +
+ Neumeldung): GP4 (Allokation) + GP7 (ELDA-Export).
+- 15.2.-Cutoff (Vorjahres-L16 + berichtigter Lohnzettel),
+ Fünftelregelung § 67 Abs 8 lit c (`_l10n_at_fuenftelregelung`),
+ Willkür→Tarif: Datenmodell + Utility in GP3, Vollausbau GP8.
\ No newline at end of file
diff --git a/addons/l10n_at_hr_payroll_private/tests/__init__.py b/addons/l10n_at_hr_payroll_private/tests/__init__.py
index 36016c6..e8ae740 100644
--- a/addons/l10n_at_hr_payroll_private/tests/__init__.py
+++ b/addons/l10n_at_hr_payroll_private/tests/__init__.py
@@ -1,3 +1,4 @@
# Part of the odoo-at-payroll project. License: LGPL-3.
from . import test_kv_katalog
-from . import test_entgelt_private
\ No newline at end of file
+from . import test_entgelt_private
+from . import test_lst_private
\ No newline at end of file
diff --git a/addons/l10n_at_hr_payroll_private/tests/test_entgelt_private.py b/addons/l10n_at_hr_payroll_private/tests/test_entgelt_private.py
index 1fd3c30..82f846e 100644
--- a/addons/l10n_at_hr_payroll_private/tests/test_entgelt_private.py
+++ b/addons/l10n_at_hr_payroll_private/tests/test_entgelt_private.py
@@ -368,8 +368,9 @@ class EntgeltPrivateTest(TransactionCase):
# ------------------------------------------------------------------
# Netto / All-in
# ------------------------------------------------------------------
- def test_20_netto_ohne_abzuege(self):
- """R1: Netto = Brutto-Kategorien (AP3/AP4 folgen)."""
+ def test_20_netto_mit_dn_abzuegen(self):
+ """GP3: Netto = ATP_BRUTTO − ATP_ABZUG — SVDN + LSt laufen
+ real mit (AP3-Kern angebunden)."""
employee = self._employee(stufe=1)
slip = self._compute(employee, 11)
brutto = sum(
@@ -379,7 +380,12 @@ class EntgeltPrivateTest(TransactionCase):
'MEHR_VZ', 'MEHR_VZ_NACHT', 'MEHR_TZ',
'MEHR_TZ_NACHT', 'URL_UE', 'PRAM', 'B13', 'B14',
'B14_RUECK'))
- self.assertAlmostEqual(self._total(slip, 'NETTO'), brutto, places=2)
+ abzug = sum(
+ self._total(slip, code)
+ for code in ('SVDN_KV', 'SVDN_PV', 'SVDN_AV', 'SVDN_AK',
+ 'SVDN_WF', 'LST_LFD', 'LST_SZ'))
+ self.assertAlmostEqual(
+ self._total(slip, 'NETTO'), brutto - abzug, places=2)
def test_21_allin_deckungsrechnung(self):
"""§ 2g AVRAG: Jahresdeckung = 12 × Pauschale vs. geschuldete
diff --git a/addons/l10n_at_hr_payroll_private/tests/test_lst_private.py b/addons/l10n_at_hr_payroll_private/tests/test_lst_private.py
new file mode 100644
index 0000000..3b4c0ea
--- /dev/null
+++ b/addons/l10n_at_hr_payroll_private/tests/test_lst_private.py
@@ -0,0 +1,188 @@
+# Part of the odoo-at-payroll project. License: LGPL-3.
+"""GP3 Lohnsteuer-Integration auf der privatwirtschaftlichen
+Structure: End-to-End-Abrechnung mit SVDN/LST-Regeln, B13 als
+§-67-Bezug, Rollung, Günstig-Aufrollung, § 68-Freibetrag."""
+from datetime import date, datetime, timedelta
+
+from odoo.tests import TransactionCase, tagged
+
+
+@tagged('post_install', '-at_install')
+class LstPrivateTest(TransactionCase):
+
+ def setUp(self):
+ super().setUp()
+ self.structure = self.env.ref(
+ 'l10n_at_hr_payroll_private.structure_at_privat')
+ self.kv2203 = self.env.ref(
+ 'l10n_at_hr_payroll_private.kv_si_2203')
+ self.gruppe_ii = self.env.ref(
+ 'l10n_at_hr_payroll_private.kv2203_grp_ii')
+ self.calendar = self.env['resource.calendar'].create({
+ 'name': 'AT Test 40h Mo-Fr',
+ 'tz': 'Europe/Vienna',
+ 'hours_per_day': 8,
+ 'attendance_ids': [
+ (0, 0, {'name': 'Tag %d' % wd, 'dayofweek': str(wd),
+ 'hour_from': 8.0, 'hour_to': 16.0})
+ for wd in range(5)
+ ],
+ })
+ self.env.company.resource_calendar_id = self.calendar
+
+ def _employee(self):
+ employee = self.env['hr.employee'].create({
+ 'name': 'Test LSt privat',
+ })
+ version = self.env['hr.version'].search(
+ [('employee_id', '=', employee.id)], limit=1)
+ if not version:
+ version = self.env['hr.version'].create({
+ 'employee_id': employee.id,
+ 'date_start': date(2020, 1, 1)})
+ version.write({
+ 'contract_date_start': date(2020, 1, 1),
+ 'kv_id': self.kv2203.id,
+ 'kv_gruppe_id': self.gruppe_ii.id,
+ 'erfahrungsstufe': 1,
+ 'resource_calendar_id': self.calendar.id,
+ })
+ return employee
+
+ def _slip(self, employee, monat, work_entries=(), confirm=False):
+ erster = date(2026, monat, 1)
+ naechster = (erster + timedelta(days=32)).replace(day=1)
+ slip = self.env['hr.payslip'].create({
+ 'name': 'Test-LSt-Privat %d' % monat,
+ 'employee_id': employee.id,
+ 'struct_id': self.structure.id,
+ 'date_from': erster,
+ 'date_to': naechster - timedelta(days=1),
+ })
+ for wet, tag, stunden in work_entries:
+ self.env['hr.work.entry'].create({
+ 'name': 'Test-Eintrag',
+ 'version_id': slip.version_id.id,
+ 'employee_id': slip.employee_id.id,
+ 'work_entry_type_id': wet.id,
+ 'date': tag,
+ 'duration': stunden,
+ })
+ slip.compute_sheet()
+ if confirm:
+ slip.action_payslip_done()
+ return slip
+
+ def _total(self, slip, code):
+ return sum(slip.line_ids.filtered(
+ lambda l: l.code == code).mapped('total'))
+
+ def test_01_end_to_end_november_mit_b13(self):
+ """Vollmonat VG II Stufe 1 mit B13: SVDN 18,07 %, LSt-lfd
+ (§ 66: JBG = Bemessung × 12 − 192), B13 nach § 67 (Abs 12-SV,
+ 620-Freibetrag, 6 %)."""
+ employee = self._employee()
+ slip = self._slip(employee, 11)
+ self.assertAlmostEqual(self._total(slip, 'KVGEO'), 2059.41, places=2)
+ self.assertAlmostEqual(
+ self._total(slip, 'SVDN_KV'), 2059.41 * 0.0387, places=2)
+ self.assertAlmostEqual(
+ self._total(slip, 'SVDN_PV'), 2059.41 * 0.1025, places=2)
+ self.assertAlmostEqual(
+ self._total(slip, 'SVDN_AV'), 2059.41 * 0.0295, places=2)
+ self.assertAlmostEqual(
+ self._total(slip, 'SVDN_AK'), 2059.41 * 0.005, places=2)
+ self.assertAlmostEqual(
+ self._total(slip, 'SVDN_WF'), 2059.41 * 0.005, places=2)
+ self.assertAlmostEqual(self._total(slip, 'B13'), 2059.41, places=2)
+ # LSt-lfd: Bemessung 1686,50 → JBG 20.046,06 → (1301,41 − 496)/12
+ bem = 2059.41 - 2059.41 * 0.1807
+ jbg = bem * 12 - 132 - 60
+ erwartet_lfd = (max(0.0, 0.20 * (jbg - 13539.0) - 496.0)) / 12
+ self.assertAlmostEqual(
+ self._total(slip, 'LST_LFD'), erwartet_lfd, places=1)
+ # LSt-SZ (§ 67): 2059,41 − SV-SZ (17,07 %) − 620 → 6 %
+ sz_bem = 2059.41 - 2059.41 * 0.1707
+ erwartet_sz = (sz_bem - 620.0) * 0.06
+ self.assertAlmostEqual(
+ self._total(slip, 'LST_SZ'), erwartet_sz, places=1)
+ # Netto-Konsistenz: ATP_BRUTTO − ATP_ABZUG
+ brutto = (self._total(slip, 'KVGEO') + self._total(slip, 'B13'))
+ abzug = sum(
+ self._total(slip, code) for code in
+ ('SVDN_KV', 'SVDN_PV', 'SVDN_AV', 'SVDN_AK', 'SVDN_WF',
+ 'LST_LFD', 'LST_SZ'))
+ self.assertAlmostEqual(
+ self._total(slip, 'NETTO'), brutto - abzug, places=2)
+
+ def test_02_rollung_zweiter_monat(self):
+ """Kumulative §-66-Rollung: Februar zahlt die Differenz."""
+ employee = self._employee()
+ slip_jan = self._slip(employee, 1, confirm=True)
+ jan = self._total(slip_jan, 'LST_LFD')
+ slip_feb = self._slip(employee, 2)
+ feb = self._total(slip_feb, 'LST_LFD')
+ self.assertAlmostEqual(feb, jan, places=2)
+
+ def test_03_guenstig_aufrollung_b14(self):
+ """B13 November + B14 Dezember (ohne Urlaub): zweite SZ rechnet
+ die Jahres-SZ-Steuer neu (Günstig-Aufrollung, lb-son-04)."""
+ employee = self._employee()
+ self._slip(employee, 10, confirm=True)
+ slip_nov = self._slip(employee, 11, confirm=True)
+ sz_nov = self._total(slip_nov, 'LST_SZ')
+ sz_bem = 2059.41 - 2059.41 * 0.1707
+ self.assertAlmostEqual(sz_nov, (sz_bem - 620.0) * 0.06, places=1)
+ slip_dez = self._slip(employee, 12)
+ self.assertAlmostEqual(
+ self._total(slip_dez, 'B14'), 2059.41, places=2)
+ soll_jahr = (2 * sz_bem - 620.0) * 0.06
+ self.assertAlmostEqual(
+ self._total(slip_dez, 'LST_SZ'), soll_jahr - sz_nov, places=1)
+
+ def test_04_ue_std_68_freibetrag(self):
+ """2 ÜStd Tag: UE_STD 41,19 (quantity=Stunden); §-68-Anteil
+ 13,73 mindert die LSt-Bemessung (§ 68 Abs 2 iVm § 124b Z 440)."""
+ employee = self._employee()
+ wet = self.env.ref('l10n_at_hr_payroll_private.wett_ue_std')
+ slip_ohne = self._slip(employee, 10, confirm=True)
+ lst_ohne = self._total(slip_ohne, 'LST_LFD')
+ slip_mit = self._slip(employee, 11, work_entries=[
+ (wet, date(2026, 11, 5), 2.0),
+ ])
+ self.assertAlmostEqual(self._total(slip_mit, 'UE_STD'), 41.19, places=2)
+ freibe = slip_mit._l10n_at_freibetraege_68()
+ self.assertAlmostEqual(freibe['ue_std'], 41.19 * 0.33333, places=1)
+ # LSt steigt (nur der nicht befreite Anteil wird besteuert)
+ self.assertGreater(self._total(slip_mit, 'LST_LFD'), lst_ohne)
+
+ def test_05_wien_wf(self):
+ """Wiener WF-Zuschlag: SVDN_WF 0,75 % statt 0,50 %."""
+ employee = self._employee()
+ slip = self._slip(employee, 11)
+ slip.company_id.write({'l10n_at_wf_wien': True})
+ slip.compute_sheet()
+ self.assertAlmostEqual(
+ self._total(slip, 'SVDN_WF'), 2059.41 * 0.0075, places=2)
+
+ def test_06_2748_november_basis_b13_dezember(self):
+ """2748: B13 im Dezember mit November-Basis; B14-Dezember-
+ fallback ohnt Urlaub läuft mit — §-67-Bemessung über beide
+ Bezüge (Abs 12-SV vorher)."""
+ employee = self._employee()
+ version = self.env['hr.version'].search(
+ [('employee_id', '=', employee.id)], limit=1)
+ version.write({
+ 'kv_id': self.env.ref(
+ 'l10n_at_hr_payroll_private.kv_si_2748').id,
+ 'kv_gruppe_id': self.env.ref(
+ 'l10n_at_hr_payroll_private.kv2748_grp_ii').id,
+ })
+ slip = self._slip(employee, 12)
+ self.assertAlmostEqual(self._total(slip, 'KVGEO'), 2199.15, places=2)
+ self.assertAlmostEqual(self._total(slip, 'B13'), 2199.15, places=2)
+ # B14-Dezember-Fallback (kein Urlaub im Jahr) — läuft mit
+ self.assertAlmostEqual(self._total(slip, 'B14'), 2199.15, places=2)
+ sz_bem = 2 * 2199.15 - 2 * 2199.15 * 0.1707
+ self.assertAlmostEqual(
+ self._total(slip, 'LST_SZ'), (sz_bem - 620.0) * 0.06, places=1)
\ No newline at end of file