mirror of
http://100.103.83.12:3003/fegger/odoo-at-payroll.git
synced 2026-09-17 08:52:45 +00:00
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.
96 lines
4.2 KiB
Python
96 lines
4.2 KiB
Python
# Part of the odoo-at-payroll project. License: LGPL-3.
|
||
"""Meldewesen-Stammdaten (GP7/AP5): ELDA-/FinOnl-Identifikation der
|
||
Dienstgeberin und der Arbeitsstätten.
|
||
|
||
- ``res.company``: Beitragskontonummer (BKNR — ELDA-Feld, Präfix =
|
||
KV-Träger) und Lohnsteuernummer (9-stellig: 2 Stellen Finanzamt + 7
|
||
Stellen Nummer; L16-I1 STNRA/STNRL, Arbeitsstättenmeldung STNR).
|
||
- ``res.partner``: österreichische Gemeindekennziffer (AGKZ) für die
|
||
Arbeitsstättenmeldung nach § 34 Abs 6 ASVG — Odoo hält keine GKZ, das
|
||
Feld ist mandantenseitig zu pflegen (Warnung des L16-Laufs, wenn es am
|
||
Dienstort-Partner fehlt).
|
||
- ``hr.version``: Kennzeichen fallweise Beschäftigung (§ 33 Abs 1a
|
||
Z 1 ASVG — tagesgenaue vereinfachte Anmeldung vor Arbeitsantritt,
|
||
DM-ORG BEST MA) und VSNR-Formatprüfung (10-stellig LLLPTTMMJJ;
|
||
Prüfzifferalgorithmus der SVS ist nicht beschafft → nur Format/
|
||
Plausibilität, Rest dokumentiert).
|
||
- ``hr.departure.reason``: Mapping auf den eSV-Abmeldegrund (AGRD,
|
||
DM-ORG D.22; Standard ``00`` sonstiger Grund mit Ende des
|
||
Beschäftigungsverhältnisses)."""
|
||
from odoo import api, fields, models
|
||
|
||
from odoo.exceptions import ValidationError
|
||
|
||
|
||
class ResCompany(models.Model):
|
||
_inherit = 'res.company'
|
||
|
||
l10n_at_beitragskontonummer = fields.Char(
|
||
string='AT Beitragskontonummer (BKNR)',
|
||
help='eSV-Beitragskontonummer des Dienstgebers (10 Stellen;\n'
|
||
'die ersten Stellen codieren den KV-Träger). Pflichtfeld\n'
|
||
'jeder ELDA-Meldung (mBGM-Paket BKNR, Versichertenmeldung,\n'
|
||
'Arbeitsstättenmeldung, fallweise Anmeldung).')
|
||
l10n_at_lohnsteuernummer = fields.Char(
|
||
string='AT Lohnsteuernummer',
|
||
help='9-stellig: 2 Stellen Finanzamtsnummer + 7 Stellen\n'
|
||
'Steuernummer (Lohnzettel Finanz I1 STNRA/STNRL;\n'
|
||
'Arbeitsstättenmeldung STNR).')
|
||
|
||
|
||
class ResPartner(models.Model):
|
||
_inherit = 'res.partner'
|
||
|
||
l10n_at_gemeindekennziffer = fields.Char(
|
||
string='AT Gemeindekennziffer (GKZ)',
|
||
help='5-stellige österreichische Gemeindekennzahl für die\n'
|
||
'Arbeitsstättenmeldung nach § 34 Abs 6 ASVG (Feld AGKZ).\n'
|
||
'Am Dienstort-Partner des Mitarbeiters pflegen.')
|
||
|
||
|
||
class HrVersion(models.Model):
|
||
_inherit = 'hr.version'
|
||
|
||
l10n_at_fallweise = fields.Boolean(
|
||
string='AT Fallweise Beschäftigung',
|
||
groups="hr_payroll.group_hr_payroll_user", tracking=True,
|
||
help='Fallweise Beschäftigung (§ 33 Abs 1a Z 1 ASVG): je\n'
|
||
'Beschäftigungstag ist die vereinfachte Anmeldung vor\n'
|
||
'Arbeitsantritt zu übermitteln (ELDA BEST MA, Satzarten\n'
|
||
'32/33); An-/Abmeldung laufen über die mBGM (G3/G4).')
|
||
|
||
@api.constrains('ssnid')
|
||
def _l10n_at_vsnr_format(self):
|
||
"""VSNR-Formatprüfung LLLPTTMMJJ (10 Stellen, Ziffern, plausibles
|
||
Geburtsdatum TTMMJJ an den Stellen 5–10). Der
|
||
Prüfzifferalgorithmus der SVS ist nicht beschafft → nur
|
||
Format/Plausibilität (Rest dokumentiert; die Meldungsgeneratoren
|
||
listen ungültige VSNR listenweise als UserError)."""
|
||
for version in self:
|
||
if not version.ssnid:
|
||
continue
|
||
roh = version.ssnid.strip()
|
||
if len(roh) != 10 or not roh.isdigit():
|
||
raise ValidationError(
|
||
'Versicherungsnummer %r: 10 Ziffern im Format LLLPTTMMJJ '
|
||
'erwartet.' % version.ssnid)
|
||
try:
|
||
tag = int(roh[4:6])
|
||
monat = int(roh[6:8])
|
||
if not 1 <= tag <= 31 or not 1 <= monat <= 12:
|
||
raise ValueError()
|
||
except ValueError:
|
||
raise ValidationError(
|
||
'Versicherungsnummer %r enthält kein plausibles '
|
||
'Geburtsdatum (TTMMJJ an den Stellen 5–10).'
|
||
% version.ssnid)
|
||
|
||
|
||
class HrDepartureReason(models.Model):
|
||
_inherit = 'hr.departure.reason'
|
||
|
||
l10n_at_agrd = fields.Char(
|
||
string='AT eSV-Abmeldegrund (AGRD)',
|
||
help='2-stelliger Abmeldegrund-Code der Versichertenmeldung '
|
||
'(DM-ORG D.22); Standard 00 „Sonstiger Grund mit Ende des '
|
||
'Beschäftigungsverhältnisses“.') |