d53e1e64a1
Close the parental/care-leave block after the pilot-client scope release (8.13): six unpaid full-release kinds join the generic parental-leave work entry — Bildungskarenz (AVRAG § 11, two to twelve months by agreement with the AMS-financed Weiterbildungsbeihilfe), Familienhospizkarenz for terminal accompaniment (AVRAG § 14a, three to six months), accompaniment of seriously ill children (§ 14b, five to nine months), children's rehabilitation leave (§ 14e, four weeks per year), Pflegekarenz (§ 14c, one to three months, undivided) and the Papa-Monat (VKG § 1a, one month, creditable toward seniority under § 1a Abs 7 VKG iVm § 15f MSchG while Bildungs-/Pflegekarenz is not). All are unpaid at the employer — the external bodies pay the replacement income (AK, AMS, Bundesamt, KV-Träger) — so the new negative KARENZ_ENTGELT rule stops the employer wage per working day through the shared Tagesatz helper whose exclusion list now covers EFZG, Mutterschutz and Karenz corrections. The Versichertenmeldung run emits M4 deregistrations at the end of the pay entitlement (the day before the release begins) and M3 re-registrations at the return date — omitted when the employment itself ends — with the eSV Karenzart strings logged as protocol information pending the special DM-ORG formats (GP7 track). The fictive BMSVG basis gains the § 7 Abs 4 Z 1–3 bridge: when a new maternity prohibition follows a parental-leave stretch directly or after employment of less than three months, the reference months fall before the preceding prohibition, and a re-founded employment within three months of the ended karenziert employment uses the single last full calendar month; contributions during the KBG period are FLAF-financed (§ 7 Abs 5) and during hospice/care leave Bund-financed (§ 7 Abs 6) — no employer rule, verified by test. Pflegeteilzeit and comparable reduced-hour arrangements pay the BV contribution on the pre-reduction wage via a new version field consumed by the BMSVG basis (§ 6 Abs 4). The endabrechnung wizard gains the § 23 Abs 8 AngG full-time re-rating preview (monthly salary ÷ work_time_rate, confirmable) with § 23a Abs 4a and UEL § 10 Abs 4 UrlG guidance. The Mutterschutz status now detects the § 163 ASVG Sonderwochengeld case (prohibition inside a Karenz — the KV-Träger pays the increased sickness benefit) and warns on § 166 ASVG suspension (full for >50 % continued pay, half at 50 %, activity and Pflegekarenzgeld hints). Verified against the original statute text in .ris/ (AVRAG §§ 11/11a/14a–14e, VKG § 1a, MSchG § 15f, BMSVG § 7 Abs 4–6, ASVG §§ 163/166) and the knowledge-base sources lb-kar/elt/kzs/pap/kbg; the vacation proration of leave stretches (§ 15f Abs 2 MSchG on the hr_holidays allocation side), the eSV special formats for the Karenz art block, the full Sonderwochengeld computation and the leave-overlap policy decision remain documented open points. Version 19.0.17.0.0 (private)
345 lines
16 KiB
Python
345 lines
16 KiB
Python
# Part of the odoo-at-payroll project. License: LGPL-3.
|
||
"""AP7: Mutterschutz/Wochengeld (MSchG §§ 3/5, ASVG § 162, BMSVG
|
||
§ 7 Abs 4 — verifiziert 2026-09-12 gegen RIS bzw. Wissensbasis
|
||
lb-msf-03, Stand 2026-07).
|
||
|
||
**Klargestellte Rollenverteilung** (lb-msf-03, § 162 Abs 1 ASVG): Das
|
||
Wochengeld ist eine **Leistung aus der Krankenversicherung** — die ÖGK
|
||
zahlt es direkt an die Arbeitnehmerin. Der Dienstgeber
|
||
1. schuldet für die Beschäftigungsverbots-Tage **kein Entgelt**
|
||
(Regel ``MSCH_ENTGELT`` stoppt die arbeitsseitigen Bezüge),
|
||
2. liefert die **Bemessungsdaten** (13-Wochen-/3-Kalendermonats-
|
||
Basis; Arbeits- und Entgeltbestätigung folgt mit AP8) — hier als
|
||
vollständige Wochengeld-Berechnung nach § 162 Abs 3 ASVG:
|
||
Ø-Arbeitsverdienst der letzten drei Kalendermonate (Geld- und
|
||
Sachbezüge, Entstehungsprinzip), vermindert um Lohnsteuer,
|
||
SV-Anteil, Kammerumlage, Wohnbauförderungsbeitrag, Aufwandersätze
|
||
und Schlechtwetterentschädigungsbeitrag (= Netto der
|
||
Referenzläufe), erhöht um den satzungsmäßigen Sonderzahlungs-
|
||
Prozentsatz (14/17/21 %, ``at_msch_sz_prozentsatz`` — mandanten-
|
||
seitig zu pflegen), je Kalendertag,
|
||
3. zahlt die **Abfertigung Neu auf fiktiver Grundlage** weiter
|
||
(§ 7 Abs 4 BMSVG): fiktive BG = Monatsentgelt, berechnet nach dem
|
||
in den letzten drei Kalendermonaten vor dem Versicherungsfall der
|
||
Mutterschaft gebührenden Entgelt **einschließlich anteiliger
|
||
Sonderzahlungen** (SZ-Fortzahlung während des Bezugs = vereinbarte
|
||
Ausnahme, dokumentierte Option), aliquot nach Kalendertagen
|
||
(30-Tage-Konvention).
|
||
|
||
Schutzfristen: § 3 MSchG Achtwochenfrist vor der voraussichtlichen
|
||
Entbindung; § 5 MSchG acht Wochen Wochenbett (mindestens zwölf bei
|
||
Früh-/Mehrlingsgeburten oder Kaiserschnitt; Verlängerung um
|
||
vorzeitig verkürzte Achtwochenfrist, max. 16 Wochen) — als
|
||
Urlaubsart „Mutterschutz-Frist" (WET ``ATP_MSCH``) zu erfassen; die
|
||
Fristberechnung aus dem ärztlichen Zeugnis bleibt beim Mandanten.
|
||
|
||
Urlaubsanspruch erwächst während des Wochengeldbezugs (lb-msf-03);
|
||
Sonderwochengeld (§ 163 ASVG, Günstigkeitsvergleich § 162 Abs 3b) =
|
||
dokumentierte Option.
|
||
|
||
AP16 (8.13): Karenz-Brücke nach § 7 Abs 4 Z 1–3 BMSVG — folgt das
|
||
neuerliche Beschäftigungsverbot unmittelbar auf eine Karenz (Z 1) oder
|
||
nach einer kürzeren als dreimonatigen Beschäftigung (Z 2), ist die
|
||
Bemessungsgrundlage die des Kalendermonats vor dem der Karenz
|
||
unmittelbar vorangegangenen Beschäftigungsverbots; Z 3 (neues DV nach
|
||
Beendigung des karenzierten DV, < 3 Monate): der letzte volle
|
||
Kalendermonat vor dem neuerlichen Verbot. BV während des KBG-Bezugs
|
||
läuft zu Lasten des FLAF (§ 7 Abs 5), in der Familienhospiz-/Pflege-
|
||
karenz zu Lasten des Bundes (§ 7 Abs 6) — keine AG-Regeln; das Ruhen
|
||
des Wochengeldes (§ 166 ASVG) und das Sonderwochengeld während
|
||
einer Karenz (§ 163 ASVG) werden als Info/Warnung ausgewiesen.
|
||
"""
|
||
from datetime import date, timedelta
|
||
|
||
from dateutil.relativedelta import relativedelta
|
||
|
||
from odoo import fields, models
|
||
|
||
from .karenz import KARENZ_ARTEN
|
||
|
||
_MSCH_WET = 'ATP_MSCH'
|
||
|
||
|
||
class HrPayslip(models.Model):
|
||
_inherit = 'hr.payslip'
|
||
|
||
l10n_at_msch_info = fields.Char(
|
||
string='AT Mutterschutz/Wochengeld-Status',
|
||
compute='_compute_msch_info',
|
||
help='Wochengeld je Kalendertag (§ 162 Abs 3 ASVG), '
|
||
'Bezugstage und fiktive BV-Grundlage (§ 7 Abs 4 BMSVG) '
|
||
'im Abrechnungszeitraum.')
|
||
|
||
def _compute_msch_info(self):
|
||
for slip in self:
|
||
msch = slip._at_msch_status()
|
||
if not msch:
|
||
slip.l10n_at_msch_info = ''
|
||
continue
|
||
wochengeld = slip._at_wochengeld()
|
||
info = (
|
||
'%d Kalendertage Bezug · WG %.2f/Kalendertag '
|
||
'(Ø-Netto %.2f + %.0f %%)' % (
|
||
len(msch['kalendertage']),
|
||
wochengeld['tagesatz'],
|
||
wochengeld['netto_drei_monate'],
|
||
wochengeld['sz_prozent']))
|
||
if slip._at_msch_sonderwochengeld():
|
||
info += (' · Sonderwochengeld (§ 163 ASVG) statt '
|
||
'Wochengeld — ÖGK, erhöhtes Krankengeld '
|
||
'(§ 141 Abs 2, § 125 iVm § 108f)')
|
||
warnungen = slip._at_msch_ruhen_warnungen()
|
||
if warnungen:
|
||
info += ' · Ruhen: ' + '; '.join(warnungen)
|
||
slip.l10n_at_msch_info = info
|
||
|
||
def _at_msch_leaves(self):
|
||
"""Validierte Mutterschutz-Frist-Abwesenheiten (ATP_MSCH)."""
|
||
self.ensure_one()
|
||
typen = self.env['hr.leave.type'].search([
|
||
('work_entry_type_id.code', '=', _MSCH_WET)])
|
||
leaves = self.env['hr.leave'].search([
|
||
('employee_id', '=', self.employee_id.id),
|
||
('holiday_status_id', 'in', typen.ids),
|
||
('state', 'in', ('validate', 'validate1')),
|
||
])
|
||
return [
|
||
(leave.request_date_from, leave.request_date_to)
|
||
for leave in leaves
|
||
if leave.request_date_from and leave.request_date_to]
|
||
|
||
def _at_msch_status(self):
|
||
"""Mutterschutz-Tage je Abrechnungsperiode: {'kalendertage',
|
||
'arbeitstage', 'episode_beginn'} — None ohne Frist im Zeitraum.
|
||
|
||
Episode-Beginn = Anfang des zusammenhängenden Fristenblocks,
|
||
der in die Abrechnungsperiode reicht (Versicherungsfall der
|
||
Mutterschaft, § 120 Abs 1 Z 3 ASVG) — Bemessungsstichtag für
|
||
Wochengeld (§ 162 Abs 3) und fiktive BV (§ 7 Abs 4 BMSVG)."""
|
||
self.ensure_one()
|
||
tage = {}
|
||
for von, bis in self._at_msch_leaves():
|
||
tag = von
|
||
while tag <= bis:
|
||
tage[tag] = True
|
||
tag += timedelta(days=1)
|
||
in_periode = sorted(
|
||
tag for tag in tage
|
||
if self.date_from <= tag <= self.date_to)
|
||
if not in_periode:
|
||
return None
|
||
block = in_periode[0]
|
||
while (block - timedelta(days=1)) in tage:
|
||
block = block - timedelta(days=1)
|
||
eintraege = self.env['hr.work.entry'].search([
|
||
('employee_id', '=', self.employee_id.id),
|
||
('date', '>=', self.date_from),
|
||
('date', '<=', self.date_to),
|
||
('state', '!=', 'cancelled'),
|
||
('work_entry_type_id.code', '=', _MSCH_WET),
|
||
])
|
||
return {
|
||
'kalendertage': in_periode,
|
||
'arbeitstage': sorted({e.date for e in eintraege}),
|
||
'episode_beginn': block,
|
||
}
|
||
|
||
def _at_msch_sonderwochengeld(self):
|
||
"""Sonderwochengeld statt Wochengeld (§ 163 ASVG, verifiziert):
|
||
die Mutterschutz-Frist fällt in eine Karenz nach dem MSchG —
|
||
kein Anspruch auf Wochengeld (§ 162), das Sonderwochengeld
|
||
zahlt der Krankenversicherungsträger (erhöhtes Krankengeld
|
||
§ 141 Abs 2, Bemessungsgrundlage § 125 aus dem Arbeits-
|
||
verdienst vor dem Ende des letzten Entgeltanspruchs, mit
|
||
§ 108f-Anpassung)."""
|
||
self.ensure_one()
|
||
msch = self._at_msch_status()
|
||
if not msch:
|
||
return False
|
||
for von, bis, _wett in self._at_karenz_leaves():
|
||
if any(von <= tag <= bis for tag in msch['kalendertage']):
|
||
return True
|
||
return False
|
||
|
||
def _at_msch_ruhen_warnungen(self):
|
||
"""Ruhen des Wochengeldes (§ 166 ASVG, verifiziert) —
|
||
Warnungen zu Fortbezug während der Frist:
|
||
Z 2 Entgeltfortzahlung 100 % → Ruhen zur Gänze, 50 % →
|
||
hälftig (Urlaubs-/UEL-/KE-Zeiten gelten nicht als weiterge-
|
||
leistete Bezüge); Z 3 Erwerbstätigkeit und Z 4 Pflegekarenz-
|
||
geld sind Bezugszeitraum-Daten (AEB/ÖGK-Seite, Hinweis)."""
|
||
self.ensure_one()
|
||
msch = self._at_msch_status()
|
||
if not msch:
|
||
return []
|
||
tage = set(msch['kalendertage'])
|
||
efz = self._at_efz_status()
|
||
warnungen = []
|
||
if efz:
|
||
if any(tag in tage for tag in efz.get('voll_arbeitstage', [])):
|
||
warnungen.append('Fortbezug > 50 % (Entgeltfortzahlung) '
|
||
'— Ruhen zur Gänze (§ 166 Abs 1 Z 2)')
|
||
elif any(tag in tage for tag in efz.get('halb_arbeitstage', [])):
|
||
warnungen.append('Fortbezug = 50 % — Ruhen hälftig '
|
||
'(§ 166 Abs 1 Z 2)')
|
||
warnungen.append('Erwerbstätigkeit/Pflegekarenzgeld während der '
|
||
'Frist prüfen (§ 166 Abs 1 Z 3/4)')
|
||
return warnungen
|
||
|
||
def _at_msch_referenz_stichtag_und_anzahl(self, episode_beginn):
|
||
"""(Stichtag, Referenz-Monatsanzahl) nach § 7 Abs 4 BMSVG mit
|
||
Karenz-Brücke Z 1–3 (AP16):
|
||
- Z 1/Z 2: endete eine Karenz innerhalb von drei Monaten vor
|
||
dem neuerlichen Beschäftigungsverbot, gilt das der Karenz
|
||
unmittelbar vorangegangene Beschäftigungsverbot als
|
||
Bemessungsstichtag (Referenzmonate davor);
|
||
- Z 3: neues DV binnen drei Monaten nach Beendigung des
|
||
karenzierten DV (mit Karenz) → der letzte volle Kalendermonat
|
||
vor dem neuerlichen Verbot als EINZELNER Referenzmonat
|
||
(„volles Monatsentgelt");
|
||
- sonst: Versicherungsfall selbst, drei Monate."""
|
||
self.ensure_one()
|
||
stichtag = episode_beginn or self.date_from
|
||
grenze = stichtag - relativedelta(months=3)
|
||
karenz_kandidat = None
|
||
for von, bis, _wett in sorted(self._at_karenz_leaves(),
|
||
key=lambda k: k[1], reverse=True):
|
||
if grenze <= bis < stichtag:
|
||
karenz_kandidat = (von, bis)
|
||
break
|
||
if karenz_kandidat:
|
||
karenz_von = karenz_kandidat[0]
|
||
frist = max(
|
||
(m for m in self._at_msch_leaves()
|
||
if m[1] <= karenz_von),
|
||
key=lambda m: m[1], default=None)
|
||
if frist:
|
||
return frist[0], 3
|
||
version = self.version_id
|
||
vorher = self.env['hr.version'].search([
|
||
('employee_id', '=', self.employee_id.id),
|
||
('contract_date_end', '!=', False),
|
||
('id', '!=', version.id),
|
||
], order='contract_date_end desc', limit=1)
|
||
if (vorher and vorher.contract_date_end
|
||
and version.contract_date_start
|
||
and vorher.contract_date_end
|
||
<= version.contract_date_start
|
||
and (version.contract_date_start
|
||
- vorher.contract_date_end).days < 92
|
||
and any(k[1] <= vorher.contract_date_end
|
||
for k in self._at_karenz_leaves())):
|
||
erster = date(stichtag.year, stichtag.month, 1)
|
||
erster = (erster - timedelta(days=1)).replace(day=1)
|
||
return erster, 1
|
||
return stichtag, 3
|
||
|
||
def _at_msch_referenz_monate(self, episode_beginn):
|
||
"""Referenzkalendermonate der fiktiven BV (§ 7 Abs 4 BMSVG:
|
||
letzte drei Monate; Karenz-Brücke Z 1–3, AP16 — s.
|
||
_at_msch_referenz_stichtag_und_anzahl; § 162 Abs 3: letzte
|
||
13 Wochen). Versicherungsfall im Monat X → Monate X-3, X-2,
|
||
X-1 (aufsteigend)."""
|
||
self.ensure_one()
|
||
stichtag, anzahl = self._at_msch_referenz_stichtag_und_anzahl(
|
||
episode_beginn)
|
||
erster = date(stichtag.year, stichtag.month, 1)
|
||
monate = []
|
||
hilf = erster
|
||
for _ in range(anzahl):
|
||
hilf = (hilf - timedelta(days=1)).replace(day=1)
|
||
monate.append(hilf)
|
||
return list(reversed(monate))
|
||
|
||
def _at_msch_referenz_slip(self, monats_beginn):
|
||
"""Vollständiger validierter Referenzlauf des Monats (oder
|
||
leer)."""
|
||
self.ensure_one()
|
||
monats_ende = (monats_beginn.replace(day=28)
|
||
+ timedelta(days=4)).replace(day=1) \
|
||
- timedelta(days=1)
|
||
return self.env['hr.payslip'].search([
|
||
('employee_id', '=', self.employee_id.id),
|
||
('state', 'in', ('validated', 'paid')),
|
||
('date_from', '<=', monats_beginn),
|
||
('date_to', '>=', monats_ende),
|
||
], order='date_from desc', limit=1)
|
||
|
||
def _at_wochengeld(self):
|
||
"""Wochengeld-Berechnung nach § 162 Abs 3 ASVG (volle
|
||
Berechnung, AP7 — Ausweis/AEB-Daten; Zahlung durch die ÖGK):
|
||
|
||
Ø-Arbeitsverdienst der drei Referenzmonate, vermindert um
|
||
Lohnsteuer, SV-Anteil, Kammerumlage, Wohnbauförderungsbeitrag,
|
||
Aufwandersätze und Schlechtwetterentschädigungsbeitrag (=
|
||
Netto der Referenzläufe), erhöht um den satzungsmäßigen
|
||
SZ-Prozentsatz, je Kalendertag der drei Monate. Kurzes
|
||
Dienstverhältnis: Ø über die vorhandenen Monate (lb-msf-03);
|
||
ohne jede Historie der laufende Monat."""
|
||
self.ensure_one()
|
||
sz_prozent = float(self.env['hr.rule.parameter']
|
||
._get_parameter_from_code(
|
||
'at_msch_sz_prozentsatz', self.date_from,
|
||
raise_if_not_found=False) or 0.0)
|
||
ergebnis = {
|
||
'netto_drei_monate': 0.0, 'sz_prozent': sz_prozent,
|
||
'kalendertage_basis': 0, 'tagesatz': 0.0,
|
||
'referenz_monate': [], 'episode_beginn': None,
|
||
}
|
||
msch = self._at_msch_status()
|
||
if not msch:
|
||
return ergebnis
|
||
ergebnis['episode_beginn'] = msch['episode_beginn']
|
||
netto, kalendertage, gefundene = 0.0, 0, []
|
||
for monat in self._at_msch_referenz_monate(
|
||
msch['episode_beginn']):
|
||
ergebnis['referenz_monate'].append(monat)
|
||
monats_ende = (monat.replace(day=28)
|
||
+ timedelta(days=4)).replace(day=1) \
|
||
- timedelta(days=1)
|
||
kalendertage += monats_ende.day
|
||
slip = self._at_msch_referenz_slip(monat)
|
||
if slip:
|
||
gefundene.append(monat)
|
||
netto += sum(slip.line_ids.filtered(
|
||
lambda l: l.code == 'NET').mapped('total'))
|
||
if not gefundene:
|
||
# Ohne jede Historie: Netto des laufenden Monats
|
||
# (tatsächlicher Arbeitszeitraum, lb-msf-03).
|
||
netto = sum(self.line_ids.filtered(
|
||
lambda l: l.code == 'NET').mapped('total'))
|
||
kalendertage = 30
|
||
ergebnis['netto_drei_monate'] = netto
|
||
ergebnis['kalendertage_basis'] = kalendertage
|
||
ergebnis['tagesatz'] = netto * (1.0 + sz_prozent / 100.0) \
|
||
/ max(kalendertage, 1)
|
||
return ergebnis
|
||
|
||
def _at_bv_fiktiv_wochengeld(self):
|
||
"""Fiktive BV-Grundlage während des Wochengeldbezugs
|
||
(§ 7 Abs 4 BMSVG, verifiziert): Monatsentgelt-Ø aus den
|
||
letzten drei Kalendermonaten vor dem Versicherungsfall der
|
||
Mutterschaft **einschließlich anteiliger Sonderzahlungen**
|
||
(hier: laufende + sonstige Bezüge der Referenzläufe;
|
||
SZ-Fortzahlung während des Bezugs = vereinbarte Ausnahme,
|
||
Option), aliquot nach Kalendertagen des Bezugs
|
||
(30-Tage-Konvention)."""
|
||
self.ensure_one()
|
||
msch = self._at_msch_status()
|
||
if not msch or not msch['kalendertage']:
|
||
return 0.0
|
||
summe, monate = 0.0, 0
|
||
for monat in self._at_msch_referenz_monate(
|
||
msch['episode_beginn']):
|
||
slip = self._at_msch_referenz_slip(monat)
|
||
if not slip:
|
||
continue
|
||
summe += (slip._l10n_at_brutto('laufend')
|
||
+ slip._l10n_at_brutto('sonstig'))
|
||
monate += 1
|
||
if not monate:
|
||
# Ohne Historie: laufender Monat (kurzes DV, lb-msf-03).
|
||
summe = (self._l10n_at_brutto('laufend')
|
||
+ self._l10n_at_brutto('sonstig'))
|
||
monate = 1
|
||
return summe / monate * len(msch['kalendertage']) / 30.0 |