Files
odoo-at-payroll/addons/l10n_at_hr_payroll/models/kind.py
T
fegger c474a39466 [IMP] l10n_at_hr_payroll: GP7 ELDA-Fixsatz-Framework, L16 + Arbeitsstättenmeldung, Kind-Modell, Belege (AP5/AP6)
ELDA-Fixsatz-Framework (models/elda.py) nach DM-ORG 42.7.0 Kapitel C.1/C.2+E.1-E.3: Fixlängen-Sätze ohne Feldtrennzeichen (a/a-n linksbündig, Grundstellung blank; n rechtsbündig, Grundstellung 0, führende Nullen, KEINE Interpunktion — Beträge EURO-Cent kaufmännisch gerundet, Prozentsätze x1000 mit 3 Nachkommastellen), Encoding ISO-8859-15 (Standard-Encoding für Fixlängen), optionaler Dateiheader (DHKZ 2x blank + DHVN 01 + ENCD), Vorlaufsatz SART 00 (BEST/VSTR/VERS) + Schlusssatz SART 99 mit SANZ; SANR lückenlos ab 1 je Datenbestand; CRLF als Satztrenner (Praxis-Annahme, DM-ORG normiert keines — Parallellauf verifizieren).

Lohnzettel Finanz L16 (models/lohnzettel_l16.py): Informationssatz I1 (SART I1, Länge 1.100, Strukturversion 03) + Mitteilungssatz L1 (SART L1, Länge 3.500, Lohnzettelversion 28 — zwingend ab 01.03.2026) mit vollständiger, PDF-verifizierter Feldtabelle (~210 Felder inkl. Kinderblock 15x123 ab Pos. 1501; DM-ORG 42.7.0 E.13/E.14). KZ-245-Formel: KZ 210 = Bruttobezüge gem. § 25 GESAMT (DM-ORG-Feldbeschreibung) — die Formel 210-215-220-230-243 ergibt die laufende Steuerbemessung, per Konstruktion konsistent mit der GP3-§-66-Kette (Ableitung aus l10n_at_bemessung/steuerbefreiung_68/s67_abs und Regelcodes; bewusst KEIN paralleles KZ-Klassifikationsfeld). Arbeitsstättenmeldung § 34 Abs 6 ASVG: SART 45 (Länge 300, Version 02, BEST AD/VSTR ST Statistik Austria) je AN am 31.12. aus dem Dienstort-Partner (GKZ manuell, Warnung wenn fehlt) — im selben Jahreslauf (Nutzer-Entscheidung 5.1).

Kind-Datenmodell (models/kind.py — l10n.at.payroll.kind): Stammdaten-Wartung des Familienbonus Plus (Nutzer-Entscheidung 5.3: FB+ rechnet NICHT im Lohnsteuerkern — bei Unterschreiten der Steuer auf 0 erfolgt die Geltendmachung über die Arbeitnehmerveranlagung); Felder für den L16-Kinderblock (Familienname/Vorname/Geburtsdatum/VSNR/Staatsangehörigkeit/FB+-Monate voll/halb, Alterstaffel 166,68/58,34).

Stammdaten (models/meldewesen.py): Beitragskontonummer BKNR + Lohnsteuernummer (res.company), Gemeindekennziffer GKZ (res.partner), VSNR-Formatprüfung LLLPTTMMJJ auf hr.version.ssnid (Prüfzifferalgorithmus = dokumentierter Rest, SVS-Quelle beschaffen), eSV-Abmeldegrund AGRD-Mapping auf hr.departure.reason (Standard 00); fallweise-Beschäftigungs-Flag auf hr.version (§ 33 Abs 1a Z 1 ASVG).

Belege (AP6): Lohnzettel AT (ir.actions.report auf hr.payslip, AT-Layout mit Bezügen/DN-Abzügen/DG-Anteilen/Netto), Monatsdienstliste (mBGM-Kontrollschicht je AN) + Monatsjournal (je Lohnart Summen je Lauf) auf hr.payslip.run als QWeb (models/monatsreports.py + report/belege_reports.xml; account.report wird in keinem HR-Modul genutzt).

Tests: test_elda_l16 (4 — Feldformatierung/Cent-Beträge/skala, Bestands-Assembly SANR/SANZ/CRLF/ISO-8859-15/Dateiheader, L16-Lauf mit positionsexakten Assertions aus der November-Fixture, Arbeitsstätten-Satz). 19.0.4.0.0.
2026-09-12 17:30:59 +02:00

