c474a39466
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.
104 lines
4.9 KiB
Python
104 lines
4.9 KiB
Python
# 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. 196–209: 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 (1–12), 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 (1–12) des vollen Familienbonus Plus (L16 '
|
||
'KEGFP).')
|
||
fab_halb_monat_von = fields.Integer(
|
||
string='Halber FB+ ab Monat (KBHFP)',
|
||
help='Monat (1–12), 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 (1–12) 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)) |