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: Wr. DAG, Buchungsbeleg, VWGJ-Einstufung, EFZG-Teilentgelt mit fiktiver BV (AP2d+AP3+AP9+AP4)
AP3 — Wiener Dienstgeberabgabe (U-Bahn-Steuer, RIS LrW GesNr 20000133 verifiziert): Regel WIEN_DAG (Kopfpauschale 2 Euro je DN und angefangene Woche, Wochenlogik Montag-im-Monat, Befreiungen Lehrling/55+/<=10 WStd/Karenz-Woche), Company-Flag-Verwertung, Parameter at_wien_dag_satz, FI 6663/3560 (Referenz-Rest) AP2d — Buchungsbeleg-Report am Lohnlauf (Konto/Belegtext/Soll/Haben der FI-Buchung, balancierte Summen; test_account_private-Erweiterung) AP9 — Vordienstzeiten -> Einstufung: Stufen-Schwelle vwgj_ab an l10n.at.payroll.kv.gruppe.stufe (aus Labels 'nach N VWGJ'; -1 = nicht gepflegt, ORM liest Integer-NULL als 0), CSV-Seed, _get_stufe_fuer_vwgj/_get_naechste_vwgj, Vertragsfeld l10n_at_vordienstzeit_monate + VWGJ-Vorschlag + Vorrueckungstermin (S 17 Abs 5 Rahmen-KV, Erster des Erreichungsmonats) + Uebernahme- Button (manueller Verwaltungsakt) AP4 — EFZG/Teilentgelt (AngG S 8/EFZG S 2 verifiziert, Gleichlauf 6/8/10/12+4 Wochen; lb-krs-07/08): EFZ-Phasenmaschine models/efzg.py (Kalendertage-Kontingente je Arbeitsjahr, Krank-/AU-Topf getrennt, AU-Urlaubsart ATP_AU), Regeln TEIL_ENTGELT_HALB/-50 % und KRANKENTGELT_PHASE/-100 % je Arbeitstag (Tagesatz aus unreduzierter Basis), fiktive BV-Grundlage S 7 Abs 3 BMSVG im _at_bv_basis_laufen- Hook (50 % Vormonatsentgelt x Bezugs-KT/30), Parameter at_efz_stufen; Krankengeldumlage S 62 ASVG = Falsch-Praemise (kein Baustein); Rechenfaelle docs/rechenfaelle/efzg.md
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
# Part of the odoo-at-payroll project. License: LGPL-3.
|
||||
"""Wiener Dienstgeberabgabe („U-Bahn-Steuer") — Wr. DAG, RIS LrW
|
||||
Gesetzesnummer 20000133, geltende Fassung 12. 09. 2026 (verifiziert,
|
||||
Quelle .firecrawl/ris/wien/WDGAG_*.html; § 9: die Einzahlungen fließen
|
||||
der Stadt Wien zu und sind zur Errichtung einer Untergrundbahn zu
|
||||
verwenden):
|
||||
|
||||
- **§ 5:** 2 € je Dienstnehmer:in und je angefangene Woche eines
|
||||
bestehenden Dienstverhältnisses; **§ 2:** Beschäftigungsort Wien
|
||||
(feste Arbeitsstätte, sonst Wohnsitz des Dienstnehmers; Bau-
|
||||
ausführungen > 6 Monate).
|
||||
- **§ 3 Befreiungen:** Gebietskörperschaften; Überschreiten des
|
||||
55. Lebensjahres; Behinderte/Opferfürsorge/BEinstG; Lehrverhältnisse
|
||||
(BAG); ≤ 10 Wochenstunden; Hausbesorger; gesetzliche Mutter-
|
||||
schaftsverbote und Karenzurlaub auf gesetzlichem Anspruch;
|
||||
Präsenzdienst/Zivildienst.
|
||||
- **§ 6:** Selbstberechnung — Vormonatsschuld bis 15. des Folgemonats
|
||||
an die Stadtkasse; Jahreserklärung bis 31. 3. inklusive befreiter
|
||||
Dienstverhältnisse (Fristenkalender-Arten wien_dag/wien_dag_jahres).
|
||||
|
||||
Wochenlogik (Praxis-Annahme, im Parallellauf gegen die Magistrats-
|
||||
Praxis zu verifizieren): „angefangene Woche" = Kalenderwoche, deren
|
||||
Montag im Abrechnungsmonat liegt; abgabepflichtig, wenn das Dienst-
|
||||
verhältnis an mindestens einem Tag der Woche besteht. Karenz-Wochen
|
||||
(validierte ATP_KARENZ-Work Entries) sind befreit. Nicht abgebildet
|
||||
(bis AP 7): Mutterschaftsverbote, Präsenzdienst/Zivildienst, Haus-
|
||||
besorger, Behinderten-Status — manuelle Prüfung, Rest dokumentiert.
|
||||
"""
|
||||
from datetime import timedelta
|
||||
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
from odoo import models
|
||||
|
||||
|
||||
class HrPayslip(models.Model):
|
||||
_inherit = 'hr.payslip'
|
||||
|
||||
def _at_wien_dag(self):
|
||||
"""Wr. DAG je Beitragsmonat: {'wochen', 'satz', 'betrag',
|
||||
'info'} — None, wenn die Company nicht erfasst ist; 0-Wochen
|
||||
bei Befreiung. Kopfpauschale, nicht lohnabhängig (§ 5)."""
|
||||
self.ensure_one()
|
||||
if not self.company_id.l10n_at_wien_dag:
|
||||
return None
|
||||
version = self.version_id
|
||||
employee = self.employee_id
|
||||
monat_start = self.date_from
|
||||
monat_ende = self.date_to
|
||||
info = []
|
||||
# Befreiungen § 3 (monatsweise Bewertung)
|
||||
if version.l10n_at_lehrling:
|
||||
return {'wochen': 0, 'satz': 0.0, 'betrag': 0.0,
|
||||
'info': 'Befreit: Lehrverhältnis (§ 3 lit d).'}
|
||||
if employee.birthday:
|
||||
vollendet55 = employee.birthday + relativedelta(years=55)
|
||||
if vollendet55 <= monat_ende:
|
||||
return {'wochen': 0, 'satz': 0.0, 'betrag': 0.0,
|
||||
'info': 'Befreit: 55. Lebensjahr überschritten '
|
||||
'(§ 3 lit b, %s).' % vollendet55}
|
||||
kalender = version.resource_calendar_id
|
||||
wochenstunden = sum(
|
||||
(att.hour_to or 0.0) - (att.hour_from or 0.0)
|
||||
for att in kalender.attendance_ids) \
|
||||
if kalender else 0.0
|
||||
if wochenstunden <= 10.0:
|
||||
return {'wochen': 0, 'satz': 0.0, 'betrag': 0.0,
|
||||
'info': 'Befreit: ≤ 10 Wochenstunden (§ 3 lit e).'}
|
||||
# Dienstverhältnis-Bestand im Monat (§ 1)
|
||||
von = max(monat_start,
|
||||
version.contract_date_start or monat_start)
|
||||
bis = min(monat_ende,
|
||||
version.contract_date_end or monat_ende)
|
||||
if von > bis:
|
||||
return {'wochen': 0, 'satz': 0.0, 'betrag': 0.0,
|
||||
'info': 'Kein bestehendes Dienstverhältnis im '
|
||||
'Abrechnungsmonat.'}
|
||||
# Karenz-Tage des Monats (§ 3 lit g — Rest via WET ATP_KARENZ)
|
||||
karenz_tage = set()
|
||||
if self.env['hr.work.entry']._fields.get('leave_id'):
|
||||
eintraege = self.env['hr.work.entry'].search([
|
||||
('employee_id', '=', employee.id),
|
||||
('date', '>=', monat_start),
|
||||
('date', '<=', monat_ende),
|
||||
('state', '!=', 'cancelled'),
|
||||
('work_entry_type_id.code', '=', 'ATP_KARENZ'),
|
||||
])
|
||||
karenz_tage = {e.date for e in eintraege}
|
||||
# Wochen mit Montag im Monat; abgabepflichtig, wenn das
|
||||
# Dienstverhältnis an mindestens einem Tag der Woche besteht
|
||||
# (Schnittmenge [DV] ∩ [Woche] nicht leer).
|
||||
wochen = 0
|
||||
tag = monat_start + timedelta(
|
||||
days=(7 - monat_start.weekday()) % 7)
|
||||
while tag <= monat_ende:
|
||||
woche_ende = tag + timedelta(days=6)
|
||||
if von <= woche_ende and bis >= tag:
|
||||
karenz_in_woche = any(
|
||||
tag + timedelta(days=i) in karenz_tage
|
||||
for i in range((woche_ende - tag).days + 1)
|
||||
if tag + timedelta(days=i) <= monat_ende)
|
||||
if karenz_in_woche:
|
||||
info.append('Karenz-Woche ab %s befreit (§ 3 lit g)'
|
||||
% tag)
|
||||
else:
|
||||
wochen += 1
|
||||
tag += timedelta(days=7)
|
||||
satz = float(self.env['hr.rule.parameter']
|
||||
._get_parameter_from_code(
|
||||
'at_wien_dag_satz', monat_start,
|
||||
raise_if_not_found=False) or 0.0)
|
||||
return {'wochen': wochen, 'satz': satz,
|
||||
'betrag': wochen * satz, 'info': '; '.join(info)}
|
||||
Reference in New Issue
Block a user