1df5fb19c4
Document generation module: templates with own text (structure reference and source stand documented per template, no licensed full text in the repo), fill fields resolved from hr data (company, employee, hr.version stand) plus wizard input, preview rendering and PDF/HTML attachment via the generic report layout. - templates: pilot set for the general-AT track formulated as own text after the KB muster structure (lb-bes-09, lb-bnd-55, lb-bnd-52/62, lb-end-35), legal status per template marked as draft - rendering via convert_inline_template_to_qweb + ir.qweb (t-out auto-escapes values) — deliberately not mail.render.mixin inline engine, whose expression whitelist blocks werte[...] placeholders for non-template-editors - wizard resolves hr.version by date_version at the reference date; PDF falls back to HTML attachment without wkhtmltopdf - security: ir.model.access (hr user read / manager write) + company rule; 8 tests, suite 236/236 green on a fresh -i database run
183 lines
7.7 KiB
Python
183 lines
7.7 KiB
Python
# Part of the odoo-at-payroll project. License: LGPL-3.
|
|
"""Tests: Dokumentgenerierung aus Vorlagen.
|
|
|
|
Rendering der eigenen Vorlagentexte (werte['key']-Platzhalter), Auto-Werte
|
|
aus hr-Stammdaten, Pflichtfeld-Validierung, hr.version-Stichtagsauflösung,
|
|
generisches Druck-Template und Anhang-Ablage (PDF wenn wkhtmltopdf
|
|
verfügbar, sonst HTML-Fallback)."""
|
|
import base64
|
|
import datetime
|
|
|
|
from odoo.exceptions import AccessError, UserError
|
|
from odoo.tests import TransactionCase, tagged
|
|
|
|
|
|
@tagged('post_install', '-at_install')
|
|
class DokumenteTest(TransactionCase):
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.company = self.env.company
|
|
self.company.write({'name': 'Musterwerkstatt GmbH'})
|
|
employee = self.env['hr.employee'].create({
|
|
'name': 'Dokument Test'})
|
|
version = self.env['hr.version'].search(
|
|
[('employee_id', '=', employee.id)], limit=1)
|
|
version.write({
|
|
'date_version': datetime.date(2020, 1, 1),
|
|
'contract_date_start': datetime.date(2020, 1, 1),
|
|
'job_title': 'Techniker',
|
|
'private_street': 'Testgasse 1',
|
|
'private_zip': '1010',
|
|
'private_city': 'Wien',
|
|
})
|
|
self.employee = employee
|
|
self.version = version
|
|
self.vorlage = self.env['l10n.at.payroll.dokument.vorlage'].create({
|
|
'name': 'Testbescheinigung',
|
|
'thema': 'bescheinigung',
|
|
'kb_ref': 'lb-test',
|
|
'rechtsstatus': 'entwurf',
|
|
'inhalt_html':
|
|
'<p>Firma: {{ werte[\'firma\'] }}</p>'
|
|
'<p>Mitarbeiter: {{ werte[\'mitarbeiter\'] }}</p>'
|
|
'<p>Funktion: {{ werte[\'funktion\'] }}</p>'
|
|
'<p>EAD: {{ werte[\'ead\'] }}</p>'
|
|
'<p>Zusatz: {{ werte[\'zusatz\'] }}</p>',
|
|
'feld_ids': [
|
|
(0, 0, {'sequence': 1, 'key': 'zusatz',
|
|
'quelle': 'manuell', 'pflicht': True,
|
|
'bezeichnung': 'Zusatzangabe'}),
|
|
(0, 0, {'sequence': 2, 'key': 'firma', 'quelle': 'auto',
|
|
'pflicht': True}),
|
|
(0, 0, {'sequence': 3, 'key': 'mitarbeiter',
|
|
'quelle': 'auto', 'pflicht': True}),
|
|
(0, 0, {'sequence': 4, 'key': 'funktion', 'quelle': 'auto',
|
|
'pflicht': False}),
|
|
(0, 0, {'sequence': 5, 'key': 'ead', 'quelle': 'auto',
|
|
'pflicht': False}),
|
|
],
|
|
})
|
|
|
|
def _wizard(self):
|
|
wizard = self.env['l10n.at.payroll.dokument.wizard'].create({
|
|
'vorlage_id': self.vorlage.id,
|
|
'employee_id': self.employee.id,
|
|
'datum': datetime.date(2026, 1, 15),
|
|
})
|
|
wizard.action_befuellen()
|
|
return wizard
|
|
|
|
def test_01_auto_werte(self):
|
|
wizard = self._wizard()
|
|
wizard.feld_wert_ids.filtered(
|
|
lambda l: l.key == 'zusatz').wert = 'X'
|
|
werte = wizard._at_werte()
|
|
self.assertEqual(werte['firma'], 'Musterwerkstatt GmbH')
|
|
self.assertEqual(werte['mitarbeiter'], 'Dokument Test')
|
|
self.assertEqual(werte['funktion'], 'Techniker')
|
|
self.assertTrue(werte['ead'])
|
|
|
|
def test_02_vorschau_ersetzt_platzhalter(self):
|
|
wizard = self._wizard()
|
|
wizard.feld_wert_ids.filtered(
|
|
lambda l: l.key == 'zusatz').wert = 'Projekt Alpha'
|
|
wizard.action_vorschau()
|
|
vorschau = wizard.vorschau_html
|
|
self.assertIn('Musterwerkstatt GmbH', vorschau)
|
|
self.assertIn('Dokument Test', vorschau)
|
|
self.assertIn('Projekt Alpha', vorschau)
|
|
self.assertNotIn('{{', vorschau)
|
|
|
|
def test_03_pflichtfeld_leist_user_error(self):
|
|
wizard = self._wizard()
|
|
with self.assertRaises(UserError):
|
|
wizard._at_werte()
|
|
|
|
def test_04_version_am_stichtag(self):
|
|
self.env['hr.version'].create({
|
|
'employee_id': self.employee.id,
|
|
'date_version': datetime.date(2026, 3, 1),
|
|
'job_title': 'Senior Techniker',
|
|
})
|
|
wizard = self._wizard()
|
|
self.assertEqual(wizard.version_id.job_title, 'Techniker')
|
|
wizard.datum = datetime.date(2026, 6, 1)
|
|
wizard._compute_version_id()
|
|
self.assertEqual(wizard.version_id.job_title, 'Senior Techniker')
|
|
|
|
def test_05_pdf_ablegt_anhang(self):
|
|
wizard = self._wizard()
|
|
wizard.feld_wert_ids.filtered(
|
|
lambda l: l.key == 'zusatz').wert = 'Fall 5'
|
|
wizard.action_pdf()
|
|
anh = self.env['ir.attachment'].search(
|
|
[('res_model', '=', 'hr.employee'),
|
|
('res_id', '=', self.employee.id)])
|
|
self.assertEqual(len(anh), 1)
|
|
self.assertIn(anh.mimetype, ('application/pdf', 'text/html'))
|
|
if anh.mimetype == 'text/html':
|
|
html = base64.b64decode(anh.datas).decode('utf-8')
|
|
self.assertIn('Fall 5', html)
|
|
|
|
def test_06_report_values_generic(self):
|
|
wizard = self._wizard()
|
|
report = self.env[
|
|
'report.l10n_at_payroll_dokumente'
|
|
'.report_dokument_generisch']
|
|
values = report._get_report_values(
|
|
wizard.ids, data={'titel': 'T', 'body': '<p>Body</p>'})
|
|
self.assertEqual(str(values['body']), '<p>Body</p>')
|
|
html_content, _typ = self.env['ir.actions.report']._render_qweb_html(
|
|
'l10n_at_payroll_dokumente.action_report_dokument_generisch',
|
|
wizard.ids, data={'titel': 'T', 'body': values['body']})
|
|
self.assertIn('Body', str(html_content))
|
|
|
|
def test_07_access_hr_user_lesen_manager_schreiben(self):
|
|
hr_user = self.env['res.users'].create({
|
|
'name': 'Dokument Lesetest',
|
|
'login': 'dok_lesetest',
|
|
'group_ids': [(4, self.env.ref('hr.group_hr_user').id)],
|
|
})
|
|
umgebung = self.env(user=hr_user.id)
|
|
self.assertTrue(
|
|
umgebung['l10n.at.payroll.dokument.vorlage'].browse(
|
|
self.vorlage.id).name)
|
|
kein_hr = self.env['res.users'].create({
|
|
'name': 'Dokument Ohne HR',
|
|
'login': 'dok_ohne_hr',
|
|
'group_ids': [(4, self.env.ref('base.group_user').id)],
|
|
})
|
|
ohne = self.env(user=kein_hr.id)
|
|
with self.assertRaises(AccessError):
|
|
ohne['l10n.at.payroll.dokument.vorlage'].browse(
|
|
self.vorlage.id).name
|
|
|
|
def test_08_pilot_vorlagen_installiert(self):
|
|
for xmlid in ('vorlage_dienstvertrag_angestellte',
|
|
'vorlage_kuendigung_dienstgeber',
|
|
'vorlage_einvernehmliche_aufloesung',
|
|
'vorlage_vordienstzeiten'):
|
|
vorlage = self.env.ref(
|
|
'l10n_at_payroll_dokumente.%s' % xmlid)
|
|
self.assertTrue(vorlage.inhalt_html)
|
|
self.assertEqual(vorlage.rechtsstatus, 'entwurf')
|
|
kuendigung = self.env.ref(
|
|
'l10n_at_payroll_dokumente.vorlage_kuendigung_dienstgeber')
|
|
wizard = self.env['l10n.at.payroll.dokument.wizard'].create({
|
|
'vorlage_id': kuendigung.id,
|
|
'employee_id': self.employee.id,
|
|
'datum': datetime.date(2026, 9, 1),
|
|
})
|
|
wizard.action_befuellen()
|
|
wizard.feld_wert_ids.filtered(
|
|
lambda l: l.key == 'anrede').wert = 'Sehr geehrter Herr'
|
|
wizard.feld_wert_ids.filtered(
|
|
lambda l: l.key == 'kuendigung_frist').wert = '6 Wochen'
|
|
wizard.feld_wert_ids.filtered(
|
|
lambda l: l.key == 'beendigungstermin').wert = '31.10.2026'
|
|
wizard.feld_wert_ids.filtered(
|
|
lambda l: l.key == 'ort_datum').wert = 'Wien, 1.9.2026'
|
|
wizard.action_vorschau()
|
|
self.assertIn('31.10.2026', wizard.vorschau_html)
|
|
self.assertIn('Musterwerkstatt GmbH', wizard.vorschau_html) |