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.
90 lines
4.0 KiB
Python
90 lines
4.0 KiB
Python
# Part of the odoo-at-payroll project. License: LGPL-3.
|
|
"""Monatsdienstliste und Monatsjournal (GP7/AP6): QWeb-Datenlieferanten
|
|
auf dem Lohnlauf (hr.payslip.run) — voraberechnete Aggregation über
|
|
`hr.payslip.line` (read_group-Muster des Contribution Register), damit
|
|
die Templates reine Darstellung bleiben.
|
|
|
|
- **Monatsdienstliste**: je AN eine Zeile mit den mBGM-Kontrollgrößen
|
|
(Versicherungstage/Beitragsgrundlagen je Beitragsmonat, DN- und
|
|
DG-Beiträge, DG-Anteile) — Abstimmung Abrechnung ↔ SV-Meldung.
|
|
- **Monatsjournal**: je Lohnart die Summe über den Lauf (Brutto,
|
|
Abzüge, DG-Anteile, Netto) mit Kontrollsummen gegen die
|
|
GP6-Buchung."""
|
|
from odoo import models
|
|
|
|
|
|
class L10nAtPayrollMonatsReports(models.AbstractModel):
|
|
_name = 'report.l10n_at_hr_payroll.report_monatsdienstliste'
|
|
_description = 'Monatsdienstliste (GP7/AP6)'
|
|
|
|
def _get_report_values(self, docids, data=None):
|
|
runs = self.env['hr.payslip.run'].browse(docids)
|
|
zeilen = []
|
|
for run in runs:
|
|
for slip in run.slip_ids.filtered(
|
|
lambda p: p.state not in ('draft', 'cancel')):
|
|
version = slip.version_id
|
|
basen = slip._l10n_at_sv_basis_laufen_monate()
|
|
sv_dn = sum(
|
|
slip._l10n_at_svdn(k) for k in
|
|
('kv', 'pv', 'av', 'ak', 'wf'))
|
|
sv_dg = slip._l10n_at_svdg_gesamt()
|
|
sz_dn = slip._l10n_at_svdn_sz()
|
|
sz_basis = slip._l10n_at_svdn_basis_sz()
|
|
zeilen.append({
|
|
'slip': slip,
|
|
'versicherungsnummer': version.ssnid if version else '',
|
|
'personalgruppe': getattr(
|
|
version, 'l10n_at_personalgruppe', '')
|
|
if version else '',
|
|
'geringfuegig': slip.l10n_at_geringfuegig,
|
|
'basis_laufen': sum(
|
|
b for _m, b in basen),
|
|
'sz_basis': sz_basis,
|
|
'sv_dn': sv_dn,
|
|
'sv_dg': sv_dg,
|
|
'sv_dn_sz': sz_dn,
|
|
'lohnsteuer': sum(
|
|
slip.line_ids.filtered(
|
|
lambda l: l.code in ('LST_LFD', 'LST_SZ')
|
|
).mapped('total')),
|
|
'netto': sum(
|
|
slip.line_ids.filtered(
|
|
lambda l: l.code == 'NET').mapped('total')),
|
|
'brutto': sum(
|
|
slip.line_ids.filtered(
|
|
lambda l: l.salary_rule_id.l10n_at_bemessung
|
|
!= 'neutral').mapped('total')),
|
|
})
|
|
return {'docs': runs, 'zeilen': zeilen}
|
|
|
|
|
|
class L10nAtPayrollMonatsjournal(models.AbstractModel):
|
|
_name = 'report.l10n_at_hr_payroll.report_monatsjournal'
|
|
_description = 'Monatsjournal (GP7/AP6)'
|
|
|
|
def _get_report_values(self, docids, data=None):
|
|
runs = self.env['hr.payslip.run'].browse(docids)
|
|
zeilen = []
|
|
for run in runs:
|
|
gruppen = {}
|
|
for slip in run.slip_ids.filtered(
|
|
lambda p: p.state not in ('draft', 'cancel')):
|
|
for line in slip.line_ids:
|
|
regel = line.salary_rule_id
|
|
if not line.total or regel.l10n_at_bemessung \
|
|
== 'neutral':
|
|
continue
|
|
schluessel = (regel.code, regel.name,
|
|
regel.l10n_at_bemessung)
|
|
eintrag = gruppen.setdefault(schluessel, 0.0)
|
|
gruppen[schluessel] = eintrag + line.total
|
|
zeilen.append({
|
|
'run': run,
|
|
'arten': [
|
|
{'code': code, 'name': name, 'bemessung': bemessung,
|
|
'summe': summe}
|
|
for (code, name, bemessung), summe
|
|
in sorted(gruppen.items())],
|
|
})
|
|
return {'docs': runs, 'journal': zeilen} |