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 voluntary severance under § 67 Abs 6 (AP15-B)
Split the voluntary severance into its privileged part and a tariff remainder on the exit run: the privileged part combines the quarter rule (1/4 of the last twelve months' running pay, capped at nine times the monthly HBG) with the twelfth rule (2/12 to 12/12 by proven service, capped at n × 3 × HBG), reduced per Z 3 by severance already received and the statutory claim due at this termination, all taxed at the flat 6 % rate. The excess runs as its own rule flagged l10n_at_s67_abs6_ueberhang: taxed with the running tariff of the payment month but excluded from the annual sixth, since Z 5 exempts only the Z 1/Z 2 amounts and the excess is a special payment, not running pay. The twelve-month basis aggregates validated prior runs plus own lines, excluding the flagged excess and § 26 Z 4 travel overhang via a dedicated rule flag. Free of SV (§ 49 Abs 3 Z 7 ASVG), payroll levies regardless of the 6 %/tarif split (§ 41 Abs 4 lit b FLAG, § 122 Abs 8 WKG, § 5 Abs 2 lit b KommStG) and BV contributions; the former BV-Anwartschaft exclusion Z 7 is repealed (BGBl I 118/2015). The wizard proposes the rolling basis and the statutory claim, previews Z 1/Z 2/reduction/privileged/excess/tax and requires explicit confirmation against claim-conversion risk. The § 291d EO termination pot nets both severance wage-tax rules; key 260 now includes LST_ABF_FREI. Verified against the original statute text in .ris/ (EStG § 67 Abs 6) and the knowledge-base sources lb-end-12/13/15; KZ positioning of Abs-6 amounts and the excess's § 291d pot allocation remain documented verification points against LStR Rz 1087 ff. Version 19.0.14.0.0 (private), 19.0.10.0.0 (core)
This commit is contained in:
@@ -479,3 +479,135 @@ class EndabrechnungPrivateTest(TransactionCase):
|
||||
})
|
||||
with self.assertRaises(UserError):
|
||||
wizard.action_payslip()
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# AP15-B: freiwillige Abfertigung (§ 67 Abs 6)
|
||||
# ------------------------------------------------------------------
|
||||
def test_14_abf_frei_viertel_und_zwoelftel(self):
|
||||
"""15 Jahre (6/12), Basis 36.000: Z 1 9.000 + Z 2 18.000; Brutto
|
||||
12.000 → voll begünstigt, 6 % = 720; kein Überhang."""
|
||||
emp = self._employee()
|
||||
slip = self._slip(emp)
|
||||
slip.write({
|
||||
'l10n_at_abf_frei_basis_12': 36000.0,
|
||||
'l10n_at_abf_frei_dienstzeit_monate': 180,
|
||||
})
|
||||
self._input(slip, 'ATP_ABF_FREI', 12000.0)
|
||||
|
||||
split = slip._at_abf_frei_split()
|
||||
self.assertAlmostEqual(split['z1'], 9000.0, places=2)
|
||||
self.assertAlmostEqual(split['z2'], 18000.0, places=2)
|
||||
self.assertAlmostEqual(split['beguenstigt'], 12000.0, places=2)
|
||||
self.assertAlmostEqual(split['ueberhang'], 0.0, places=2)
|
||||
self.assertAlmostEqual(
|
||||
self._total(slip, 'ABF_FREI'), 12000.0, places=2)
|
||||
self.assertAlmostEqual(
|
||||
self._total(slip, 'LST_ABF_FREI'), 720.0, places=2)
|
||||
self.assertFalse(self._total(slip, 'ABF_FREI_TARIF'))
|
||||
|
||||
def test_15_abf_frei_ueberhang_tarif_ohne_sechstel(self):
|
||||
"""Brutto 40.000: begünstigt 27.000 (Z 1 + Z 2), Überhang 13.000
|
||||
läuft tarifisch über die LSt-Bemessung, ohne das Jahressechstel
|
||||
zu erhöhen; SV, LNK und BV bleiben unberührt."""
|
||||
emp_mit = self._employee()
|
||||
emp_ohne = self._employee()
|
||||
slip_mit = self._slip(emp_mit)
|
||||
slip_ohne = self._slip(emp_ohne)
|
||||
slip_mit.write({
|
||||
'l10n_at_abf_frei_basis_12': 36000.0,
|
||||
'l10n_at_abf_frei_dienstzeit_monate': 180,
|
||||
})
|
||||
self._input(slip_mit, 'ATP_ABF_FREI', 40000.0)
|
||||
|
||||
self.assertAlmostEqual(
|
||||
self._total(slip_mit, 'ABF_FREI'), 27000.0, places=2)
|
||||
self.assertAlmostEqual(
|
||||
self._total(slip_mit, 'ABF_FREI_TARIF'), 13000.0, places=2)
|
||||
lst6 = self.env.company.currency_id.round(27000.0 * 0.06)
|
||||
self.assertAlmostEqual(
|
||||
self._total(slip_mit, 'LST_ABF_FREI'), lst6, places=2)
|
||||
self.assertAlmostEqual(
|
||||
slip_mit._l10n_at_bemessung_laufen()
|
||||
- slip_ohne._l10n_at_bemessung_laufen(), 13000.0, places=2)
|
||||
self.assertGreater(
|
||||
self._total(slip_mit, 'LST_LFD'), self._total(slip_ohne, 'LST_LFD'))
|
||||
self.assertAlmostEqual(
|
||||
slip_mit._l10n_at_sechstel() - slip_ohne._l10n_at_sechstel(),
|
||||
0.0, 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._at_pfaendung_basis(None, 'beendigung'),
|
||||
27000.0 - lst6, places=2)
|
||||
|
||||
def test_16_abf_frei_kuerzung_und_deckel(self):
|
||||
"""Z 3: erhaltene Abfertigungen plus gesetzlicher Anspruch kürzen
|
||||
das Z-2-Ausmaß; bei großer Basis greifen die Deckel 9 × HBG
|
||||
(Z 1) und n × 3 × HBG (Z 2)."""
|
||||
emp = self._employee()
|
||||
slip = self._slip(emp)
|
||||
slip.write({
|
||||
'l10n_at_abf_frei_basis_12': 36000.0,
|
||||
'l10n_at_abf_frei_dienstzeit_monate': 180,
|
||||
'l10n_at_abf_frei_erhaltene_abfertigungen': 4000.0,
|
||||
'l10n_at_abf_frei_gesetzlicher_anspruch': 9000.0,
|
||||
})
|
||||
self._input(slip, 'ATP_ABF_FREI', 20000.0)
|
||||
|
||||
split = slip._at_abf_frei_split()
|
||||
self.assertAlmostEqual(split['kuerzung'], 13000.0, places=2)
|
||||
self.assertAlmostEqual(split['z2'], 5000.0, places=2)
|
||||
self.assertAlmostEqual(split['beguenstigt'], 14000.0, places=2)
|
||||
self.assertAlmostEqual(split['ueberhang'], 6000.0, places=2)
|
||||
|
||||
slip.write({
|
||||
'l10n_at_abf_frei_basis_12': 12000000.0,
|
||||
'l10n_at_abf_frei_erhaltene_abfertigungen': 0.0,
|
||||
'l10n_at_abf_frei_gesetzlicher_anspruch': 0.0,
|
||||
})
|
||||
slip.input_line_ids.filtered(
|
||||
lambda i: i.input_type_id.code == 'ATP_ABF_FREI').write(
|
||||
{'amount': 300000.0})
|
||||
slip.compute_sheet()
|
||||
split = slip._at_abf_frei_split()
|
||||
self.assertAlmostEqual(split['z1'], 62370.0, places=2)
|
||||
self.assertAlmostEqual(split['z2'], 124740.0, places=2)
|
||||
self.assertAlmostEqual(split['beguenstigt'], 187110.0, places=2)
|
||||
self.assertAlmostEqual(split['ueberhang'], 112890.0, places=2)
|
||||
|
||||
def test_17_abf_frei_wizard(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(
|
||||
'l10n_at_hr_payroll.departure_reason_einvernehmlich').id,
|
||||
'abf_frei_aktiv': True,
|
||||
'abf_frei_anspruch_bestaetigt': True,
|
||||
'abf_frei_betrag': 20000.0,
|
||||
'abf_frei_basis_12': 36000.0,
|
||||
'abf_frei_dienstzeit_monate': 180,
|
||||
'abf_frei_nachweis': 'Vordienstzeiten zum Lohnkonto nachgewiesen',
|
||||
})
|
||||
ergebnis = wizard.action_payslip()
|
||||
slip = self.env['hr.payslip'].browse(ergebnis['res_id'])
|
||||
|
||||
self.assertAlmostEqual(self._total(slip, 'ABF_FREI'), 20000.0,
|
||||
places=2)
|
||||
self.assertAlmostEqual(self._total(slip, 'LST_ABF_FREI'), 1200.0,
|
||||
places=2)
|
||||
self.assertEqual(slip.l10n_at_abf_frei_dienstzeit_monate, 180)
|
||||
self.assertEqual(slip.l10n_at_abf_frei_nachweis,
|
||||
'Vordienstzeiten zum Lohnkonto nachgewiesen')
|
||||
|
||||
@@ -209,6 +209,35 @@ class PfaendungPrivateTest(TransactionCase):
|
||||
self.assertAlmostEqual(
|
||||
self._total(slip, 'PFAENDUNG_END'), masse, places=2)
|
||||
|
||||
def test_09c_pfaendung_abf_frei_topf(self):
|
||||
"""AP15-B: begünstigter Teil der freiwilligen Abfertigung läuft
|
||||
in den §-291d-Topf, abzüglich beider Abfertigungs-LSt-Arten; der
|
||||
Überhang bleibt über die laufende Bemessung pfändbar."""
|
||||
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_frei_basis_12': 36000.0,
|
||||
'l10n_at_abf_frei_dienstzeit_monate': 180,
|
||||
})
|
||||
input_type = self.env.ref(
|
||||
'l10n_at_hr_payroll_private.input_type_atp_abf_frei')
|
||||
self.env['hr.payslip.input'].create({
|
||||
'payslip_id': slip.id,
|
||||
'input_type_id': input_type.id,
|
||||
'amount': 40000.0,
|
||||
})
|
||||
slip.compute_sheet()
|
||||
|
||||
lst6 = self.env.company.currency_id.round(27000.0 * 0.06)
|
||||
self.assertAlmostEqual(
|
||||
self._total(slip, 'ABF_FREI'), 27000.0, places=2)
|
||||
self.assertAlmostEqual(
|
||||
self._total(slip, 'ABF_FREI_TARIF'), 13000.0, places=2)
|
||||
self.assertAlmostEqual(
|
||||
slip._at_pfaendung_basis(None, 'beendigung'),
|
||||
27000.0 - lst6, 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