104 lines
4.9 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.
"""Kind-Datenmodell (GP7): Kinder je Mitarbeiter für die Wartung des
Familienbonus Plus und die Befüllung des L16-Kinderblocks
(Lohnzettel-Finanz-Mitteilungssatz, DM-ORG 42.7.0 E.14, Felder
Nr. 196209: je Kind 123 Stellen — KFAM/KVON/KSTAAT/KSTWE/KVSNR/
KGEBD/KAFBZ/KAPFB/KAUHZ/KBGFP/KEGFP/KBHFP/KEHFP).
Nutzer-Entscheidung 2026-09-12: die FB+-Steuerwirkung selbst ist KEIN
Bestandteil der Personalverrechnung (bei Unterschreiten der Steuer auf 0
erfolgt die Geltendmachung über die Arbeitnehmerveranlagung) — das Modell
dient der Stammdaten-Wartung (Anspruch, Monate, halber Bonus) und der
L16-Meldung. Monatsfelder im Format MM (Kinderblock 2n); die
Betragsermittlung (FABO) rechnet der L16-Generator aus den gepflegten
Monaten und der Altersstaffel (§ 33 Abs 5 EStG: bis 18. Geburtstag
166,68 €/Monat, darüber 58,34 €/Monat solange Familienbeihilfe)."""
from odoo import fields, models
class HrEmployee(models.Model):
_inherit = 'hr.employee'
l10n_at_payroll_kind_ids = fields.One2many(
'l10n.at.payroll.kind', 'employee_id',
string='Kinder (Familienbonus Plus)',
groups="hr_payroll.group_hr_payroll_user")
class L10nAtPayrollKind(models.Model):
_name = 'l10n.at.payroll.kind'
_description = 'Kind (Familienbonus Plus / Lohnzettel L16)'
_order = 'geburtstag, id'
employee_id = fields.Many2one(
'hr.employee', string='Mitarbeiter:in', required=True,
index=True, ondelete='cascade')
familienname = fields.Char(
string='Familienname (KFAM)', required=True)
vorname = fields.Char(string='Vorname (KVON)')
geburtstag = fields.Date(
string='Geburtsdatum (KGEBD)', required=True,
help='Maßgeblich für die FB+-Staffel (bis zum 18. Geburtstag '
'166,68 €/Monat, danach 58,34 €/Monat solange '
'Familienbeihilfe) und die Altersfilter (z. B. '
'Schwerarbeitsmeldung 40 M/35 F).')
versicherungsnummer = fields.Char(
string='Versicherungsnummer (KVSNR)',
help='10-stellig, LLLPTTMMJJ; nur wenn zugeteilt.')
staatsangehoerigkeit = fields.Char(
string='Staatsangehörigkeit (KSTAAT, 3-stelliger Code)')
staatsangehoerigkeit_2 = fields.Char(
string='2. Staatsangehörigkeit (KSTWE, 1-stelliger Code)')
# ------------------------------------------------------------------
# Familienbonus Plus — Wartung (L16-Kinderblock)
# ------------------------------------------------------------------
fab_plus = fields.Boolean(
string='Familienbonus Plus geltend gemacht',
help='L16 KFABO (Anzahl) und FABO (Betrag) ergeben sich aus den '
'Kindern mit gesetztem FB+-Kennzeichen.')
fab_monat_von = fields.Integer(
string='FB+ ab Monat (KBGFP)',
help='Monat (112), ab dem der volle Familienbonus Plus für '
'dieses Kind geltend gemacht wurde (L16 KBGFP).')
fab_monat_bis = fields.Integer(
string='FB+ bis Monat (KEGFP)',
help='Letzter Monat (112) des vollen Familienbonus Plus (L16 '
'KEGFP).')
fab_halb_monat_von = fields.Integer(
string='Halber FB+ ab Monat (KBHFP)',
help='Monat (112), ab dem nur mehr der halbe Familienbonus Plus '
'geltend gemacht wurde (Aufteilung zwischen den '
'Elternteilen; L16 KBHFP).')
fab_halb_monat_bis = fields.Integer(
string='Halber FB+ bis Monat (KEHFP)',
help='Letzter Monat (112) des halben Familienbonus Plus (L16 '
'KEHFP).')
def _l10n_at_fab_monate(self, jahr):
"""[(Monat, Anteil 1.0/0.5), …] des FB+ im Lohnzetteljahr aus den
gepflegten Monatsfeldern (voll + halb, Überschneidung: der
spätere Eintrag gewinnt)."""
self.ensure_one()
monate = {}
von, bis = self.fab_monat_von or 0, self.fab_monat_bis or 0
if von and bis and 1 <= von <= bis <= 12:
for monat in range(von, bis + 1):
monate[monat] = 1.0
hvon, hbis = (self.fab_halb_monat_von or 0,
self.fab_halb_monat_bis or 0)
if hvon and hbis and 1 <= hvon <= hbis <= 12:
for monat in range(hvon, hbis + 1):
monate[monat] = 0.5
return sorted(monate.items())
def _l10n_at_fab_betrag(self, jahr, voll=166.68, vermindert=58.34):
"""FB+-Jahresbetrag je Kind aus Monaten × Monatsrate; die Rate
wechselt mit dem 18. Geburtstag (monatliche Staffel: Monate vor
dem Geburtsmonat + 18 Jahre voller Satz, danach verminderter
Satz — § 33 Abs 5 EStG, solange Familienbeihilfe)."""
self.ensure_one()
if not self.geburtstag or not self.fab_plus:
return 0.0
umschalt = self.geburtstag.month
return sum(anteil * (vermindert if monat >= umschalt else voll)
for monat, anteil in self._l10n_at_fab_monate(jahr))