mirror of
http://100.103.83.12:3003/fegger/odoo-at-payroll.git
synced 2026-09-17 08:52:45 +00:00
[IMP] l10n_at_hr_payroll_private: add statutory old-system severance (AP15-A)
Pay the pre-2003 AngG severance from the end-of-employment run as its own neutral benefit line: free of SV (§ 49 Abs 3 Z 7 ASVG) and payroll levies, outside the annual sixth, and collected into the § 291d EO termination pot net of its own wage tax. Taxation follows the binding better-of rule of § 67 Abs 3 EStG: multiple-of-method (tariff wage tax on the running monthly wage × the statutory multiple of the payout) versus the flat 6 % rate, applied mandatorily in favour of the employee. The tariff wage tax is captured as a separately confirmed value instead of deriving it from gross pay through the § 62 engine. The wizard enforces § 23a Abs 3 AngG (parental exit needs five uninterrupted years), blocks BMSVG-new contracts except frozen claims and validates AGRD 02/06 against the claim type. The LNK basis subtracts § 67 Abs 3/6 rules only when they actually run in the tax base, the L16 generator reports LST_ABF in key 260 outside 210/220, and result_rules access in the garnishment paths avoids DefaultDictPayroll materialisation. Verified against the original statute texts in .ris/ (AngG §§ 23/23a, EStG § 67 Abs 3). Version 19.0.13.0.0 (private), 19.0.9.0.0 (core)
This commit is contained in:
@@ -5,6 +5,7 @@ Abgangsentschädigung (§ 67 Abs 8 lit b Fünftel), Aufrechnung offener
|
||||
Vorschüsse (§ 293 Abs 3 EO) und Zeitguthaben (§ 19e Abs 2 AZG)."""
|
||||
from datetime import date, timedelta
|
||||
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.tests import TransactionCase, tagged
|
||||
|
||||
|
||||
@@ -278,7 +279,8 @@ class EndabrechnungPrivateTest(TransactionCase):
|
||||
wizard = self.env['l10n.at.payroll.endabrechnung.wizard'].create({
|
||||
'employee_id': emp.id,
|
||||
'austrittsdatum': date(2026, 3, 15),
|
||||
'beendigungsart': 'einvernehmlich',
|
||||
'departure_reason_id': self.env.ref(
|
||||
'l10n_at_hr_payroll.departure_reason_einvernehmlich').id,
|
||||
'uel_tage': 11.0,
|
||||
'abgang_betrag': 9000.0,
|
||||
'abgang_ffrist_tage': 42.0,
|
||||
@@ -295,4 +297,185 @@ class EndabrechnungPrivateTest(TransactionCase):
|
||||
self.assertAlmostEqual(
|
||||
self._total(slip, 'URL_ERSATZ_LFD'), 1320.0, places=2)
|
||||
self.assertAlmostEqual(self._total(slip, 'ABGANG'), 9000.0,
|
||||
places=2)
|
||||
places=2)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# AP15-A: gesetzliche Abfertigung Alt (§§ 23/23a, § 67 Abs 3)
|
||||
# ------------------------------------------------------------------
|
||||
def test_07_abfertigung_alt_staffel_und_sonderfaelle(self):
|
||||
slip = self._slip(self._employee())
|
||||
for monate, erwartet in (
|
||||
(35, 0.0), (36, 2.0), (60, 3.0), (120, 4.0),
|
||||
(180, 6.0), (240, 9.0), (300, 12.0)):
|
||||
self.assertEqual(slip._at_abf_alt_staffel(monate), erwartet)
|
||||
self.assertEqual(
|
||||
slip._at_abf_alt_mehrfach_vorschlag(59, 'eltern'), 0.0)
|
||||
self.assertEqual(
|
||||
slip._at_abf_alt_mehrfach_vorschlag(60, 'eltern'), 1.5)
|
||||
self.assertEqual(
|
||||
slip._at_abf_alt_mehrfach_vorschlag(180, 'eltern'), 3.0)
|
||||
self.assertEqual(
|
||||
slip._at_abf_alt_mehrfach_vorschlag(240, 'tod'), 4.5)
|
||||
|
||||
def test_08_abfertigung_alt_vervielfachung_guenstiger(self):
|
||||
"""21 Jahre: 9 Monatsentgelte inklusive 2/12 SZ ergeben den
|
||||
steuerlichen Vervielfacher 10,5; der Tarif ist günstiger als 6 %."""
|
||||
emp_mit = self._employee(ueberzahlung=1800.0)
|
||||
emp_ohne = self._employee(ueberzahlung=1800.0)
|
||||
slip_mit = self._slip(emp_mit)
|
||||
slip_ohne = self._slip(emp_ohne)
|
||||
slip_mit.write({
|
||||
'l10n_at_abf_alt_anspruchsart': 'normal',
|
||||
'l10n_at_abf_alt_dienstzeit_monate': 252,
|
||||
'l10n_at_abf_alt_monatsentgelt': 2100.0,
|
||||
'l10n_at_abf_alt_laufender_arbeitslohn': 1800.0,
|
||||
'l10n_at_abf_alt_monatslohnsteuer': 101.96,
|
||||
'l10n_at_abf_alt_mehrfach': 9.0,
|
||||
'l10n_at_abf_alt_nachweis': 'Testnachweis',
|
||||
})
|
||||
self._input(slip_mit, 'ATP_ABF_ALT', 18900.0)
|
||||
|
||||
erwartet = 101.96 * 10.5
|
||||
self.assertAlmostEqual(self._total(slip_mit, 'ABF_ALT'), 18900.0,
|
||||
places=2)
|
||||
self.assertAlmostEqual(
|
||||
slip_mit._at_abf_alt_steuer_vervielfachung(), erwartet, places=2)
|
||||
self.assertLess(erwartet, 18900.0 * 0.06)
|
||||
self.assertAlmostEqual(
|
||||
self._total(slip_mit, 'LST_ABF'),
|
||||
self.env.company.currency_id.round(erwartet), places=2)
|
||||
self.assertAlmostEqual(
|
||||
slip_mit._l10n_at_sv_basis_roh()
|
||||
- slip_ohne._l10n_at_sv_basis_roh(), 0.0, places=2)
|
||||
self.assertAlmostEqual(
|
||||
slip_mit._at_lnk_basis('flaf')
|
||||
- slip_ohne._at_lnk_basis('flaf'), 0.0, places=2)
|
||||
self.assertAlmostEqual(
|
||||
slip_mit._at_bv_basis_sonst()
|
||||
- slip_ohne._at_bv_basis_sonst(), 0.0, places=2)
|
||||
self.assertAlmostEqual(
|
||||
slip_mit._l10n_at_sechstel() - slip_ohne._l10n_at_sechstel(),
|
||||
0.0, places=2)
|
||||
self.assertAlmostEqual(
|
||||
slip_mit._at_pfaendung_basis(None, 'beendigung'),
|
||||
18900.0 - self._total(slip_mit, 'LST_ABF'), places=2)
|
||||
|
||||
def test_09_abfertigung_alt_sechs_prozent_guenstiger(self):
|
||||
emp = self._employee(ueberzahlung=4000.0)
|
||||
slip = self._slip(emp)
|
||||
slip.write({
|
||||
'l10n_at_abf_alt_laufender_arbeitslohn': 4000.0,
|
||||
'l10n_at_abf_alt_monatslohnsteuer': 300.0,
|
||||
'l10n_at_abf_alt_mehrfach': 2.0,
|
||||
})
|
||||
self._input(slip, 'ATP_ABF_ALT', 9333.34)
|
||||
|
||||
self.assertGreater(
|
||||
slip._at_abf_alt_steuer_vervielfachung(), 9333.34 * 0.06)
|
||||
self.assertAlmostEqual(
|
||||
self._total(slip, 'LST_ABF'), 9333.34 * 0.06, places=2)
|
||||
|
||||
def test_10_abfertigung_alt_wizard(self):
|
||||
emp = self._employee(ueberzahlung=3000.0)
|
||||
version = emp.version_id
|
||||
version.write({
|
||||
'structure_type_id': self.env.ref(
|
||||
'l10n_at_hr_payroll_private.structure_type_at_privat').id,
|
||||
'l10n_at_abfertigung_neu': False,
|
||||
})
|
||||
reason = self.env.ref(
|
||||
'l10n_at_hr_payroll.departure_reason_einvernehmlich')
|
||||
wizard = self.env['l10n.at.payroll.endabrechnung.wizard'].create({
|
||||
'employee_id': emp.id,
|
||||
'austrittsdatum': date(2026, 3, 15),
|
||||
'departure_reason_id': reason.id,
|
||||
'abf_alt_aktiv': True,
|
||||
'abf_alt_anspruch_bestaetigt': True,
|
||||
'abf_alt_anspruchsart': 'normal',
|
||||
'abf_alt_dienstzeit_monate': 74,
|
||||
'abf_alt_monatsentgelt': 3500.0,
|
||||
'abf_alt_laufender_arbeitslohn': 3000.0,
|
||||
'abf_alt_monatslohnsteuer': 200.0,
|
||||
'abf_alt_mehrfach': 3.0,
|
||||
'abf_alt_nachweis': 'Dienstzeiten und Altstatus geprüft',
|
||||
})
|
||||
ergebnis = wizard.action_payslip()
|
||||
slip = self.env['hr.payslip'].browse(ergebnis['res_id'])
|
||||
|
||||
self.assertAlmostEqual(self._total(slip, 'ABF_ALT'), 10500.0,
|
||||
places=2)
|
||||
self.assertGreater(self._total(slip, 'LST_ABF'), 0.0)
|
||||
self.assertEqual(slip.l10n_at_abf_alt_dienstzeit_monate, 74)
|
||||
self.assertEqual(slip.l10n_at_abf_alt_nachweis,
|
||||
'Dienstzeiten und Altstatus geprüft')
|
||||
self.assertEqual(version.departure_reason_id, reason)
|
||||
|
||||
def test_11_abfertigung_alt_neu_sperre(self):
|
||||
emp = self._employee()
|
||||
emp.version_id.write({
|
||||
'structure_type_id': self.env.ref(
|
||||
'l10n_at_hr_payroll_private.structure_type_at_privat').id,
|
||||
'l10n_at_abfertigung_neu': True,
|
||||
})
|
||||
wizard = self.env['l10n.at.payroll.endabrechnung.wizard'].create({
|
||||
'employee_id': emp.id,
|
||||
'austrittsdatum': date(2026, 3, 15),
|
||||
'departure_reason_id': self.env.ref(
|
||||
'l10n_at_hr_payroll.departure_reason_einvernehmlich').id,
|
||||
'abf_alt_aktiv': True,
|
||||
'abf_alt_anspruch_bestaetigt': True,
|
||||
'abf_alt_anspruchsart': 'normal',
|
||||
'abf_alt_dienstzeit_monate': 74,
|
||||
'abf_alt_monatsentgelt': 3000.0,
|
||||
'abf_alt_laufender_arbeitslohn': 2640.0,
|
||||
'abf_alt_monatslohnsteuer': 150.0,
|
||||
'abf_alt_mehrfach': 3.0,
|
||||
})
|
||||
with self.assertRaises(UserError):
|
||||
wizard.action_payslip()
|
||||
|
||||
def test_12_abfertigung_alt_arbeitnehmerkuendigung_ohne_sonderfall(self):
|
||||
emp = self._employee()
|
||||
emp.version_id.write({
|
||||
'structure_type_id': self.env.ref(
|
||||
'l10n_at_hr_payroll_private.structure_type_at_privat').id,
|
||||
'l10n_at_abfertigung_neu': False,
|
||||
})
|
||||
wizard = self.env['l10n.at.payroll.endabrechnung.wizard'].create({
|
||||
'employee_id': emp.id,
|
||||
'austrittsdatum': date(2026, 3, 15),
|
||||
'departure_reason_id': self.env.ref('hr.departure_resigned').id,
|
||||
'abf_alt_aktiv': True,
|
||||
'abf_alt_anspruch_bestaetigt': True,
|
||||
'abf_alt_anspruchsart': 'normal',
|
||||
'abf_alt_dienstzeit_monate': 74,
|
||||
'abf_alt_monatsentgelt': 3000.0,
|
||||
'abf_alt_laufender_arbeitslohn': 2640.0,
|
||||
'abf_alt_monatslohnsteuer': 150.0,
|
||||
'abf_alt_mehrfach': 3.0,
|
||||
})
|
||||
with self.assertRaises(UserError):
|
||||
wizard.action_payslip()
|
||||
|
||||
def test_13_abfertigung_alt_elternaustritt_unter_fuenf_jahren(self):
|
||||
emp = self._employee()
|
||||
emp.version_id.write({
|
||||
'structure_type_id': self.env.ref(
|
||||
'l10n_at_hr_payroll_private.structure_type_at_privat').id,
|
||||
'l10n_at_abfertigung_neu': False,
|
||||
})
|
||||
wizard = self.env['l10n.at.payroll.endabrechnung.wizard'].create({
|
||||
'employee_id': emp.id,
|
||||
'austrittsdatum': date(2026, 3, 15),
|
||||
'departure_reason_id': self.env.ref('hr.departure_resigned').id,
|
||||
'abf_alt_aktiv': True,
|
||||
'abf_alt_anspruch_bestaetigt': True,
|
||||
'abf_alt_anspruchsart': 'eltern',
|
||||
'abf_alt_dienstzeit_monate': 59,
|
||||
'abf_alt_monatsentgelt': 3000.0,
|
||||
'abf_alt_laufender_arbeitslohn': 2640.0,
|
||||
'abf_alt_monatslohnsteuer': 150.0,
|
||||
'abf_alt_mehrfach': 1.0,
|
||||
})
|
||||
with self.assertRaises(UserError):
|
||||
wizard.action_payslip()
|
||||
|
||||
@@ -185,6 +185,30 @@ class PfaendungPrivateTest(TransactionCase):
|
||||
self.assertAlmostEqual(self._total(slip, 'PFAENDUNG'),
|
||||
pfaendbar, places=2)
|
||||
|
||||
def test_09b_pfaendung_regel_beendigung(self):
|
||||
"""AP15-A: Netto-Abfertigung läuft über den eigenen §-291d-Topf."""
|
||||
employee = self._employee(datetime.date(2020, 1, 1), unterhalt=0)
|
||||
self._pfaendung(employee, datetime.date(2026, 2, 1))
|
||||
slip = self._slip(employee, 3)
|
||||
slip.write({
|
||||
'l10n_at_abf_alt_laufender_arbeitslohn': 3000.0,
|
||||
'l10n_at_abf_alt_monatslohnsteuer': 200.0,
|
||||
'l10n_at_abf_alt_mehrfach': 3.0,
|
||||
})
|
||||
input_type = self.env.ref(
|
||||
'l10n_at_hr_payroll_private.input_type_atp_abf_alt')
|
||||
self.env['hr.payslip.input'].create({
|
||||
'payslip_id': slip.id,
|
||||
'input_type_id': input_type.id,
|
||||
'amount': 10500.0,
|
||||
})
|
||||
slip.compute_sheet()
|
||||
|
||||
masse = slip._at_pfaendung_masse(None, 'beendigung')
|
||||
self.assertGreater(masse, 0.0)
|
||||
self.assertAlmostEqual(
|
||||
self._total(slip, 'PFAENDUNG_END'), masse, 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))
|
||||
|
||||
Reference in New Issue
Block a user