mirror of
http://100.103.83.12:3003/fegger/odoo-at-payroll.git
synced 2026-09-17 08:52:45 +00:00
[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
This commit is contained in:
@@ -1,16 +1,29 @@
|
||||
# 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.
|
||||
"""Monatsreports und Jahresberichte der AT-Lohnverrechnung.
|
||||
|
||||
- **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
|
||||
- **Monatsdienstliste** (GP7/AP6): je AN die mBGM-Kontrollgrößen —
|
||||
Abstimmung Abrechnung ↔ SV-Meldung.
|
||||
- **Monatsjournal** (GP7/AP6, AP2a erweitert): je Lohnart die Summe
|
||||
über den Lauf, gegliedert nach Bezügen/Abzügen (Bemessung ≠
|
||||
neutral) und — seit AP2a — **eigenem Block Dienstgeberanteile**
|
||||
(Kategorie ``ATP_DG``: SV-DG je Zweig, FLAF-DB, KommSt, DZ, BV —
|
||||
zuvor wurden die neutralen DG-Regeln still ausgefiltert) mit
|
||||
Kontrollsummen gegen die GP6-Buchung.
|
||||
- **Dienstgeber-Gesamtkosten** (AP2a): je AN Bruttolohn und die
|
||||
Lohnnebenkosten aufgeschlüsselt (SV-DG laufend/SZ, FLAF-DB,
|
||||
KommSt, DZ, BV, weitere DG-Positionen) und kumuliert
|
||||
(Σ Lohnnebenkosten, Gesamtkosten = Brutto + LNK) mit Laufsummen.
|
||||
- **Jahreslohnkonto je Dienstnehmer:in** (AP2b) und
|
||||
**Betriebsjahreslohnkonto** (AP2c): Monatsmatrix je Lohnart aus
|
||||
``hr.payslip.line`` — Datenbereitstellung über den Wizard
|
||||
``l10n.at.payroll.jahresbericht.wizard``.
|
||||
|
||||
Die Templates bleiben reine Darstellung (QWeb); alle Zahlen werden
|
||||
hier vorberechnet (read_group-Muster des Contribution Register).
|
||||
"""
|
||||
from datetime import date
|
||||
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class L10nAtPayrollMonatsReports(models.AbstractModel):
|
||||
@@ -61,24 +74,32 @@ class L10nAtPayrollMonatsReports(models.AbstractModel):
|
||||
|
||||
class L10nAtPayrollMonatsjournal(models.AbstractModel):
|
||||
_name = 'report.l10n_at_hr_payroll.report_monatsjournal'
|
||||
_description = 'Monatsjournal (GP7/AP6)'
|
||||
_description = 'Monatsjournal (GP7/AP6, AP2a)'
|
||||
|
||||
def _get_report_values(self, docids, data=None):
|
||||
runs = self.env['hr.payslip.run'].browse(docids)
|
||||
zeilen = []
|
||||
for run in runs:
|
||||
gruppen = {}
|
||||
dg_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':
|
||||
if not line.total:
|
||||
continue
|
||||
kategorie = regel.category_id.code or ''
|
||||
schluessel = (regel.code, regel.name,
|
||||
regel.l10n_at_bemessung)
|
||||
eintrag = gruppen.setdefault(schluessel, 0.0)
|
||||
gruppen[schluessel] = eintrag + line.total
|
||||
if kategorie == 'ATP_DG':
|
||||
# AP2a: Dienstgeberanteile eigener Block —
|
||||
# die neutralen DG-Regeln (SVDG_*, FLAFDB,
|
||||
# KOMMST, DZ, BVG) nicht mehr ausfiltern.
|
||||
dg_gruppen[schluessel] = \
|
||||
dg_gruppen.get(schluessel, 0.0) + line.total
|
||||
elif regel.l10n_at_bemessung != 'neutral':
|
||||
gruppen[schluessel] = \
|
||||
gruppen.get(schluessel, 0.0) + line.total
|
||||
zeilen.append({
|
||||
'run': run,
|
||||
'arten': [
|
||||
@@ -86,5 +107,182 @@ class L10nAtPayrollMonatsjournal(models.AbstractModel):
|
||||
'summe': summe}
|
||||
for (code, name, bemessung), summe
|
||||
in sorted(gruppen.items())],
|
||||
'dg_arten': [
|
||||
{'code': code, 'name': name, 'bemessung': bemessung,
|
||||
'summe': summe}
|
||||
for (code, name, bemessung), summe
|
||||
in sorted(dg_gruppen.items())],
|
||||
'summe_arten': sum(gruppen.values()),
|
||||
'summe_dg': sum(dg_gruppen.values()),
|
||||
})
|
||||
return {'docs': runs, 'journal': zeilen}
|
||||
return {'docs': runs, 'journal': zeilen}
|
||||
|
||||
|
||||
class L10nAtPayrollDgKosten(models.AbstractModel):
|
||||
_name = 'report.l10n_at_hr_payroll.report_dg_kosten'
|
||||
_description = 'Dienstgeber-Gesamtkosten (AP2a)'
|
||||
|
||||
# Festspalten des Reports; weitere DG-Positionen (z. B. §-53a-UV
|
||||
# der Geringfügigen) laufen unter 'weitere'.
|
||||
_SPALTEN = ('FLAFDB', 'KOMMST', 'DZ', 'BVG', 'WIEN_DAG')
|
||||
|
||||
def _get_report_values(self, docids, data=None):
|
||||
runs = self.env['hr.payslip.run'].browse(docids)
|
||||
zeilen = []
|
||||
summen = {
|
||||
'brutto': 0.0, 'svdg': 0.0, 'dg_summe': 0.0,
|
||||
'gesamtkosten': 0.0,
|
||||
'spalten': {code: 0.0 for code in self._SPALTEN},
|
||||
'weitere': 0.0,
|
||||
}
|
||||
for run in runs:
|
||||
for slip in run.slip_ids.filtered(
|
||||
lambda p: p.state not in ('draft', 'cancel')):
|
||||
brutto = sum(
|
||||
slip.line_ids.filtered(
|
||||
lambda l: l.total
|
||||
and l.salary_rule_id.category_id.code
|
||||
== 'ATP_BRUTTO'
|
||||
and l.salary_rule_id.l10n_at_bemessung != 'neutral'
|
||||
).mapped('total'))
|
||||
dg_komponenten = {}
|
||||
for line in slip.line_ids:
|
||||
regel = line.salary_rule_id
|
||||
if not line.total \
|
||||
or regel.category_id.code != 'ATP_DG':
|
||||
continue
|
||||
dg_komponenten[regel.code] = \
|
||||
dg_komponenten.get(regel.code, 0.0) + line.total
|
||||
svdg = (slip._l10n_at_svdg_gesamt()
|
||||
+ sum(slip._l10n_at_svdg_sz(k) for k in
|
||||
('kv', 'pv', 'av', 'uv', 'iesg')))
|
||||
spalten = {code: dg_komponenten.get(code, 0.0)
|
||||
for code in self._SPALTEN}
|
||||
dg_summe = sum(dg_komponenten.values())
|
||||
weitere = dg_summe - svdg - sum(spalten.values())
|
||||
zeilen.append({
|
||||
'slip': slip,
|
||||
'brutto': brutto,
|
||||
'svdg': svdg,
|
||||
'spalten': spalten,
|
||||
'weitere': max(weitere, 0.0),
|
||||
'dg_summe': dg_summe,
|
||||
'gesamtkosten': brutto + dg_summe,
|
||||
})
|
||||
summen['brutto'] += brutto
|
||||
summen['svdg'] += svdg
|
||||
summen['dg_summe'] += dg_summe
|
||||
summen['gesamtkosten'] += brutto + dg_summe
|
||||
summen['weitere'] += max(weitere, 0.0)
|
||||
for code in self._SPALTEN:
|
||||
summen['spalten'][code] += spalten[code]
|
||||
return {'docs': runs, 'zeilen': zeilen, 'summen': summen}
|
||||
|
||||
|
||||
class L10nAtPayrollJahresberichtWizard(models.TransientModel):
|
||||
"""Druck-Wrapper für Jahreslohnkonto (je DN) und
|
||||
Betriebsjahreslohnkonto (Summen je Lohnart) — AP2b/AP2c."""
|
||||
_name = 'l10n.at.payroll.jahresbericht.wizard'
|
||||
_description = 'Jahresberichte: Lohnkonten drucken'
|
||||
|
||||
jahr = fields.Integer(
|
||||
required=True,
|
||||
default=lambda self: fields.Date.context_today(self).year)
|
||||
art = fields.Selection(
|
||||
[('dn', 'Jahreslohnkonto je Dienstnehmer:in'),
|
||||
('betrieb', 'Betriebsjahreslohnkonto (Summen je Lohnart)')],
|
||||
required=True, default='dn')
|
||||
company_id = fields.Many2one(
|
||||
'res.company', required=True,
|
||||
default=lambda self: self.env.company)
|
||||
employee_ids = fields.Many2many(
|
||||
'hr.employee', string='Dienstnehmer:innen',
|
||||
help='Leer = alle Dienstnehmer:innen des Unternehmens.')
|
||||
|
||||
def action_drucken(self):
|
||||
self.ensure_one()
|
||||
if self.art == 'dn':
|
||||
employees = self.employee_ids or self.env[
|
||||
'hr.employee'].search(
|
||||
[('company_id', '=', self.company_id.id)])
|
||||
report = self.env.ref(
|
||||
'l10n_at_hr_payroll.action_report_jahreslohnkonto')
|
||||
return report.report_action(
|
||||
employees, data={'jahr': self.jahr})
|
||||
report = self.env.ref(
|
||||
'l10n_at_hr_payroll.action_report_betriebslohnkonto')
|
||||
return report.report_action(
|
||||
self.company_id, data={'jahr': self.jahr})
|
||||
|
||||
@api.model
|
||||
def _l10n_at_lohnkonto_zeilen(self, jahr, company_id,
|
||||
employee_id=None):
|
||||
"""Aggregierte Lohnartzeilen des Jahres: [(code, name,
|
||||
bemessung, kategorie, [Jan..Dez], total), …] — sortiert nach
|
||||
Kategorie und Code."""
|
||||
domain = [
|
||||
('company_id', '=', company_id),
|
||||
('date_from', '>=', date(jahr, 1, 1)),
|
||||
('date_from', '<=', date(jahr, 12, 31)),
|
||||
('slip_id.state', 'not in', ('draft', 'cancel')),
|
||||
]
|
||||
if employee_id:
|
||||
domain.append(('employee_id', '=', employee_id))
|
||||
zeilen = {}
|
||||
for line in self.env['hr.payslip.line'].search(domain):
|
||||
regel = line.salary_rule_id
|
||||
schluessel = (regel.code, regel.name,
|
||||
regel.l10n_at_bemessung,
|
||||
regel.category_id.code or '')
|
||||
eintrag = zeilen.setdefault(
|
||||
schluessel, {'monate': [0.0] * 12, 'total': 0.0})
|
||||
eintrag['monate'][line.date_from.month - 1] += line.total
|
||||
eintrag['total'] += line.total
|
||||
result = []
|
||||
for (code, name, bemessung, kategorie), eintrag in sorted(
|
||||
zeilen.items()):
|
||||
result.append({
|
||||
'code': code, 'name': name, 'bemessung': bemessung,
|
||||
'kategorie': kategorie,
|
||||
'monate': eintrag['monate'], 'total': eintrag['total'],
|
||||
})
|
||||
return result
|
||||
|
||||
|
||||
class L10nAtPayrollJahreslohnkonto(models.AbstractModel):
|
||||
_name = 'report.l10n_at_hr_payroll.report_jahreslohnkonto'
|
||||
_description = 'Jahreslohnkonto je Dienstnehmer:in (AP2b)'
|
||||
|
||||
def _get_report_values(self, docids, data=None):
|
||||
jahr = (data or {}).get('jahr') \
|
||||
or fields.Date.context_today(self).year
|
||||
employees = self.env['hr.employee'].browse(docids)
|
||||
berichte = []
|
||||
for employee in employees:
|
||||
berichte.append({
|
||||
'employee': employee,
|
||||
'zeilen': self.env[
|
||||
'l10n.at.payroll.jahresbericht.wizard'
|
||||
]._l10n_at_lohnkonto_zeilen(
|
||||
jahr, employee.company_id.id, employee.id),
|
||||
})
|
||||
return {'docs': employees, 'jahr': jahr, 'berichte': berichte}
|
||||
|
||||
|
||||
class L10nAtPayrollBetriebslohnkonto(models.AbstractModel):
|
||||
_name = 'report.l10n_at_hr_payroll.report_betriebslohnkonto'
|
||||
_description = 'Betriebsjahreslohnkonto (AP2c)'
|
||||
|
||||
def _get_report_values(self, docids, data=None):
|
||||
jahr = (data or {}).get('jahr') \
|
||||
or fields.Date.context_today(self).year
|
||||
companies = self.env['res.company'].browse(docids)
|
||||
berichte = []
|
||||
for company in companies:
|
||||
berichte.append({
|
||||
'company': company,
|
||||
'zeilen': self.env[
|
||||
'l10n.at.payroll.jahresbericht.wizard'
|
||||
]._l10n_at_lohnkonto_zeilen(jahr, company.id),
|
||||
})
|
||||
return {'docs': companies, 'jahr': jahr, 'berichte': berichte}
|
||||
Reference in New Issue
Block a user