a2a823ad35
AP4-Kernbau (D3-Vorfinanzierung, freigegebener GP4-Bauplan): Die Dienstgeberseite der Sozialversicherung wird sektorneutral im Kern gebaut, damit sowohl das General-AT-Produkt als auch die GemBG-Schiene sie konsumieren (keine Fork-Werte, payroll-Skill-Architekturregel). Neu in models/sozialversicherung.py: - SVDG laufend (ÖGK Normalbetriebe 2026: 20,98 %; Wien 21,23 %) und auf Sonderzahlungen (§ 54 ASVG: 20,48 % inkl. UV 1,10 und IESG 0,10); - Geringfügigkeit § 5 Abs 2 ASVG mit Hochrechnung bei untermonatiger Beschäftigung (SZ zählen nicht) und §-53a-Abs-1-UV als Monatspauschale; - beitragsfreie Bezüge § 49 Abs 3 ASVG als Jahres-Kumulative je DN (8 €/AT, 1.000/365/186/300/2.000 €/J, Homeoffice 3 €/Tag × 100); - altersabhängige Satzausschaltungen als Stichtags-Datumfelder am Vertrag: AlVG § 1 Abs 2 lit e (AV DN+DG und IESG), ASVG § 51 Abs 6 (UV ab 60/Lehrlinge), § 51 Abs 7 (PV in der Bonusphase je hälftig DG+DN), FLAG § 41 Abs 4 lit f (LNK-Freiheit ab 60); - ALV-DN-Staffel § 2a AMPFG, WF-Satzvektor Bundesland × Jahr (Wien 0,75/0,75 ab 1.1.2026, löst die KB-Protokollnotiz), Beitragsmonats- Allokation (Anspruchsprinzip: Sätze/HBG historisch je Gruppe) und DAG-Bewertung § 1 DAG (19,4 %, Schwelle 1,5 × GFG, ≥ 2 Geringfügige). Quellenverifikation (verbindlich): ÖGK-Beitragstarifsystem gültig ab 01.07.2026, TASY-LSWH-Export 2026-03-24, Beitragsrechtliche Werte 2026 und RIS-Normtexte. GFG 2026 = 551,10 € (Aufwertung ausgesetzt gem. § 810 Abs 3 ASVG — löst KB-Konflikt A7-Rest); §-19a-Selbstversicherung 2026 = 83,49 €/Monat, DG-UV daraus abgeleitet 6,50 €/Monat. Rechtskorrektur § 2a AMPFG: Die Staffel mindert nur den DN-Anteil (ÖGK-Abschläge 'Mind. AV auf 0/1/2 %') und gilt auch für SZ-Beiträge; bei 2.059,41 € BG sinkt DN-SV von pauschal 18,07 % auf 15,12 %. test_lohnsteuer wurde entsprechend angepasst (Rechenfälle RF-L1 ff.). Tests: test_sozialversicherung (14 Fälle); Suite 74/74 grün auf odoo_dev. Rechenfälle: l10n_at_hr_payroll_private/docs/rechenfaelle/ sozialversicherung.md.
400 lines
19 KiB
Python
400 lines
19 KiB
Python
# Part of the odoo-at-payroll project. License: LGPL-3.
|
||
"""GP4 SV/DB-Kern: DG-Beiträge (ÖGK 2026), SZ-Beiträge (§ 54 ASVG),
|
||
Geringfügigkeit (§ 5 Abs 2 iVm § 810 Abs 3 ASVG) + §-53a-UV,
|
||
beitragsfreie Bezüge § 49 Abs 3 mit Jahres-Kumulativen, Altersstichtage
|
||
(AlVG § 1 Abs 2 lit e, ASVG § 51 Abs 6/7, IESG § 12), ALV-DN-Staffel
|
||
(§ 2a AMPFG), Beitragsmonats-Allokation und DAG (§ 1 DAG). Rechenfälle
|
||
s. addons/l10n_at_hr_payroll_private/docs/rechenfaelle/
|
||
sozialversicherung.md und RECHTSQUELLEN-Privat Abschnitt 4."""
|
||
from datetime import date, timedelta
|
||
|
||
from odoo.tests import TransactionCase, tagged
|
||
|
||
|
||
@tagged('post_install', '-at_install')
|
||
class SozialversicherungTest(TransactionCase):
|
||
|
||
def setUp(self):
|
||
super().setUp()
|
||
self.category = self.env['hr.salary.rule.category'].create({
|
||
'name': 'Test AT SV',
|
||
'code': 'TEST_AT_SV',
|
||
})
|
||
self.structure_type = self.env['hr.payroll.structure.type'].create({
|
||
'name': 'Test AT Kern Type',
|
||
})
|
||
self.structure = self.env['hr.payroll.structure'].create({
|
||
'name': 'Test AT SV',
|
||
'code': 'TEST_AT_SV',
|
||
'type_id': self.structure_type.id,
|
||
})
|
||
|
||
def _struktur(self, code):
|
||
"""Eigene Structure je Testfall-Stufe (Regeln kumulieren auf
|
||
geteilten Structures — Muster test_lohnsteuer.py)."""
|
||
return self.env['hr.payroll.structure'].create({
|
||
'name': 'Test AT %s' % code,
|
||
'code': 'TEST_AT_%s' % code,
|
||
'type_id': self.structure_type.id,
|
||
})
|
||
|
||
def _regel(self, code, betrag, bemessung='laufend', sv49='keine',
|
||
beitragsmonat=None, quantity='1.0', struct=None):
|
||
return self.env['hr.salary.rule'].create({
|
||
'name': code,
|
||
'code': code,
|
||
'struct_id': (struct or self.structure).id,
|
||
'category_id': self.category.id,
|
||
'condition_select': 'none',
|
||
'l10n_at_bemessung': bemessung,
|
||
'l10n_at_sv49_frei': sv49,
|
||
'l10n_at_beitragsmonat': beitragsmonat,
|
||
'amount_select': 'fix',
|
||
'amount_fix': betrag,
|
||
'quantity': quantity,
|
||
})
|
||
|
||
def _employee(self, version_vals=None):
|
||
employee = self.env['hr.employee'].create({'name': 'Test SV'})
|
||
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)})
|
||
if version_vals:
|
||
version.write(version_vals)
|
||
return employee
|
||
|
||
def _slip(self, employee, monat, jahr=2026, confirm=False,
|
||
struct=None):
|
||
erster = date(jahr, monat, 1)
|
||
naechster = (erster + timedelta(days=32)).replace(day=1)
|
||
slip = self.env['hr.payslip'].create({
|
||
'name': 'Test-SV %d/%d' % (monat, jahr),
|
||
'employee_id': employee.id,
|
||
'struct_id': (struct or self.structure).id,
|
||
'date_from': erster,
|
||
'date_to': naechster - timedelta(days=1),
|
||
})
|
||
slip.compute_sheet()
|
||
if confirm:
|
||
slip.action_payslip_done()
|
||
return slip
|
||
|
||
# ------------------------------------------------------------------
|
||
# DG-Beiträge (ÖGK „Normalbetriebe" 2026: DG 20,98 % / Wien 21,23)
|
||
# ------------------------------------------------------------------
|
||
def test_01_svdg_gesamt(self):
|
||
"""DG-Summe 20,98 % = KV 3,78 + PV 12,55 + AV 2,95 + UV 1,10 +
|
||
WF 0,50 + IESG 0,10 auf laufender, HBG-gecappter Basis."""
|
||
employee = self._employee()
|
||
self._regel('LFD', 2059.41)
|
||
slip = self._slip(employee, 1)
|
||
self.assertAlmostEqual(
|
||
slip._l10n_at_svdg('kv'), 2059.41 * 0.0378, places=2)
|
||
self.assertAlmostEqual(
|
||
slip._l10n_at_svdg('pv'), 2059.41 * 0.1255, places=2)
|
||
self.assertAlmostEqual(
|
||
slip._l10n_at_svdg('av'), 2059.41 * 0.0295, places=2)
|
||
self.assertAlmostEqual(
|
||
slip._l10n_at_svdg('uv'), 2059.41 * 0.011, places=2)
|
||
self.assertAlmostEqual(
|
||
slip._l10n_at_svdg('wf'), 2059.41 * 0.005, places=2)
|
||
self.assertAlmostEqual(
|
||
slip._l10n_at_svdg('iesg'), 2059.41 * 0.001, places=2)
|
||
self.assertAlmostEqual(
|
||
slip._l10n_at_svdg_gesamt(), 2059.41 * 0.2098, places=2)
|
||
|
||
def test_02_svdg_wien(self):
|
||
"""WF-Wien 0,75 % DG ab 1.1.2026 (Satzvektor); 2025 nur 0,5 %."""
|
||
employee = self._employee()
|
||
self._regel('LFD', 2059.41)
|
||
slip = self._slip(employee, 1)
|
||
slip.company_id.write({'l10n_at_wf_wien': True})
|
||
self.assertAlmostEqual(
|
||
slip._l10n_at_svdg('wf'), 2059.41 * 0.0075, places=2)
|
||
self.assertAlmostEqual(
|
||
slip._l10n_at_svdg_gesamt(), 2059.41 * 0.2123, places=2)
|
||
# 2025: Vektor-Zeile ohne 'wien'-Eintrag → Bundesstandard 0,5 %
|
||
slip_2025 = self._slip(employee, 3, jahr=2025)
|
||
self.assertAlmostEqual(
|
||
slip_2025._l10n_at_svdg('wf'), 2059.41 * 0.005, places=2)
|
||
# HBG 2025 = 30 × 215 € = 6.450 € (KB A8, VO-Reihe § 108 Abs 3)
|
||
self.assertAlmostEqual(
|
||
slip_2025._l10n_at_svdn_basis_laufen(), 2059.41, places=2)
|
||
# Bundesland-Schlüssel wirkt identisch zum Company-Flag
|
||
slip.company_id.write(
|
||
{'l10n_at_wf_wien': False, 'l10n_at_bundesland': 'wien'})
|
||
self.assertAlmostEqual(
|
||
slip._l10n_at_svdg('wf'), 2059.41 * 0.0075, places=2)
|
||
|
||
def test_03_svdg_sz(self):
|
||
"""DG auf sonstigen Bezügen: KV/PV/AV/UV/IESG = 20,48 %
|
||
(§ 54 ASVG; HBG monatlich + SZ-Jahreshöchstbetrag 13.860)."""
|
||
employee = self._employee()
|
||
self._regel('LFD', 2059.41)
|
||
slip_okt = self._slip(employee, 10, confirm=True)
|
||
self._regel('SZ', 2059.41, bemessung='sonstig')
|
||
slip_nov = self._slip(employee, 11)
|
||
for komp, satz in (('kv', 0.0378), ('pv', 0.1255),
|
||
('av', 0.0295), ('uv', 0.011),
|
||
('iesg', 0.001)):
|
||
self.assertAlmostEqual(
|
||
slip_nov._l10n_at_svdg_sz(komp), 2059.41 * satz, places=2)
|
||
self.assertAlmostEqual(
|
||
slip_nov._l10n_at_svdg_sz_gesamt(), 2059.41 * 0.2048, places=2)
|
||
# Dezember: Jahresrest 13.860 − 2.059,41 lässt die zweite SZ zu
|
||
slip_dez = self._slip(employee, 12)
|
||
self.assertAlmostEqual(
|
||
slip_dez._l10n_at_svdn_basis_sz(), 2059.41, places=2)
|
||
# Deckel: SZ 20.000 im Januar 2027 (Jahresrest voll) → HBG 6.930
|
||
self._regel('SZ2', 20000.0, bemessung='sonstig')
|
||
slip_2027 = self._slip(employee, 1, jahr=2027)
|
||
self.assertAlmostEqual(
|
||
slip_2027._l10n_at_svdn_basis_sz(),
|
||
min(20000.0 + 2059.41, 6930.0, 13860.0), places=2)
|
||
|
||
# ------------------------------------------------------------------
|
||
# Geringfügigkeit (§ 5 Abs 2 ASVG; Aufwertung 2026 ausgesetzt) + § 53a
|
||
# ------------------------------------------------------------------
|
||
def test_04_geringfuegig_uv(self):
|
||
"""Laufendes Entgelt ≤ GFG 551,10 €: keine SVDN/SVDG; stattdessen
|
||
§-53a-Abs-1-UV 6,50 €/Monat (1,1 % der allgemeinen BG)."""
|
||
employee = self._employee()
|
||
self._regel('LFD', 500.0)
|
||
slip = self._slip(employee, 1)
|
||
self.assertTrue(slip._l10n_at_geringfuegig())
|
||
self.assertAlmostEqual(slip._l10n_at_svdn_gesamt(), 0.0, places=2)
|
||
self.assertAlmostEqual(slip._l10n_at_svdg_gesamt(), 0.0, places=2)
|
||
self.assertAlmostEqual(
|
||
slip._l10n_at_svdg_uv_geringfuegig(), 6.50, places=2)
|
||
self.assertTrue(slip.l10n_at_geringfuegig) # stored (DAG-Suche)
|
||
# Über der Grenze: Vollversicherung, kein §-53a-UV
|
||
employee_b = self._employee()
|
||
struktur = self._struktur('GFG2')
|
||
self._regel('LFD', 600.0, struct=struktur)
|
||
slip_b = self._slip(employee_b, 1, struct=struktur)
|
||
self.assertFalse(slip_b._l10n_at_geringfuegig())
|
||
self.assertAlmostEqual(
|
||
slip_b._l10n_at_svdg_uv_geringfuegig(), 0.0, places=2)
|
||
self.assertAlmostEqual(
|
||
slip_b._l10n_at_svdg_gesamt(), 600.0 * 0.2098, places=2)
|
||
|
||
def test_05_geringfuegig_hochrechnung(self):
|
||
"""Untermonatiger Beginn: Hochrechnung auf den Monat (lb-bes-06);
|
||
Verhältnis < 1 Naturalmonat: Gesamtentgelt direkt gegen die GFG."""
|
||
employee = self._employee(
|
||
{'contract_date_start': date(2026, 1, 16)})
|
||
self._regel('LFD', 400.0)
|
||
slip = self._slip(employee, 1)
|
||
# 400 × 30/16 = 750 > 551,10 → voll versichert; DN-AV 0 (§ 2a:
|
||
# 400 ≤ 2.225) → DN-Gesamt 15,12 %
|
||
self.assertFalse(slip._l10n_at_geringfuegig())
|
||
self.assertAlmostEqual(
|
||
slip._l10n_at_svdn_gesamt(), 400.0 * 0.1512, places=2)
|
||
# 250 × 30/16 = 468,75 ≤ 551,10 → geringfügig
|
||
struktur = self._struktur('HGR')
|
||
employee_b = self._employee(
|
||
{'contract_date_start': date(2026, 1, 16)})
|
||
self._regel('LFD', 250.0, struct=struktur)
|
||
slip_b = self._slip(employee_b, 1, struct=struktur)
|
||
self.assertTrue(slip_b._l10n_at_geringfuegig())
|
||
# auf unter einen Naturalmonat befristet: direkt gegen die GFG
|
||
employee_c = self._employee({
|
||
'contract_date_start': date(2026, 1, 16),
|
||
'contract_date_end': date(2026, 1, 25)})
|
||
struktur_c = self._struktur('KURZ')
|
||
self._regel('LFD', 500.0, struct=struktur_c)
|
||
slip_c = self._slip(employee_c, 1, struct=struktur_c)
|
||
self.assertTrue(slip_c._l10n_at_geringfuegig())
|
||
|
||
def test_06_lehrling(self):
|
||
"""Lehrling: keine AK-Umlage (§ 61 AKG), kein UV (§ 51 Abs 6
|
||
ASVG); nie geringfügig (§ 5 Abs 3 ASVG)."""
|
||
employee = self._employee({'l10n_at_lehrling': True})
|
||
self._regel('LFD', 300.0)
|
||
slip = self._slip(employee, 1)
|
||
self.assertFalse(slip._l10n_at_geringfuegig())
|
||
self.assertAlmostEqual(slip._l10n_at_svdn('ak'), 0.0, places=2)
|
||
self.assertAlmostEqual(slip._l10n_at_svdg('uv'), 0.0, places=2)
|
||
self.assertAlmostEqual(slip._l10n_at_svdg_uv_geringfuegig(), 0.0)
|
||
# KV/PV/WF regulär; AV 0 (§ 2a), AK 0, IESG 0 (§ 12 Abs 2 IESG):
|
||
# DN 14,62 % / DG 19,78 %
|
||
self.assertAlmostEqual(
|
||
slip._l10n_at_svdn_gesamt(), 300.0 * 0.1462, places=2)
|
||
self.assertAlmostEqual(
|
||
slip._l10n_at_svdg_gesamt(), 300.0 * 0.1978, places=2)
|
||
|
||
# ------------------------------------------------------------------
|
||
# § 49 Abs 3 ASVG — beitragsfreie Bezugsbestandteile (Kumulative)
|
||
# ------------------------------------------------------------------
|
||
def test_07_sv49_mahlzeit(self):
|
||
"""Mahlzeiten-Gutscheine 8 €/Arbeitstag (Z 12): 110 € auf
|
||
10 Arbeitstage → 80 € beitragsfrei, 30 € Umwidmung."""
|
||
employee = self._employee()
|
||
self._regel('LFD', 2059.41)
|
||
self._regel('MAH', 11.0, sv49='mahlzeit', quantity='10.0')
|
||
slip = self._slip(employee, 1)
|
||
self.assertAlmostEqual(
|
||
slip._l10n_at_sv49_freie_laufen(), 80.0, places=2)
|
||
basis = slip._l10n_at_sv_basis_roh()
|
||
self.assertAlmostEqual(basis, 2059.41 + 110.0 - 80.0, places=2)
|
||
# DN-AV 0 (§ 2a: Basis 2.089,41 ≤ 2.225) → DN-Gesamt 15,12 %
|
||
self.assertAlmostEqual(
|
||
slip._l10n_at_svdn_gesamt(), basis * 0.1512, places=2)
|
||
|
||
def test_08_sv49_jahrespools(self):
|
||
"""Jahres-Kumulative: Betriebsveranstaltung 365 €/Jahr und
|
||
Homeoffice-Pauschale 3 €/Tag max 100 Tage (YTD-Verbrauch)."""
|
||
employee = self._employee()
|
||
self._regel('LFD', 2059.41)
|
||
self._regel('BVER', 300.0, sv49='betriebsveranstaltung')
|
||
slip_a = self._slip(employee, 3, confirm=True)
|
||
self.assertAlmostEqual(
|
||
slip_a._l10n_at_sv49_freie_laufen(), 300.0, places=2)
|
||
# April: BVER-Rest 65 €; Homeoffice 70 Tage frei (3 × 70 = 210)
|
||
self._regel('HO', 3.0, sv49='homeoffice', quantity='70.0')
|
||
slip_b = self._slip(employee, 4, confirm=True)
|
||
self.assertAlmostEqual(
|
||
slip_b._l10n_at_sv49_freie_laufen(), 65.0 + 210.0, places=2)
|
||
# Mai: BVER-Pool verbraucht; Homeoffice-Rest 30 Tage → 90 €
|
||
slip_c = self._slip(employee, 5)
|
||
self.assertAlmostEqual(
|
||
slip_c._l10n_at_sv49_freie_laufen(), 90.0, places=2)
|
||
|
||
# ------------------------------------------------------------------
|
||
# Altersabhängige Satzausschaltungen (Stichtags-Regeln am Vertrag)
|
||
# ------------------------------------------------------------------
|
||
def test_09_alv_entfall(self):
|
||
"""§ 1 Abs 2 lit e AlVG: AV DN+DG und IESG entfallen ab
|
||
Stichtag (§ 12 Abs 1 Z 4 iVm Abs 2 IESG); Nachzahlungslinien mit
|
||
früherem Beitragsmonat zahlen weiter AV (Anspruchsprinzip)."""
|
||
employee = self._employee(
|
||
{'l10n_at_alv_entfall_ab': date(2026, 1, 1)})
|
||
self._regel('LFD', 2059.41)
|
||
slip = self._slip(employee, 1)
|
||
self.assertAlmostEqual(slip._l10n_at_svdn('av'), 0.0, places=2)
|
||
self.assertAlmostEqual(slip._l10n_at_svdg('av'), 0.0, places=2)
|
||
self.assertAlmostEqual(slip._l10n_at_svdg('iesg'), 0.0, places=2)
|
||
# DG ohne AV+IESG: 20,98 − 2,95 − 0,10 = 17,93 %
|
||
self.assertAlmostEqual(
|
||
slip._l10n_at_svdg_gesamt(), 2059.41 * 0.1793, places=2)
|
||
# DN ohne AV: 18,07 − 2,95 = 15,12 %
|
||
self.assertAlmostEqual(
|
||
slip._l10n_at_svdn_gesamt(), 2059.41 * 0.1512, places=2)
|
||
# Nachzahlungslinie Beitragsmonat 6/2025: AV greift weiter
|
||
self._regel('NACH', 1000.0, beitragsmonat=date(2025, 6, 1))
|
||
slip2 = self._slip(employee, 2)
|
||
self.assertAlmostEqual(
|
||
slip2._l10n_at_svdg('av'), 1000.0 * 0.0295, places=2)
|
||
|
||
def test_10_uv_entfall(self):
|
||
"""§ 51 Abs 6 ASVG: UV entfällt ab 60. LJ — laufend und SZ."""
|
||
employee = self._employee(
|
||
{'l10n_at_uv_entfall_ab': date(2026, 1, 1)})
|
||
self._regel('LFD', 2059.41)
|
||
self._regel('SZ', 2059.41, bemessung='sonstig')
|
||
slip = self._slip(employee, 1)
|
||
self.assertAlmostEqual(slip._l10n_at_svdg('uv'), 0.0, places=2)
|
||
self.assertAlmostEqual(slip._l10n_at_svdg_sz('uv'), 0.0, places=2)
|
||
self.assertAlmostEqual(
|
||
slip._l10n_at_svdg_gesamt(), 2059.41 * 0.1988, places=2)
|
||
|
||
def test_11_pv_bonusphase(self):
|
||
"""§ 51 Abs 7 ASVG: PV je zur Hälfte reduziert (DG und DN)."""
|
||
employee = self._employee(
|
||
{'l10n_at_pv_bonusphase_ab': date(2026, 1, 1)})
|
||
self._regel('LFD', 2059.41)
|
||
slip = self._slip(employee, 1)
|
||
self.assertAlmostEqual(
|
||
slip._l10n_at_svdn('pv'), 2059.41 * 0.05125, places=2)
|
||
self.assertAlmostEqual(
|
||
slip._l10n_at_svdg('pv'), 2059.41 * 0.06275, places=2)
|
||
|
||
# ------------------------------------------------------------------
|
||
# ALV-DN-Staffel (§ 2a AMPFG — reduziert nur den DN-Anteil)
|
||
# ------------------------------------------------------------------
|
||
def test_12_av_staffel(self):
|
||
"""Bis 2.225 €: DN-AV 0 %; bis 2.427: 1 %; bis 2.630: 2 % —
|
||
DG-AV bleibt stets 2,95 % (ÖGK „Mind. AV auf 0/1/2 %")."""
|
||
employee_a = self._employee()
|
||
struktur_a = self._struktur('ST1')
|
||
self._regel('LFD', 2000.0, struct=struktur_a)
|
||
slip_a = self._slip(employee_a, 1, struct=struktur_a)
|
||
self.assertAlmostEqual(slip_a._l10n_at_svdn('av'), 0.0, places=2)
|
||
self.assertAlmostEqual(
|
||
slip_a._l10n_at_svdg('av'), 2000.0 * 0.0295, places=2)
|
||
employee_b = self._employee()
|
||
struktur_b = self._struktur('ST2')
|
||
self._regel('LFD', 2500.0, struct=struktur_b)
|
||
slip_b = self._slip(employee_b, 1, struct=struktur_b)
|
||
self.assertAlmostEqual(
|
||
slip_b._l10n_at_svdn('av'), 2500.0 * 0.02, places=2)
|
||
self.assertAlmostEqual(
|
||
slip_b._l10n_at_svdg('av'), 2500.0 * 0.0295, places=2)
|
||
employee_c = self._employee()
|
||
struktur_c = self._struktur('ST3')
|
||
self._regel('LFD', 3000.0, struct=struktur_c)
|
||
slip_c = self._slip(employee_c, 1, struct=struktur_c)
|
||
self.assertAlmostEqual(
|
||
slip_c._l10n_at_svdn('av'), 3000.0 * 0.0295, places=2)
|
||
self.assertAlmostEqual(
|
||
slip_c._l10n_at_svdg('av'), 3000.0 * 0.0295, places=2)
|
||
|
||
# ------------------------------------------------------------------
|
||
# Beitragsmonats-Allokation (Anspruchsprinzip)
|
||
# ------------------------------------------------------------------
|
||
def test_13_beitragsmonat_hbg(self):
|
||
"""Nachzahlungslinie mit Beitragsmonat 2025: HBG historisch
|
||
versioniert (2025: 6.450 € statt 6.930 €), je Gruppe gecapped."""
|
||
employee = self._employee()
|
||
self._regel('LFD', 7000.0)
|
||
self._regel('NACH', 7000.0, beitragsmonat=date(2025, 6, 1))
|
||
slip = self._slip(employee, 1)
|
||
monate = dict(slip._l10n_at_sv_basis_laufen_monate())
|
||
self.assertAlmostEqual(
|
||
monate[date(2026, 1, 1)], 6930.0, places=2)
|
||
self.assertAlmostEqual(
|
||
monate[date(2025, 6, 1)], 6450.0, places=2)
|
||
self.assertAlmostEqual(
|
||
slip._l10n_at_svdg('kv'),
|
||
(6930.0 + 6450.0) * 0.0378, places=2)
|
||
|
||
# ------------------------------------------------------------------
|
||
# Dienstgeberabgabe geringfügig Beschäftigter (§ 1 DAG)
|
||
# ------------------------------------------------------------------
|
||
def test_14_dag(self):
|
||
"""DAG 19,4 %: Lohnsumme (BG ohne SZ) aller Geringfügigen >
|
||
826,65 € (= 1,5 × GFG 551,10, TASY MINGRDAGP1) und ≥ 2 Personen."""
|
||
employee_a = self._employee()
|
||
struktur_a = self._struktur('DAGA')
|
||
self._regel('LFD', 500.0, struct=struktur_a)
|
||
self._slip(employee_a, 1, confirm=True, struct=struktur_a)
|
||
employee_b = self._employee()
|
||
struktur_b = self._struktur('DAGB')
|
||
self._regel('LFD', 550.0, struct=struktur_b)
|
||
self._slip(employee_b, 1, confirm=True, struct=struktur_b)
|
||
company = self.env.company
|
||
dag = company._l10n_at_dag(date(2026, 1, 1))
|
||
self.assertAlmostEqual(dag['schwelle'], 826.65, places=2)
|
||
self.assertEqual(dag['anzahl'], 2)
|
||
self.assertAlmostEqual(dag['lohnsumme'], 1050.0, places=2)
|
||
self.assertAlmostEqual(dag['abgabe'], 1050.0 * 0.194, places=2)
|
||
# Zwei Geringfügige unter der Schwelle: keine DAG
|
||
employee_c = self._employee()
|
||
struktur_c = self._struktur('DAGC')
|
||
self._regel('LFD', 400.0, struct=struktur_c)
|
||
self._slip(employee_c, 2, confirm=True, struct=struktur_c)
|
||
employee_d = self._employee()
|
||
struktur_d = self._struktur('DAGD')
|
||
self._regel('LFD', 400.0, struct=struktur_d)
|
||
self._slip(employee_d, 2, confirm=True, struct=struktur_d)
|
||
dag_tief = company._l10n_at_dag(date(2026, 2, 1))
|
||
self.assertEqual(dag_tief['anzahl'], 2)
|
||
self.assertAlmostEqual(dag_tief['lohnsumme'], 800.0, places=2)
|
||
self.assertAlmostEqual(dag_tief['abgabe'], 0.0, places=2)
|
||
# Nur ein Geringfügiger: keine DAG
|
||
dag_einzel = company._l10n_at_dag(date(2026, 3, 1))
|
||
self.assertAlmostEqual(dag_einzel['abgabe'], 0.0, places=2) |