mirror of
http://100.103.83.12:3003/fegger/odoo-at-payroll.git
synced 2026-09-17 08:52:45 +00:00
9b4221fcef
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
113 lines
5.3 KiB
Python
113 lines
5.3 KiB
Python
# 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)} |