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: Mutterschutz/Wochengeld — Entgelt-Stopp, volle §-162-Abs-3-Berechnung, fiktive BV (AP7)
Rollenklärung (lb-msf-03, ASVG § 162 Abs 1): Wochengeld ist eine KV-Leistung — die ÖGK zahlt; der Dienstgeber stoppt das Entgelt in den Beschäftigungsfristen (MSchG §§ 3/5), liefert die Bemessungsdaten (Arbeits-/Entgeltbestätigung folgt mit AP8) und fuehrt die Abfertigung Neu auf fiktiver Grundlage. - models/mutterschutz.py: _at_msch_status (Kalendertage/Arbeitstage/ Episode aus der neuen Urlaubsart 'Mutterschutz-Frist', WET ATP_MSCH), _at_wochengeld — volle Wochengeld-Rechnung § 162 Abs 3 ASVG: Ø-Netto der drei Referenzmonate (letzte 13 Wochen) x (1 + satzungs- SZ-Prozentsatz 14/17/21 %, Parameter at_msch_sz_prozentsatz, vor Erstverwendung mandantenseitig zu verifizieren) ÷ Kalender- tage; Fallback kurzes DV (lb-msf-03) - Regel MSCH_ENTGELT: Entgelt-Stopp je Arbeitstag der Frist (negativ, Brutto; Tagesatz-Helper mit EFZG geteilt) - Fiktive BV-Grundlage § 7 Abs 4 BMSVG (RIS verifiziert): Monatsentgelt-Ø der letzten drei Kalendermonate vor dem Versicherungs- fall der Mutterschaft inkl. anteiliger Sonderzahlungen x Bezugs-KT/30, im _at_bv_basis_laufen-Hook neben der Krankengeld-Fiktion (AP4) - Rechenfaelle docs/rechenfaelle/mutterschutz.md; 4 Tests (Entgelt-Stopp + fiktive BV, §-162-Abs-3-Arithmetik, Wirkungslosigkeit ohne Frist, kurzes-DV-Fallback); Optionen dokumentiert: Sonderwochengeld § 163, Ruhen § 166, Karenz-Referenzmonat § 7 Abs 4 Z 1-3
This commit is contained in:
@@ -12,4 +12,5 @@ from . import versichertenmeldung
|
||||
from . import fristen_private
|
||||
from . import buchungsbeleg
|
||||
from . import wien_dag
|
||||
from . import efzg
|
||||
from . import efzg
|
||||
from . import mutterschutz
|
||||
@@ -214,11 +214,11 @@ class HrPayslip(models.Model):
|
||||
basis = self._l10n_at_sv_basis_roh(result_rules)
|
||||
if pflichtig_ab is None or pflichtig_ab <= self.date_from:
|
||||
# AP4: fiktive BG während des Krankengeldbezugs (§ 7 Abs 3
|
||||
# BMSVG) — 50 % des Vormonatsentgelts aliquot nach
|
||||
# Kalendertagen der Halb-/ÖGK-Phase; nur in beitrags-
|
||||
# pflichtigen Läufen (Erstfenster-Teilmonate: dokumentierter
|
||||
# Randfall).
|
||||
return basis + self._at_bv_fiktiv_krankengeld()
|
||||
# BMSVG) und des Wochengeldbezugs (§ 7 Abs 4, AP7) — nur
|
||||
# in beitragspflichtigen Läufen (Erstfenster-Teilmonate:
|
||||
# dokumentierter Randfall).
|
||||
return basis + self._at_bv_fiktiv_krankengeld() \
|
||||
+ self._at_bv_fiktiv_wochengeld()
|
||||
if pflichtig_ab > self.date_to:
|
||||
# LZP liegt zur Gänze im beitragsfreien Erstfenster.
|
||||
return 0.0
|
||||
|
||||
@@ -233,10 +233,13 @@ class HrPayslip(models.Model):
|
||||
def _at_efz_tagesatz(self, result_rules=None):
|
||||
"""Voller Tages-Entgeltsatz des Monats (Entgeltausfalls-
|
||||
prinzip): laufende Monatsbasis ohne die eigenen
|
||||
Teilentgelt-Reduktionen ÷ Arbeitstage des Kalendermonats."""
|
||||
Teilentgelt-Reduktionen ÷ Arbeitstage des Kalendermonats.
|
||||
Geteilter Helper von EFZG (TEIL_ENTGELT_HALB,
|
||||
KRANKENTGELT_PHASE) und Mutterschutz (MSCH_ENTGELT, AP7)."""
|
||||
self.ensure_one()
|
||||
basis = self._l10n_at_sv_basis_roh(result_rules)
|
||||
for code in ('TEIL_ENTGELT_HALB', 'KRANKENTGELT_PHASE'):
|
||||
for code in ('TEIL_ENTGELT_HALB', 'KRANKENTGELT_PHASE',
|
||||
'MSCH_ENTGELT'):
|
||||
if result_rules is not None and code in result_rules \
|
||||
and result_rules[code]['total']:
|
||||
basis -= result_rules[code]['total']
|
||||
|
||||
@@ -0,0 +1,229 @@
|
||||
# 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.
|
||||
"""
|
||||
from datetime import date, timedelta
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
_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()
|
||||
slip.l10n_at_msch_info = (
|
||||
'%d Kalendertage Bezug · WG %.2f/Kalendertag '
|
||||
'(Ø-Netto %.2f + %.0f %%)' % (
|
||||
len(msch['kalendertage']),
|
||||
wochengeld['tagesatz'],
|
||||
wochengeld['netto_drei_monate'],
|
||||
wochengeld['sz_prozent']))
|
||||
|
||||
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_referenz_monate(self, episode_beginn):
|
||||
"""Die drei (vollen) Kalendermonate vor dem Versicherungsfall
|
||||
der Mutterschaft (§ 162 Abs 3: letzte 13 Wochen; § 7 Abs 4
|
||||
BMSVG: letzte drei Kalendermonate). Versicherungsfall im Monat
|
||||
X → Monate X-3, X-2, X-1 (aufsteigend)."""
|
||||
self.ensure_one()
|
||||
stichtag = episode_beginn or self.date_from
|
||||
erster = date(stichtag.year, stichtag.month, 1)
|
||||
monate = []
|
||||
hilf = erster
|
||||
for _ in range(3):
|
||||
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
|
||||
Reference in New Issue
Block a user