Files
odoo-at-payroll/addons/l10n_at_gemeinde_payroll/tests/test_entgelt.py
T
fegger 0abc1383d1 [FIX] l10n_at_gemeinde_payroll: Modul erstmals installierbar — 9 Odoo-19-Latenzfehler; BDA-Vordienstzeiten-Vorschlag (AP10+AP9)
AP10 — Latenzfehler des nie installierten Vorlaufs:
- regular_pay auf hr.payroll.structure entfernt (Feld existiert nicht)
- is_paid/round_days/round_days_type von WET-Records entfernt
- Falscher XML-ID-Praefix payroll_structure_bgld. -> Modul-Referenzen
- amount_select 'python' -> 'code' (Selection)
- import in Regelcode entfernt (safe_eval verbietet IMPORT_NAME)
- rules['ME'].total -> result_rules['ME']['total'] (Odoo 19 befuellt
  rules nie; SZQ-Sonderzahlung rechnete gegen Null)
- categories.X -> categories['X'] (NETTO-Regel)
- work_time_rate /100 entfernt (Odoo 19 speichert Bruchteil 0..1)
- nie gelaufene Tests: Payslip-name-Pflichtfeld, Stichtag vor
  Katalog-Gueltigkeit (Wizard-Skip korrekt), date_start ist Compute
  aus contract_date_start

AP9 — BDA-Vordienstzeiten (GemBG S 67 Abs 1-3, RIS LrB GesNr
20001002 verifiziert): Feld vordienstzeiten_monate (Help dokumentiert
Anrechnungskatalog inkl. Obergrenzen), Compute bda_vorschlag
(Vertragsbeginn - Monate), Uebernahme-Button als Verwaltungsakt,
View-Erweiterung
2026-09-12 23:18:07 +02:00

118 lines
4.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Part of the gem360 onboarding. See LICENSE / project docs for details.
from datetime import date
from odoo.tests import TransactionCase, tagged
@tagged('post_install', '-at_install')
class EntgeltTest(TransactionCase):
"""AP2 Rechenfälle: Monatsentgelt, Kinderzulage, Funktionszulage,
Sonderzahlung (quartalsweise), Ausbildungsphase."""
def setUp(self):
super().setUp()
self.structure = self.env.ref(
'l10n_at_gemeinde_payroll.structure_gembg_bgld')
self.gruppe_av1 = self.env.ref(
'l10n_at_gemeinde_payroll.bgld_grp_av_av1')
self.parameter = self.env['hr.rule.parameter']
def _create_employee_with_payslip(self, month=None, **kwargs):
employee = self.env['hr.employee'].create({
'name': 'Test GemBG Entgelt',
})
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_vals = {
'entlohnungsgruppe_id': self.gruppe_av1.id,
'entlohnungsstufe': kwargs.get('stufe', 1),
}
version_vals.update(kwargs.get('version_vals', {}))
version.write(version_vals)
# Ausbildungsphase und Zulagen direkt auf Version
if 'ausbildungsphase' in kwargs:
version.ausbildungsphase = kwargs['ausbildungsphase']
if 'funktionszulage' in kwargs:
version.funktionszulage = kwargs['funktionszulage']
if 'kinder_anzahl' in kwargs:
version.kinder_anzahl = kwargs['kinder_anzahl']
slip_month = month or 7
slip = self.env['hr.payslip'].create({
'name': 'GemBG-Test %d/2026' % slip_month,
'employee_id': employee.id,
'struct_id': self.structure.id,
'date_from': date(2026, slip_month, 1),
'date_to': date(2026, slip_month, 28),
})
slip.compute_sheet()
return employee, slip
def _get_rule_total(self, slip, code):
line = slip.line_ids.filtered(
lambda l: l.code == code)
return line.total if line else 0.0
def test_01_monatsentgelt_av1_stufe1(self):
"""Vollzeit av1 Stufe 1 = 7.135,20 € (Wert 1.7.2026)."""
_, slip = self._create_employee_with_payslip(month=7)
self.assertAlmostEqual(
self._get_rule_total(slip, 'ME'), 7135.20, places=2)
def test_02_kinderzulage_ungekuerzt_bei_teilzeit(self):
"""Kinderzulage: 2 Kinder = 31,20 €, auch bei Teilzeit."""
_, slip = self._create_employee_with_payslip(
month=7, kinder_anzahl=2)
# 2 × 15,60 = 31,20
self.assertAlmostEqual(
self._get_rule_total(slip, 'KIZU'), 31.20, places=2)
def test_03_funktionszulage(self):
"""Funktionszulage: 200 € am Vertrag."""
_, slip = self._create_employee_with_payslip(
month=7, funktionszulage=200.0)
self.assertAlmostEqual(
self._get_rule_total(slip, 'FUZU'), 200.0, places=2)
def test_04_ausbildungsphase_5_prozent_abzug(self):
"""§ 133h: 5 % Abzug auf Monatsentgelt."""
_, slip = self._create_employee_with_payslip(
month=7, ausbildungsphase=True)
# 7.135,20 × 0,95 = 6.778,44
self.assertAlmostEqual(
self._get_rule_total(slip, 'ME'), 6778.44, places=2)
def test_05_sonderzahlung_quartalsende(self):
"""SZ in September (Q3-Ende) = 50 % × (ME + KIZU)."""
_, slip = self._create_employee_with_payslip(
month=9, kinder_anzahl=1)
me = self._get_rule_total(slip, 'ME')
kizu = self._get_rule_total(slip, 'KIZU')
sz = self._get_rule_total(slip, 'SZQ')
self.assertAlmostEqual(sz, (me + kizu) * 0.5, places=2)
def test_06_sonderzahlung_kein_quartalsende(self):
"""Kein SZ außerhalb der Quartalsendmonate."""
_, slip = self._create_employee_with_payslip(month=7)
self.assertEqual(self._get_rule_total(slip, 'SZQ'), 0.0)
def test_07_netto_ohne_abzuege(self):
"""R1: Netto = Brutto (AP3/AP4 Abzüge noch nicht implementiert)."""
_, slip = self._create_employee_with_payslip(
month=7, kinder_anzahl=1, funktionszulage=100.0)
brutto = self._get_rule_total(slip, 'ME') \
+ self._get_rule_total(slip, 'KIZU') \
+ self._get_rule_total(slip, 'FUZU')
netto = self._get_rule_total(slip, 'NETTO')
self.assertAlmostEqual(netto, brutto, places=2)
def test_08_stufe_12_max(self):
"""Stufe 12 von av1 = 8.712,50 €."""
_, slip = self._create_employee_with_payslip(month=7, stufe=12)
self.assertAlmostEqual(
self._get_rule_total(slip, 'ME'), 8712.50, places=2)