Files
odoo-at-payroll/addons/l10n_at_hr_payroll/models/fristenkalender.py
T
fegger 3c85cc6790 [IMP] l10n_at_hr_payroll: TASY-Tarifsystem-Katalog, AGRD-Auswahl, DG-Kosten-/Lohnkonten-Reports, Wr.-DAG-Grundlagen (AP0-AP3)
AP0 Variante b — TASY-Tarifsystem-Katalog:
- Neue Katalogmodelle (BSGR/ERGB/Dienstgebergruppen/Tarifgruppen/
  Tarifsets/Tarife/Zuordnungen/Zu-Abschläge inkl. Steuerung),
  models/tasy_katalog.py — Auflösung Tarifgruppe (BSGR+ERGB+DGG)
  -> Sätze je Verrechnungsbasis
- Import-Wizard l10n.at.payroll.tasy.katalog.wizard (alle Träger,
  sektionsscharf, idempotent), Views/Menüs, Security
- Realexport-Test gegen TASY-LSWH-Export (B001/B002 = 39,05/37,55 %)

AP1 — eSV-Abmeldegründe (DM-ORG D.22):
- l10n_at_agrd Char -> vollständige Selection (33 Codes),
  Seed der Standard-Abgangsursachen (noupdate, hr-Defaults vorverbunden),
  Form-/Listen-Erbe

AP2a-c — Auswertungen:
- Monatsjournal: eigener Dienstgeberanteile-Block (ATP_DG vorher
  still ausgefiltert); Fix: models/monatsreports.py war nie in
  models/__init__.py importiert (GP7-Reports waren toter Code)
- Neuer Report Dienstgeber-Gesamtkosten (Brutto + LNK aufgeschlüsselt/
  kumuliert je AN + Laufsummen, Spalten FLAF/KommSt/DZ/BV/Wr. DAG)
- Jahreslohnkonto je DN + Betriebsjahreslohnkonto (Jahresbericht-
  Wizard, Monatsmatrix je Lohnart), report/jahresreports.xml

AP3 — Wr. Dienstgeberabgabe (RIS LrW GesNr 20000133 verifiziert):
- Company-Flag l10n_at_wien_dag (Kern), Fristenarten wien_dag/
  wien_dag_jahres (cron-geflaggt, 15./31. 3.), Company-View-Feld,
  Wr.-DAG-Spalte im DG-Kosten-Report
2026-09-12 23:17:00 +02:00

