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_private: mBGM liest TASY-Katalog (BSGR/ERGB), AT-Abwesenheiten mit hr_holidays (AP0+AP1+AP5)
AP0 — mBGM-Tarifierung aus dem TASY-Katalog: - Katalog vor Hand-Parameter at_mbgm_tarif (quelle katalog/parameter); Company-Feld l10n_at_dienstgebergruppe (Default NBALG), Vertrags-m2m l10n_at_ergaenzung_ids; ERGB wird im T1 real belegt - BSGR-Korrektur nach TASY-Bezeichnern: Lehrlinge Arbeiter->B045 / Angestellte->B044 (invertiert bisher), geringfuegig/fallweise B010/B030 je Personalgruppe - NSchAB-Feld: Falschbezeichnung Schlechtwetter -> Nachtschwerarbeit (3,80 % = NB-Zweig DG ab 2020); Flag nur noch Parameter-Fallback - Tests: Katalog schlaegt manipulierten Parameter, ERGB-Emission, BSGR-Mapping AP1 — M4-Test auf explizit ungemappte Abgangsursache (AGRD 00-Fallback) AP5 — hr_holidays/hr_work_entry_holidays als Pflicht-Dependence: - AT-WETs ATP_URLAUB/ATP_KRANK/ATP_KARENZ (is_leave) + AT-Urlaubsarten (UrlG, Krankenstand mit Krankmeldungspflicht, unbezahlte Karenz) nach Odoo-Lokalisierungsmuster; ATP_AU (Arbeitsunfall) folgt mit AP4 - Test: validierter Krankenstand erzeugt Work Entries mit AT-WET
This commit is contained in:
@@ -5,4 +5,5 @@ from . import test_lst_private
|
||||
from . import test_sv_private
|
||||
from . import test_bv_private
|
||||
from . import test_account_private
|
||||
from . import test_meldewesen_private
|
||||
from . import test_meldewesen_private
|
||||
from . import test_reports_private
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
# Part of the odoo-at-payroll project. License: LGPL-3.
|
||||
"""GP7 privat: mBGM (Paket/mBGM/Tarifblock/Basis/Position-Hierarchie
|
||||
aus GP4-Beitragssummen, T01-Gesamtsatz aus at_mbgm_tarif, Abschläge
|
||||
A01/A02/A03 §-2a-AMPFG, A09/A10/A15, BV-Basis V01) und
|
||||
aus GP4-Beitragssummen, T01-Gesamtsatz aus TASY-Katalog vor
|
||||
at_mbgm_tarif, Abschläge A01/A02/A03 §-2a-AMPFG, A09/A10/A15,
|
||||
BV-Basis V01, BSGR-Lehrlinge/Geringfügige korrigiert, ERGB) und
|
||||
Versichertenmeldung M3/M4 (deklarative Event-Erkennung,
|
||||
Meldungs-Registry, AGRD)."""
|
||||
import base64
|
||||
@@ -10,6 +11,10 @@ import datetime
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.tests import TransactionCase, tagged
|
||||
|
||||
from odoo.addons.l10n_at_hr_payroll.tests.test_tasy_katalog import (
|
||||
TASY_KATALOG_FIXTURE,
|
||||
)
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class MeldewesenPrivateTest(TransactionCase):
|
||||
@@ -165,10 +170,16 @@ class MeldewesenPrivateTest(TransactionCase):
|
||||
})
|
||||
with self.assertRaises(UserError):
|
||||
zweiter.action_erzeugen()
|
||||
# Ursache OHNE AGRD-Mapping → M4-Fallback 00 (D.22-Standard).
|
||||
# (Suche-beliebig wäre nach dem AGRD-Seed nicht mehr deterministisch
|
||||
# — der Seed verbindet die üblichen Ursachen vor.)
|
||||
grund_ohne = self.env['hr.departure.reason'].create({
|
||||
'name': 'Testursache ohne AGRD',
|
||||
'country_id': self.env.ref('base.at').id,
|
||||
})
|
||||
self.version.write({
|
||||
'contract_date_end': datetime.date(2026, 2, 15),
|
||||
'departure_reason_id': self.env['hr.departure.reason']
|
||||
.search([], limit=1).id,
|
||||
'departure_reason_id': grund_ohne.id,
|
||||
})
|
||||
dritter = self.env['l10n.at.payroll.vr'].create({
|
||||
'company_id': self.company.id,
|
||||
@@ -176,8 +187,134 @@ class MeldewesenPrivateTest(TransactionCase):
|
||||
})
|
||||
dritter.action_erzeugen()
|
||||
roh = base64.b64decode(dritter.datei).decode('ISO-8859-15')
|
||||
zeilen = roh.strip('\r\n').split('\r\n')
|
||||
m4 = [z for z in zeilen if z[0:2] == 'M4']
|
||||
self.assertEqual(len(m4), 1)
|
||||
self.assertEqual(m4[0][512:520], '15022026') # ADAT
|
||||
self.assertEqual(m4[0][548:550], '00') # AGRD Default
|
||||
|
||||
def test_06_agrd_vorverbunden(self):
|
||||
"""Der AGRD-Seed verbindet die üblichen Abgangsursachen vor:
|
||||
M4 der Pensionierung trägt Code 16 (D.22), hr-Defaults sind
|
||||
gemappt (fired→01, resigned→02, retired→16)."""
|
||||
self.assertEqual(self.env.ref('hr.departure_fired').l10n_at_agrd,
|
||||
'01')
|
||||
self.assertEqual(self.env.ref('hr.departure_resigned')
|
||||
.l10n_at_agrd, '02')
|
||||
employee = self.env['hr.employee'].create({
|
||||
'name': 'AGRD Test', 'birthday': '1985-03-20'})
|
||||
version = self.env['hr.version'].search(
|
||||
[('employee_id', '=', employee.id)], limit=1)
|
||||
version.write({
|
||||
'contract_date_start': datetime.date(2020, 1, 1),
|
||||
'contract_date_end': datetime.date(2026, 3, 31),
|
||||
'ssnid': '5678200385',
|
||||
'sex': 'male',
|
||||
'departure_reason_id': self.env.ref(
|
||||
'hr.departure_retired').id,
|
||||
})
|
||||
lauf = self.env['l10n.at.payroll.vr'].create({
|
||||
'company_id': self.company.id,
|
||||
'stichtag': datetime.date(2026, 4, 30),
|
||||
})
|
||||
lauf.action_erzeugen()
|
||||
roh = base64.b64decode(lauf.datei).decode('ISO-8859-15')
|
||||
m4 = [z for z in roh.strip('\r\n').split('\r\n')
|
||||
if z[0:2] == 'M4']
|
||||
self.assertEqual(len(m4), 1)
|
||||
self.assertEqual(m4[0][512:520], '15022026') # ADAT
|
||||
self.assertEqual(m4[0][548:550], '00') # AGRD Default
|
||||
self.assertEqual(m4[0][548:550], '16') # AGRD Pensionierung
|
||||
|
||||
def _katalog_importieren(self):
|
||||
wizard = self.env['l10n.at.payroll.tasy.katalog.wizard'].create({
|
||||
'file': base64.b64encode(TASY_KATALOG_FIXTURE.encode())
|
||||
.decode(),
|
||||
'filename': 'fixture.xml',
|
||||
})
|
||||
wizard.action_import()
|
||||
|
||||
def test_03_bsgr_mapping(self):
|
||||
"""BSGR nach TASY-Bezeichnern: Lehrling Arbeiter→B045,
|
||||
Lehrling Angestellte→B044, fallweise/gfg Arbeiter→B010,
|
||||
Angestellte→B030, Normalfall B001/B002."""
|
||||
deklaration = self.env['l10n.at.payroll.mbgm'].new({
|
||||
'company_id': self.company.id,
|
||||
'beitragsmonat': datetime.date(2026, 11, 1),
|
||||
})
|
||||
slip = self.env['hr.payslip'].new({})
|
||||
# Normalfall
|
||||
self.assertEqual(
|
||||
deklaration._l10n_at_bsgr(self.version, slip), 'B002')
|
||||
self.version.l10n_at_personalgruppe = 'arbeiter'
|
||||
self.assertEqual(
|
||||
deklaration._l10n_at_bsgr(self.version, slip), 'B001')
|
||||
# Lehrlinge — korrigierte Zuordnung (B045 = Arbeiterlehrlinge)
|
||||
self.version.l10n_at_lehrling = True
|
||||
self.assertEqual(
|
||||
deklaration._l10n_at_bsgr(self.version, slip), 'B045')
|
||||
self.version.l10n_at_personalgruppe = 'angestellte'
|
||||
self.assertEqual(
|
||||
deklaration._l10n_at_bsgr(self.version, slip), 'B044')
|
||||
self.version.l10n_at_lehrling = False
|
||||
# Geringfügige/fallweise je Personalgruppe
|
||||
self.version.l10n_at_fallweise = True
|
||||
self.assertEqual(
|
||||
deklaration._l10n_at_bsgr(self.version, slip), 'B030')
|
||||
self.version.l10n_at_personalgruppe = 'arbeiter'
|
||||
self.assertEqual(
|
||||
deklaration._l10n_at_bsgr(self.version, slip), 'B010')
|
||||
|
||||
def test_04_mbgm_katalog_vor_parameter(self):
|
||||
"""Katalog schlägt den Hand-Parameter: B002-T01 bleibt 39,05 %
|
||||
(39050), selbst wenn at_mbgm_tarif einen abweichenden Satz
|
||||
trägt."""
|
||||
self._katalog_importieren()
|
||||
parameter = self.env['hr.rule.parameter'].search([
|
||||
('code', '=', 'at_mbgm_tarif')])
|
||||
parameter.parameter_version_ids.write(
|
||||
{'parameter_value':
|
||||
"{'B001': {'laufend': 11.0, 'sz': 11.0}, "
|
||||
"'B002': {'laufend': 11.0, 'sz': 11.0}}"})
|
||||
slip = self._slip(11)
|
||||
deklaration = self.env['l10n.at.payroll.mbgm'].create({
|
||||
'company_id': self.company.id,
|
||||
'beitragsmonat': datetime.date(2026, 11, 1),
|
||||
'art': 'MB',
|
||||
})
|
||||
deklaration.action_erzeugen()
|
||||
self.assertEqual(deklaration.state, 'done')
|
||||
roh = base64.b64decode(deklaration.datei).decode('ISO-8859-15')
|
||||
zeilen = roh.strip('\r\n').split('\r\n')
|
||||
daten = zeilen[2:-1]
|
||||
v1_t01 = daten[4]
|
||||
self.assertEqual(v1_t01[20:23], 'T01')
|
||||
self.assertEqual(v1_t01[24:30], '039050') # Katalog 39,05 %
|
||||
v1_t02 = daten[8]
|
||||
self.assertEqual(v1_t02[24:30], '037550') # Katalog 37,55 %
|
||||
# ERGB Grundstellung: blank (keine Ergänzung gepflegt)
|
||||
t1 = daten[2]
|
||||
self.assertEqual(t1[24:39], ' ' * 15)
|
||||
|
||||
def test_05_mbgm_ergb_e01(self):
|
||||
"""Ergänzung E01 + Dienstgebergruppe NBNSA: T01 enthält den
|
||||
NB-Zweig (42,85 % → 042850), ERGB-Feld trägt E01."""
|
||||
self._katalog_importieren()
|
||||
e01 = self.env['l10n.at.payroll.tasy.ergaenzung'].search([
|
||||
('code', '=', 'E01')])
|
||||
self.company.l10n_at_dienstgebergruppe = 'NBNSA'
|
||||
self.version.l10n_at_ergaenzung_ids = [(6, 0, e01.ids)]
|
||||
self._slip(11)
|
||||
deklaration = self.env['l10n.at.payroll.mbgm'].create({
|
||||
'company_id': self.company.id,
|
||||
'beitragsmonat': datetime.date(2026, 11, 1),
|
||||
'art': 'MB',
|
||||
})
|
||||
deklaration.action_erzeugen()
|
||||
self.assertEqual(deklaration.state, 'done')
|
||||
roh = base64.b64decode(deklaration.datei).decode('ISO-8859-15')
|
||||
zeilen = roh.strip('\r\n').split('\r\n')
|
||||
daten = zeilen[2:-1]
|
||||
t1 = daten[2]
|
||||
self.assertEqual(t1[24:39], 'E01' + ' ' * 12) # ERGB
|
||||
v1_t01 = daten[4]
|
||||
self.assertEqual(v1_t01[20:23], 'T01')
|
||||
self.assertEqual(v1_t01[24:30], '042850') # 42,85 % inkl. NB
|
||||
@@ -0,0 +1,242 @@
|
||||
# Part of the odoo-at-payroll project. License: LGPL-3.
|
||||
"""AP5 Abwesenheiten (hr_holidays-Anbindung, AT-WETs) und AP2a-c
|
||||
Reports: Dienstgeber-Gesamtkosten, Monatsjournal-DG-Block,
|
||||
Jahreslohnkonto je DN, Betriebsjahreslohnkonto."""
|
||||
import base64
|
||||
import datetime
|
||||
|
||||
from odoo import SUPERUSER_ID
|
||||
from odoo.tests import TransactionCase, tagged
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class ReportsPrivateTest(TransactionCase):
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.company = self.env.company
|
||||
self.company.write({
|
||||
'l10n_at_beitragskontonummer': '1412345678',
|
||||
})
|
||||
self.structure = self.env.ref(
|
||||
'l10n_at_hr_payroll_private.structure_at_privat')
|
||||
self.kv2203 = self.env.ref(
|
||||
'l10n_at_hr_payroll_private.kv_si_2203')
|
||||
self.gruppe_ii = self.env.ref(
|
||||
'l10n_at_hr_payroll_private.kv2203_grp_ii')
|
||||
self.calendar = self.env['resource.calendar'].create({
|
||||
'name': 'AT Reports 40h',
|
||||
'tz': 'Europe/Vienna',
|
||||
'hours_per_day': 8,
|
||||
'attendance_ids': [
|
||||
(0, 0, {'name': 'Tag %d' % wd, 'dayofweek': str(wd),
|
||||
'hour_from': 8.0, 'hour_to': 16.0})
|
||||
for wd in range(5)
|
||||
],
|
||||
})
|
||||
self.company.resource_calendar_id = self.calendar
|
||||
employee = self.env['hr.employee'].create({
|
||||
'name': 'Reports Test'})
|
||||
version = self.env['hr.version'].search(
|
||||
[('employee_id', '=', employee.id)], limit=1)
|
||||
version.write({
|
||||
'contract_date_start': datetime.date(2020, 1, 1),
|
||||
'kv_id': self.kv2203.id,
|
||||
'kv_gruppe_id': self.gruppe_ii.id,
|
||||
'erfahrungsstufe': 1,
|
||||
'resource_calendar_id': self.calendar.id,
|
||||
})
|
||||
self.employee = employee
|
||||
self.version = version
|
||||
|
||||
def _slip(self, monat, jahr=2026, confirm=True):
|
||||
erster = datetime.date(jahr, monat, 1)
|
||||
letzter = (erster.replace(day=28)
|
||||
+ datetime.timedelta(days=4)).replace(day=1) \
|
||||
- datetime.timedelta(days=1)
|
||||
slip = self.env['hr.payslip'].create({
|
||||
'name': 'Reports %d/%d' % (monat, jahr),
|
||||
'employee_id': self.employee.id,
|
||||
'struct_id': self.structure.id,
|
||||
'date_from': erster,
|
||||
'date_to': letzter,
|
||||
})
|
||||
slip.compute_sheet()
|
||||
if confirm:
|
||||
slip.action_payslip_done()
|
||||
return slip
|
||||
|
||||
def _run(self, monat, jahr=2026):
|
||||
"""Lohnlauf mit einem bestätigten Slip des Monats."""
|
||||
slip = self._slip(monat, jahr)
|
||||
run = self.env['hr.payslip.run'].create({
|
||||
'name': 'Reports-Lauf %02d/%d' % (monat, jahr),
|
||||
'date_start': datetime.date(jahr, monat, 1),
|
||||
'date_end': (datetime.date(jahr, monat, 1)
|
||||
.replace(day=28)
|
||||
+ datetime.timedelta(days=4)).replace(day=1)
|
||||
- datetime.timedelta(days=1),
|
||||
})
|
||||
slip.payslip_run_id = run.id
|
||||
return run, slip
|
||||
|
||||
def _total(self, slip, predicate):
|
||||
return sum(slip.line_ids.filtered(predicate).mapped('total'))
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# AP5: AT-Abwesenheiten
|
||||
# ------------------------------------------------------------------
|
||||
def test_01_absenzen_wets_und_leave_typen(self):
|
||||
"""AT-WETs (Urlaub/Krankenstand/Karenz) sind is_leave und an
|
||||
die country-gebundenen Urlaubsarten verknüpft; ein validierter
|
||||
Krankenstand erzeugt Work Entries mit dem AT-WET."""
|
||||
wets = self.env['hr.work.entry.type'].search([
|
||||
('country_id.code', '=', 'AT'), ('is_leave', '=', True)])
|
||||
codes = set(wets.mapped('code'))
|
||||
self.assertTrue({'ATP_URLAUB', 'ATP_KRANK', 'ATP_KARENZ'}
|
||||
<= codes, codes)
|
||||
urlaub_typ = self.env.ref(
|
||||
'l10n_at_hr_payroll_private.leave_type_at_urlaub')
|
||||
krankenstand_typ = self.env.ref(
|
||||
'l10n_at_hr_payroll_private.leave_type_at_krankenstand')
|
||||
self.assertEqual(urlaub_typ.work_entry_type_id.code, 'ATP_URLAUB')
|
||||
self.assertTrue(urlaub_typ.requires_allocation)
|
||||
self.assertEqual(krankenstand_typ.work_entry_type_id.code,
|
||||
'ATP_KRANK')
|
||||
self.assertTrue(krankenstand_typ.support_document)
|
||||
# Validierter Krankenstand → Work Entry mit AT-WET (Mo, 2.11.2026).
|
||||
# Work Entries müssen für den Zeitraum bereits generiert sein
|
||||
# (hr_work_entry_holidays erzeugt Leave-Einträge nur „in bereits
|
||||
# generierte Zeiträume“ — _cancel_work_entry_conflict).
|
||||
self.version.generate_work_entries(
|
||||
datetime.date(2026, 11, 1), datetime.date(2026, 11, 30))
|
||||
leave = self.env['hr.leave'].create({
|
||||
'name': 'Krankenstand Test',
|
||||
'employee_id': self.employee.id,
|
||||
'holiday_status_id': krankenstand_typ.id,
|
||||
'request_date_from': datetime.date(2026, 11, 2),
|
||||
'request_date_to': datetime.date(2026, 11, 2),
|
||||
})
|
||||
leave.with_user(SUPERUSER_ID)._action_validate()
|
||||
work_entries = self.env['hr.work.entry'].search([
|
||||
('leave_id', '=', leave.id)])
|
||||
self.assertTrue(work_entries)
|
||||
self.assertTrue(all(
|
||||
w.work_entry_type_id.code == 'ATP_KRANK'
|
||||
for w in work_entries))
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# AP2a: Dienstgeber-Gesamtkosten
|
||||
# ------------------------------------------------------------------
|
||||
def test_02_dg_kosten_report(self):
|
||||
"""Je AN: Brutto, aufgeschlüsselte LNK (SV-DG, FLAF, KommSt,
|
||||
BV) und kumulierte Gesamtkosten = Brutto + Σ LNK."""
|
||||
run, slip = self._run(11)
|
||||
werte = self.env[
|
||||
'report.l10n_at_hr_payroll.report_dg_kosten'
|
||||
]._get_report_values(run.ids)
|
||||
self.assertEqual(len(werte['zeilen']), 1)
|
||||
zeile = werte['zeilen'][0]
|
||||
brutto = self._total(
|
||||
slip, lambda l: l.salary_rule_id.category_id.code
|
||||
== 'ATP_BRUTTO'
|
||||
and l.salary_rule_id.l10n_at_bemessung != 'neutral')
|
||||
dg = self._total(
|
||||
slip, lambda l: l.salary_rule_id.category_id.code
|
||||
== 'ATP_DG')
|
||||
self.assertAlmostEqual(zeile['brutto'], brutto, places=2)
|
||||
self.assertAlmostEqual(zeile['dg_summe'], dg, places=2)
|
||||
self.assertAlmostEqual(zeile['gesamtkosten'],
|
||||
brutto + dg, places=2)
|
||||
self.assertAlmostEqual(zeile['spalten']['FLAFDB'],
|
||||
self._total(slip, lambda l: l.code
|
||||
== 'FLAFDB'), places=2)
|
||||
self.assertAlmostEqual(zeile['spalten']['KOMMST'],
|
||||
self._total(slip, lambda l: l.code
|
||||
== 'KOMMST'), places=2)
|
||||
self.assertAlmostEqual(zeile['spalten']['BVG'],
|
||||
self._total(slip, lambda l: l.code
|
||||
== 'BVG'), places=2)
|
||||
self.assertAlmostEqual(zeile['svdg'],
|
||||
slip._l10n_at_svdg_gesamt()
|
||||
+ sum(slip._l10n_at_svdg_sz(k) for k in
|
||||
('kv', 'pv', 'av', 'uv', 'iesg')),
|
||||
places=2)
|
||||
# Kumuliert über den Lauf
|
||||
self.assertAlmostEqual(werte['summen']['gesamtkosten'],
|
||||
zeile['gesamtkosten'], places=2)
|
||||
|
||||
def test_03_monatsjournal_dg_block(self):
|
||||
"""Monatsjournal führt die Dienstgeberanteile (ATP_DG) als
|
||||
eigenen Block — SVDG_KV/FLAFDB/KOMMST/BVG erscheinen."""
|
||||
run, slip = self._run(11)
|
||||
werte = self.env[
|
||||
'report.l10n_at_hr_payroll.report_monatsjournal'
|
||||
]._get_report_values(run.ids)
|
||||
eintrag = werte['journal'][0]
|
||||
dg_codes = {art['code'] for art in eintrag['dg_arten']}
|
||||
self.assertIn('SVDG_KV', dg_codes)
|
||||
self.assertIn('FLAFDB', dg_codes)
|
||||
self.assertIn('KOMMST', dg_codes)
|
||||
self.assertIn('BVG', dg_codes)
|
||||
self.assertAlmostEqual(
|
||||
eintrag['summe_dg'],
|
||||
self._total(slip, lambda l: l.salary_rule_id.category_id
|
||||
.code == 'ATP_DG'), places=2)
|
||||
# Bezüge/Abzüge ohne DG-Zeilen (Bemessungsfilter)
|
||||
art_codes = {art['code'] for art in eintrag['arten']}
|
||||
self.assertNotIn('SVDG_KV', art_codes)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# AP2b/2c: Jahreslohnkonto / Betriebsjahreslohnkonto
|
||||
# ------------------------------------------------------------------
|
||||
def test_04_jahreslohnkonto_je_dn(self):
|
||||
"""Monatsmatrix je Lohnart: KVGEO in November und Dezember,
|
||||
Jahressumme = Summe beider Monate."""
|
||||
slip_nov = self._slip(11)
|
||||
slip_dez = self._slip(12)
|
||||
wizard = self.env[
|
||||
'l10n.at.payroll.jahresbericht.wizard'
|
||||
].create({'jahr': 2026, 'art': 'dn',
|
||||
'employee_ids': [(6, 0, self.employee.ids)]})
|
||||
zeilen = wizard._l10n_at_lohnkonto_zeilen(
|
||||
2026, self.company.id, self.employee.id)
|
||||
kvgeo = next(z for z in zeilen if z['code'] == 'KVGEO')
|
||||
total_soll = sum(
|
||||
slip_nov.line_ids.filtered(lambda l: l.code == 'KVGEO')
|
||||
.mapped('total')) + sum(
|
||||
slip_dez.line_ids.filtered(lambda l: l.code == 'KVGEO')
|
||||
.mapped('total'))
|
||||
self.assertAlmostEqual(kvgeo['monate'][10],
|
||||
sum(slip_nov.line_ids.filtered(
|
||||
lambda l: l.code == 'KVGEO')
|
||||
.mapped('total')), places=2)
|
||||
self.assertAlmostEqual(kvgeo['monate'][11],
|
||||
sum(slip_dez.line_ids.filtered(
|
||||
lambda l: l.code == 'KVGEO')
|
||||
.mapped('total')), places=2)
|
||||
self.assertAlmostEqual(kvgeo['total'], total_soll, places=2)
|
||||
# Report-Wrapper liefert denselben Datenstand je DN
|
||||
werte = self.env[
|
||||
'report.l10n_at_hr_payroll.report_jahreslohnkonto'
|
||||
]._get_report_values(self.employee.ids, {'jahr': 2026})
|
||||
self.assertEqual(len(werte['berichte']), 1)
|
||||
self.assertEqual(werte['jahr'], 2026)
|
||||
|
||||
def test_05_betriebslohnkonto(self):
|
||||
"""Betriebsjahreslohnkonto: je Lohnart Betriebssummen (ohne
|
||||
DN-Split), Monate konsolidiert."""
|
||||
self._slip(11)
|
||||
self._slip(12)
|
||||
wizard = self.env[
|
||||
'l10n.at.payroll.jahresbericht.wizard'
|
||||
].create({'jahr': 2026, 'art': 'betrieb'})
|
||||
zeilen = wizard._l10n_at_lohnkonto_zeilen(2026, self.company.id)
|
||||
self.assertTrue(zeilen)
|
||||
kvgeo = next(z for z in zeilen if z['code'] == 'KVGEO')
|
||||
self.assertAlmostEqual(
|
||||
kvgeo['total'], sum(kvgeo['monate']), places=2)
|
||||
werte = self.env[
|
||||
'report.l10n_at_hr_payroll.report_betriebslohnkonto'
|
||||
]._get_report_values(self.company.ids, {'jahr': 2026})
|
||||
self.assertEqual(len(werte['berichte']), 1)
|
||||
Reference in New Issue
Block a user