[IMP] l10n_at_hr_payroll_private: add premiums at termination under § 16 AngG (AP14-B)

Close the premium-at-termination gap of the end-of-employment cycle: an aliquote periodic premium (annual, sales, bonus, 15th payment) whose entitlement period outlasts the termination is covered by § 16 AngG — the pro-rata amount follows the ratio of the elapsed service to the entitlement period, is due for employees regardless of the termination ground (loss clauses void, 9 ObA 82/13v), while performance-linked premiums for workers may validly hinge on non-fault grounds and premiums tied to a specific service (e.g. Bilanzgeld) are not aliquote without the service (lb-end-18). Two routes: PRAM_END (bemessung sonstig) covers premiums paid with the end-of-employment run — a special payment inside the annual sixth at the flat 6 % rate with the 620 EUR free amount and 2 615 EUR free threshold checked by the core, SV as special-payment contributions (KV/PV/AV without AK/WF) against the monthly HBG and the annual SZ cap, fully payroll-levy liable and BV-contributory at the uncapped 1.53 % rate (§ 6 Abs 1 iVm Abs 5 BMSVG). PRAM_END_TARIF (bemessung laufend plus the core l10n_at_s67_abs6_ueberhang flag) covers premiums due at a pre-agreed maturity because their amount only becomes determinable after the entitlement period ends (e.g. balance-sheet premiums): taxed like running pay with the payment-month tariff without touching the annual sixth (§ 67 Abs 10, not a § 67 Abs 8 lit c case per 98/14/0009 = ARD 5483/14/2004) — the SV contribution month rolls back into the termination year via a new payslip field consumed by an extended branch of the existing SV-basis-by-Beitragsmonat override (historical caps and rates, E-MVB 044-01-00-006), while the LSt stays in the payment month. The wizard section previews the § 16 pro-rata amount from the full premium, entitlement period and elapsed service, keeps the payable amount confirmable (KB agreement), requires the service/claim-basis confirmation, enforces the contribution month for the deferred case, and lands the memo on the payslip for the separate Lohnzettel workflow (LStR Rz 911a); the existing manual PRAM rule is untouched. The account matrix posts both rules to 6230. Verified against the original statute text in .ris/ (AngG § 16, BMSVG § 6 Abs 5) and the knowledge-base source lb-end-18; the full SZ roll-forward (storno plus recomputation against the termination year's SZ annual cap) and the mBGM storno/re-transmission remain open in the GP7 payroll-reporting track, and the BV attribution of the deferred premium remains in the payment month as a documented approximation.

Version 19.0.16.0.0 (private)
This commit is contained in:
2026-09-15 13:33:49 +02:00
parent 472b19a4b2
commit 850e99df20
10 changed files with 499 additions and 9 deletions
@@ -183,6 +183,62 @@ class L10nAtEndabrechnungWizard(models.TransientModel):
help='Vergleichstext, strittige Ansprüche, erkennbare '
'Abfertigungsanteile, BV-Zeiträume und allfälliger '
'Verhältnisfaktor dokumentieren.')
pram_aktiv = fields.Boolean(
string='Prämie bei Beendigung (§ 16 AngG)',
help='Aliquote periodische Prämie/Sonderzahlung (Jahres-, '
'Umsatzprämie, Bonifikation, 15. Bezug), deren Anspruchs-'
'zeitraum bei der Beendigung noch nicht abgelaufen ist '
'(lb-end-18).')
pram_zahlungsart = fields.Selection(
[('mit_endabrechnung',
'Mit der Endabrechnung ausgezahlt (sonstiger Bezug, '
'Jahressechstel)'),
('spaeter_faellig',
'Später fällig (§ 67 Abs 10 — Tarif im Zahlungsmonat, '
'ohne Sechstel; gesonderter Lohnzettel)')],
string='Zahlungs-/Fälligkeitsart', default='mit_endabrechnung',
help='Wird die Prämie erst zum im Vorhinein vereinbarten '
'Fälligkeitszeitpunkt fällig, weil ihre Höhe erst nach '
'Ablauf der Anspruchsperiode ermittelbar ist (z. B. '
'Bilanzprämie), liegt keine Nachzahlung vor und läuft die '
'Besteuerung über § 67 Abs 10 (lb-end-18).')
pram_anspruchszeitraum_monate = fields.Integer(
string='Anspruchszeitraum (Monate)', default=12,
help='Dienstperiode, für die die Prämie gewährt wird — z. B. '
'Kalenderjahr = 12 Monate (§ 16 AngG).')
pram_dienstzeit_monate = fields.Integer(
string='Zurückgelegte Dienstzeit im Anspruchszeitraum (Monate)',
help='Grundlage der §-16-Aliquot-Vorschau; der tatsächliche '
'Betrag folgt der KV-/Vertragsvereinbarung und bleibt '
'bestätigbar.')
pram_voller_betrag = fields.Monetary(
string='Voller Prämien-Betrag (vor Aliquotierung)',
currency_field='currency_id',
help='Betrag für die gesamte Anspruchsperiode — Basis der '
'§-16-Aliquot-Vorschau.')
pram_betrag = fields.Monetary(
string='Zu zahlender (aliquote) Brutto-Betrag',
currency_field='currency_id')
pram_beitragsmonat = fields.Date(
string='SV-Beitragsmonat (Rollung ins Austrittsjahr)',
help='Nur bei späterer Fälligkeit: Beitragsmonat, dem die '
'Prämie sv-rechtlich zuzuordnen ist — der Zeitraum, in dem '
'die Leistungen erbracht wurden (E-MVB 044-01-00-006). '
'Die mBGM-Storno-/Neuübermittlung erfolgt im GP7-Track.')
pram_leistung_bestaetigt = fields.Boolean(
string='Leistungs-/Anspruchsgrundlage bestätigt',
help='Bestätigung, dass die Prämie nicht an eine noch nicht '
'erbrachte spezifische Leistung gebunden ist (z. B. '
'Bilanzgeld — kein aliquoter Anspruch ohne Tätigkeit) und '
'allfällige leistungsabhängige Verlustklauseln (nur '
'Arbeiter, wirksam nur bei schuldhaftem Beendigungsgrund) '
'geprüft sind. Für Angestellte gebührt die aliquote Prämie '
'unabhängig von der Art der Beendigung (§ 16 AngG).')
pram_nachweis = fields.Text(
string='Nachweis/Begründung',
help='KV-/Vertragsvereinbarung, Anspruchszeitraum, '
'Aliquotierung, Fälligkeitszeitpunkt und '
'Leistungsverknüpfung dokumentieren.')
currency_id = fields.Many2one(
'res.currency', default=lambda self: self.env.company.currency_id)
@@ -240,6 +296,9 @@ class L10nAtEndabrechnungWizard(models.TransientModel):
vergleich_bv_lst_vorschau = fields.Monetary(
string='LSt 6 % BV-Teil (Vorschau)', currency_field='currency_id',
compute='_compute_vorschau')
pram_aliquot_vorschau = fields.Monetary(
string='§-16-Aliquote Prämie (Vorschau)',
currency_field='currency_id', compute='_compute_vorschau')
@api.onchange('employee_id')
def _onchange_employee(self):
@@ -253,6 +312,31 @@ class L10nAtEndabrechnungWizard(models.TransientModel):
wizard._at_abf_alt_vorschlag_setzen()
wizard._at_abf_frei_vorschlag_setzen()
@api.onchange('pram_aktiv', 'pram_voller_betrag',
'pram_dienstzeit_monate', 'pram_anspruchszeitraum_monate')
def _onchange_pram(self):
for wizard in self:
if not wizard.pram_aktiv:
continue
aliquote = wizard._at_pram_aliquot()
if aliquote and not wizard.pram_betrag:
wizard.pram_betrag = aliquote
def _at_pram_aliquot(self):
"""§ 16 AngG (Originalwortlaut): Betrag, der dem Verhältnis
zwischen der Dienstperiode des Anspruchs und der zurückgelegten
Dienstzeit entspricht."""
self.ensure_one()
if (not self.pram_anspruchszeitraum_monate
or self.pram_anspruchszeitraum_monate < 0
or self.pram_dienstzeit_monate < 0
or not self.pram_voller_betrag):
return 0.0
return (self.pram_voller_betrag
* min(self.pram_dienstzeit_monate,
self.pram_anspruchszeitraum_monate)
/ self.pram_anspruchszeitraum_monate)
@api.onchange('austrittsdatum', 'abf_alt_anspruchsart',
'abf_alt_dienstzeit_monate', 'abf_alt_aktiv',
'abf_alt_monatsentgelt', 'abf_alt_mehrfach',
@@ -306,7 +390,10 @@ class L10nAtEndabrechnungWizard(models.TransientModel):
'abf_frei_erhaltene_abfertigungen',
'abf_frei_gesetzlicher_anspruch',
'vergleich_aktiv', 'vergleich_rest_betrag',
'vergleich_bv_betrag')
'vergleich_bv_betrag',
'pram_aktiv', 'pram_voller_betrag',
'pram_dienstzeit_monate',
'pram_anspruchszeitraum_monate')
def _compute_vorschau(self):
for wizard in self:
wizard.uel_lfd_vorschau = 0.0
@@ -327,6 +414,7 @@ class L10nAtEndabrechnungWizard(models.TransientModel):
wizard.vergleich_fuenftel_frei_vorschau = 0.0
wizard.vergleich_tarif_basis_vorschau = 0.0
wizard.vergleich_bv_lst_vorschau = 0.0
wizard.pram_aliquot_vorschau = 0.0
if not wizard.employee_id or not wizard.austrittsdatum:
continue
slip = wizard._vorschau_payslip()
@@ -369,6 +457,7 @@ class L10nAtEndabrechnungWizard(models.TransientModel):
slip._at_fuenftel_tarif_basis()
wizard.vergleich_bv_lst_vorschau = \
slip._at_vergleich_bv_lst_6()
wizard.pram_aliquot_vorschau = wizard._at_pram_aliquot()
def _vorschau_payslip(self):
"""Flüchtiger Payslip-Kontext für die Vorschau-Berechnung (kein
@@ -405,6 +494,7 @@ class L10nAtEndabrechnungWizard(models.TransientModel):
self.abf_frei_gesetzlicher_anspruch,
'l10n_at_abf_frei_nachweis': self.abf_frei_nachweis,
'l10n_at_vergleich_nachweis': self.vergleich_nachweis,
'l10n_at_pram_nachweis': self.pram_nachweis,
})
eingaben = [
('ATP_UEL_TAGE', self.uel_tage),
@@ -416,6 +506,12 @@ class L10nAtEndabrechnungWizard(models.TransientModel):
if self.vergleich_aktiv else 0.0),
('ATP_VERGLEICH_BV', self.vergleich_bv_betrag
if self.vergleich_aktiv else 0.0),
('ATP_PRAM_END', self.pram_betrag
if self.pram_aktiv
and self.pram_zahlungsart == 'mit_endabrechnung' else 0.0),
('ATP_PRAM_END_TARIF', self.pram_betrag
if self.pram_aktiv
and self.pram_zahlungsart == 'spaeter_faellig' else 0.0),
]
slip.input_line_ids = [
(0, 0, {
@@ -444,6 +540,12 @@ class L10nAtEndabrechnungWizard(models.TransientModel):
if self.vergleich_aktiv else 0.0),
('ATP_VERGLEICH_BV', self.vergleich_bv_betrag
if self.vergleich_aktiv else 0.0),
('ATP_PRAM_END', self.pram_betrag
if self.pram_aktiv
and self.pram_zahlungsart == 'mit_endabrechnung' else 0.0),
('ATP_PRAM_END_TARIF', self.pram_betrag
if self.pram_aktiv
and self.pram_zahlungsart == 'spaeter_faellig' else 0.0),
]
def _abf_alt_validieren(self):
@@ -535,6 +637,34 @@ class L10nAtEndabrechnungWizard(models.TransientModel):
'Zeiträume voraus (§ 67 Abs 8 lit a letzter '
'Satz).'))
def _pram_validieren(self):
self.ensure_one()
if not self.pram_aktiv:
return
if not self.pram_leistung_bestaetigt:
raise UserError(_('Bitte die Leistungs-/Anspruchsgrundlage '
'der Prämie bestätigen (lb-end-18).'))
if self.pram_betrag <= 0:
raise UserError(_('Der zu zahlende Prämien-Betrag muss '
'größer als null sein.'))
if (self.pram_anspruchszeitraum_monate < 0
or self.pram_dienstzeit_monate < 0
or self.pram_voller_betrag < 0):
raise UserError(_('Anspruchszeitraum, Dienstzeit und '
'voller Betrag dürfen nicht negativ '
'sein.'))
if (self.pram_anspruchszeitraum_monate
and self.pram_dienstzeit_monate
> self.pram_anspruchszeitraum_monate):
raise UserError(_('Die zurückgelegte Dienstzeit kann den '
'Anspruchszeitraum nicht übersteigen.'))
if (self.pram_zahlungsart == 'spaeter_faellig'
and not self.pram_beitragsmonat):
raise UserError(_('Bei späterer Fälligkeit ist der SV-'
'Beitragsmonat der Rollung ins '
'Austrittsjahr erforderlich '
'(E-MVB 044-01-00-006).'))
def action_payslip(self):
"""Endabrechnungs-Lauf im Austrittsmonat anlegen und die
gesammelten offenen Posten als Eingaben vorbefüllen."""
@@ -542,6 +672,7 @@ class L10nAtEndabrechnungWizard(models.TransientModel):
self._abf_alt_validieren()
self._abf_frei_validieren()
self._vergleich_validieren()
self._pram_validieren()
erster = self.austrittsdatum.replace(day=1)
letzter = (erster + timedelta(days=32)).replace(day=1) \
- timedelta(days=1)
@@ -587,6 +718,11 @@ class L10nAtEndabrechnungWizard(models.TransientModel):
self.abf_frei_nachweis if self.abf_frei_aktiv else False,
'l10n_at_vergleich_nachweis':
self.vergleich_nachweis if self.vergleich_aktiv else False,
'l10n_at_pram_nachweis':
self.pram_nachweis if self.pram_aktiv else False,
'l10n_at_pram_beitragsmonat': self.pram_beitragsmonat
if (self.pram_aktiv
and self.pram_zahlungsart == 'spaeter_faellig') else False,
})
typen = {code: self.env['hr.payslip.input.type'].search(
[('code', '=', code)], limit=1)