264 lines
11 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Part of the odoo-at-payroll project. License: LGPL-3.
"""Fristenkalender (GP8/AP8): alle lohnverrechnungsrelevanten
Melde-/Zahlungsfristen je Company und Jahr.
Kuratierte Fristen (lb-sva-11, lb-mel-08, lb-lvr-04/05, Stand
2026-06…08):
- SV-Beiträge (mBGM): Einlangen **15. des Folgemonats** (Gutschrift-
prinzip; Respirofrist 3 Tage; Verzugszinsen ab dem 16. Tag);
Vorschreibebetriebe 7.
- DB (FLAF) / DZ / KommSt: je **15. des Folgemonats**.
- KommSt-Jahreserklärung: über FinanzOnline **bis Ende März** des
Folgejahres.
- Lohnzettel L16 (§ 84 EStG): **Ende Februar** des Folgejahres via
ELDA (Papierformular ohne technische Voraussetzungen: Ende Jänner);
berichtigter Lohnzettel nach steuerlich relevanter Ergänzung des
Lohnkontos **binnen 14 Tagen**.
- Arbeitsstättenmeldung § 34 Abs 6 ASVG: **Ende Februar** (via ELDA
an Statistik Austria).
- § 109a-EStG-Meldung: **Ende Februar** (ELDA); Papierformular E 109a
+ Kopie an den Entgeltempfänger Ende Jänner (nicht implementiert —
Honorar-Basis).
- Schwerarbeitsmeldung ASchV § 5: Jänner bis **Ende Februar** via
ELDA (nicht implementiert — blockiert, ASchV-Datenmodell fehlt).
Odoo-Integration: `mail.activity.mixin` für optionale Benachrichtigung
bei Überschreitung; native Fristart-Definition mit Sprungaktion zur
GP7-Deklaration (L16/mBGM erzeugen); Auto-Erzeugung per ir.cron
(monatlich am 1.: offene SV/DB/DZ/KommSt-Fristen des Vormonats;
jährlich am 1. Jänner: Jahresfristen des Vorjahres)."""
from datetime import date, timedelta
from odoo import api, fields, models
FRISTART_DEFINITIONEN = {
'sv_mbgm': {
'name': 'SV-Beiträge (mBGM) einlangen',
'monatlich': True,
'tag': 15,
'action': 'mbgm',
'hilfetext': '15. des Folgemonats (Gutschriftprinzip; '
'Vorschreibebetriebe: 7.). Verzugszinsen ab dem '
'16. Tag.',
},
'db_dz_kommst': {
'name': 'DB/DZ/KommSt einlangen',
'monatlich': True,
'tag': 15,
'action': None,
'hilfetext': 'FLAF-Dienstgeberbeitrag, Zuschlag zum '
'Dienstgeberbeitrag und Kommunalsteuer: je 15. '
'des Folgemonats.',
},
'kommst_jahres': {
'name': 'KommSt-Jahreserklärung (FinanzOnline)',
'monatlich': False,
'monat': 3,
'tag_im_monat': 31,
'action': None,
'hilfetext': 'Jahreserklärung der Kommunalsteuer über '
'FinanzOnline bis Ende März des Folgejahres '
'(§ 11 Abs 5 KommStG).',
},
'l16': {
'name': 'Lohnzettel L16 (§ 84 EStG)',
'monatlich': False,
'monat': 2,
'tag_im_monat': 28,
'action': 'l16',
'hilfetext': 'Jahres-Lohnzettel bis Ende Februar des '
'Folgejahres via ELDA; Papierformular ohne '
'technische Voraussetzungen: Ende Jänner. '
'Berichtigter Lohnzettel nach steuerlich '
'relevanter Ergänzung des Lohnkontos binnen '
'14 Tagen.',
},
'arbeitsstaette': {
'name': 'Arbeitsstättenmeldung (§ 34 Abs 6 ASVG)',
'monatlich': False,
'monat': 2,
'tag_im_monat': 28,
'action': 'l16',
'hilfetext': 'Adresse der Arbeitsstätte am 31. 12. bis '
'Ende Februar des Folgejahres via ELDA (VSTR '
'ST). Läuft im GP7-Jahreslauf mit der L16.',
},
'paragraph_109a': {
'name': '§ 109a-EStG-Meldung (Honorare)',
'monatlich': False,
'monat': 2,
'tag_im_monat': 28,
'action': None,
'hilfetext': 'Meldung der Honorare außerhalb von '
'Dienstverhältnissen bis Ende Februar via '
'ELDA (8 Tätigkeitsgruppen; kumulativ ≤ 900 € '
'und ≤ 450 € je Leistung → entfällt).',
},
'schwerarbeit': {
'name': 'Schwerarbeitsmeldung (ASchV § 5)',
'monatlich': False,
'monat': 2,
'tag_im_monat': 28,
'action': None,
'hilfetext': 'Jährlich rückwirkend Jänner bis Ende Februar '
'via ELDA an den KV-Träger; meldepflichtig '
'ab 40 M / 35 F (⚠ blockiert — ASchV-Daten-'
'modell fehlt im Modul).',
},
'wien_dag': {
'name': 'Wr. Dienstgeberabgabe entrichten (Stadtkasse)',
'monatlich': True,
'tag': 15,
'action': None,
'wien_dag': True,
'hilfetext': 'Wr. DAG („U-Bahn-Steuer“, GesNr 20000133): '
'Selbstberechnungsabgabe — die im Vormonat '
'entstandene Abgabenschuld bis zum 15. des '
'Folgemonats an die Stadtkasse (Magistrat) '
'entrichten (§ 6 Abs 1). Nur Firmen mit Flag '
'l10n_at_wien_dag (Beschäftigungsort Wien).',
},
'wien_dag_jahres': {
'name': 'Wr. DAG-Jahreserklärung (Magistrat)',
'monatlich': False,
'monat': 3,
'tag_im_monat': 31,
'action': None,
'wien_dag': True,
'hilfetext': 'Bis 31. März die im Vorjahr entstandene '
'Abgabenschuld beim Magistrat schriftlich '
'erklären; darin auch die befreiten '
'Dienstverhältnisse angeben (§ 6 Abs 2 Wr. '
'DAG). Nur Firmen mit Flag l10n_at_wien_dag.',
},
}
class L10nAtPayrollFrist(models.Model):
_name = 'l10n.at.payroll.frist'
_description = 'Lohnverrechnungs-Fristenkalender'
_inherit = ['mail.thread']
_order = 'deadline, id'
company_id = fields.Many2one(
'res.company', required=True,
default=lambda self: self.env.company, index=True)
fristart = fields.Selection(
[(k, v['name']) for k, v in FRISTART_DEFINITIONEN.items()],
required=True, index=True)
jahr = fields.Integer(
required=True, string='Bezugs-/Kalenderjahr',
help='Beitragsjahr (monatliche Fristen) bzw. gemeldetes '
'Jahr (Jahresfristen: L16/Arbeitsstätte/§-109a gelten '
'für das Vorjahr).')
monat = fields.Integer(
string='Beitragsmonat (monatliche Fristen)',
help='112; leer bei Jahresfristen.')
deadline = fields.Date(required=True, string='Frist')
state = fields.Selection(
[('offen', 'Offen'), ('erledigt', 'Erledigt'),
('ueberfaellig', 'Überfällig')],
compute='_compute_state', store=True, index=True)
erledigt_am = fields.Date(readonly=True)
hilfetext = fields.Text(readonly=True)
ref_l16_id = fields.Many2one(
'l10n.at.payroll.l16', string='L16-Deklaration',
help='Sprungziel für Fristen mit L16/Arbeitsstätten-Meldung.')
_sql_constraints = [(
'frist_company_art_jahr_monat_uniq',
'unique(company_id, fristart, jahr, monat)',
'Diese Frist existiert bereits für das Unternehmen.')]
@api.depends('deadline', 'erledigt_am')
def _compute_state(self):
heute = fields.Date.today()
for frist in self:
if frist.erledigt_am:
frist.state = 'erledigt'
elif frist.deadline and frist.deadline < heute:
frist.state = 'ueberfaellig'
else:
frist.state = 'offen'
def action_erledigt(self):
self.write({'erledigt_am': fields.Date.today()})
return True
def action_wieder_eroeffnen(self):
self.write({'erledigt_am': False})
return True
def action_zum_objekt(self):
"""Sprung zur GP7-Deklaration (L16 oder mBGM)."""
self.ensure_one()
if self.ref_l16_id:
return {
'type': 'ir.actions.act_window',
'res_model': 'l10n.at.payroll.l16',
'res_id': self.ref_l16_id.id,
'view_mode': 'form',
}
return True
@api.model
def _cron_fristen_erzeugen(self):
"""Monatlich am 1.: offene Fristen des Vormonats (SV/DB/DZ/
KommSt) + jährlich am 1. Jänner: Jahresfristen des
Vorjahres. Idempotent (Unique-Constraint)."""
heute = fields.Date.today()
vormonat = (heute.replace(day=1) - timedelta(days=1))
for company in self.env['res.company'].search([]):
# Monatliche Fristen des Vormonats (15. des Folgemonats).
folgemonat = heute.replace(day=1)
deadline_monat = date(
folgemonat.year, folgemonat.month,
FRISTART_DEFINITIONEN['sv_mbgm']['tag'])
monatliche_arten = ['sv_mbgm', 'db_dz_kommst']
if company.l10n_at_wien_dag:
monatliche_arten.append('wien_dag')
for art in monatliche_arten:
self._sicherstellen(
company, art, vormonat.year, vormonat.month,
deadline_monat)
# Jahresfristen nur am 1. Jänner (für das Vorjahr).
if heute.month == 1:
for art in ('l16', 'arbeitsstaette', 'paragraph_109a',
'schwerarbeit'):
monat = FRISTART_DEFINITIONEN[art]['monat']
tag = FRISTART_DEFINITIONEN[art]['tag_im_monat']
jahr_deadline = heute.year
deadline_jahr = date(jahr_deadline, monat, tag)
if deadline_jahr.month == 2 and tag > 28:
deadline_jahr = date(jahr_deadline, monat, 28)
self._sicherstellen(
company, art, heute.year - 1, None,
deadline_jahr)
# KommSt-Jahreserklärung (Ende März).
self._sicherstellen(
company, 'kommst_jahres', heute.year - 1, None,
date(heute.year, 3, 31))
# Wr. DAG-Jahreserklärung (31. 3., § 6 Abs 2).
if company.l10n_at_wien_dag:
self._sicherstellen(
company, 'wien_dag_jahres', heute.year - 1,
None, date(heute.year, 3, 31))
@api.model
def _sicherstellen(self, company, art, jahr, monat, deadline):
vals = {
'company_id': company.id,
'fristart': art,
'jahr': jahr,
'monat': monat,
'deadline': deadline,
'hilfetext': FRISTART_DEFINITIONEN[art]['hilfetext'],
}
bestehend = self.search([
('company_id', '=', company.id),
('fristart', '=', art),
('jahr', '=', jahr),
('monat', '=', monat)])
if not bestehend:
self.create(vals)
return bestehend or self.env['l10n.at.payroll.frist']