# Part of the odoo-at-payroll project. License: LGPL-3. """AP14-A: Endabrechnungs-Wizard — sammelt die offenen Posten der Beendigungs-Abrechnung (Urlaubsersatzleistung, Abgangsentschädigung, Zeitguthaben/-schulden, offene Vorschüsse) und legt den Endabrechnungs-Lauf mit vorbefüllten Eingaben an. Reiner Sammel-/Anlageprozess; die BV-Anwartschaft läuft über den eigenen BV-Wizard (GP5), die Pfändung über die AP11-Maschine. AP15-C: Vergleichssumme (§ 67 Abs 8 lit a) — Rest (Fünftelregelung) und BV-Anwartschafts-Teil (bis 7.500 € mit 6 %).""" from datetime import timedelta from dateutil.relativedelta import relativedelta from odoo import _, api, fields, models from odoo.exceptions import UserError class L10nAtEndabrechnungWizard(models.TransientModel): _name = 'l10n.at.payroll.endabrechnung.wizard' _description = 'Endabrechnung (Beendigung des Dienstverhältnisses)' employee_id = fields.Many2one( 'hr.employee', string='Dienstnehmer:in', required=True) austrittsdatum = fields.Date( string='Austritt (Ende des Arbeitsverhältnisses)', required=True) departure_reason_id = fields.Many2one( 'hr.departure.reason', string='Abgangsgrund', required=True, domain="['|', ('country_id', '=', False), ('country_id.code', '=', 'AT')]", help='Odoo-Abgangsgrund des Beschäftigtendatensatzes; die ' 'Abfertigungsberechtigung wird zusätzlich ausdrücklich bestätigt.') uel_tage = fields.Float( string='Offene Urlaubstage (Arbeitstage)', help='Resturlaub früherer Urlaubsjahre zur Gänze plus aliquoter ' 'Anspruch des laufenden Urlaubsjahres abzüglich verbrauchter ' 'Tage, ohne Rundung (§ 10 UrlG, lb-url-16). Für ' 'unberechtigten vorzeitigen Austritt sind die Tage der 5. ' 'und 6. Urlaubswoche des laufenden Jahres herauszurechnen.') abgang_betrag = fields.Monetary( string='Abgangsentschädigung (Brutto, § 67 Abs 8 lit b EStG)', currency_field='currency_id', help='Entschädigung für den Verzicht auf die Arbeitsleistung ' 'während der (fiktiven) Kündigungsfrist — LSt: Fünftelregelung; ' 'SV: Umlegung auf den Kündigungsfrist-Zeitraum (§ 11 Abs 2 ' 'ASVG); voll lohnnebenkostenpflichtig.') abgang_ffrist_tage = fields.Float( string='Kündigungsfrist (Kalendertage)', help='Kalendertage der (fiktiven) Kündigungsfrist, auf die die ' 'pauschalierte Abgangsentschädigung sv-rechtlich umzulegen ' 'ist (§ 11 Abs 2 ASVG).') zeit_guthaben_stunden = fields.Float( string='Zeitguthaben an Normalarbeitszeit (Stunden)', help='Offenes Guthaben an Normalarbeitszeit aus durchrechenbarem ' 'Arbeitszeitmodell — Abgeltung mit 50 % Zuschlag (§ 19e ' 'Abs 2 AZG); bei unberechtigtem vorzeitigem Austritt kein ' 'Zuschlag.') zeit_schulden_stunden = fields.Float( string='Zeitschulden (Stunden)', help='Offene Zeitschulden (negative Zeitguthaben), abgerechnet ' 'mit der aktuellen Normalstunde (lb-end-08).') vorschuss_saldo = fields.Monetary( string='Offener Vorschuss-/Darlehenssaldo', currency_field='currency_id', help='Bei Beendigung zur Gänze fälliger, noch offener ' 'Vorschusssaldo (lb-end-06) — Rückverrechnung vom Netto ' 'nach SV und Lohnsteuer, ohne Abgabenberührung ' '(§ 293 Abs 3 EO).') abf_alt_aktiv = fields.Boolean(string='Gesetzliche Abfertigung Alt') abf_alt_anspruch_bestaetigt = fields.Boolean( string='Anspruch und Nachweise geprüft', help='Bestätigung, dass Alt-Anwartschaft, Beendigungsgrund, ' 'Dienstzeiten und Bemessungsgrundlage geprüft wurden.') abf_alt_anspruchsart = fields.Selection( [('normal', 'Normalanspruch (§ 23 AngG)'), ('eltern', 'Elternaustritt (§ 23a Abs 3/4 AngG)'), ('tod', 'Tod (§ 23 Abs 6 AngG)'), ('pension', 'Pension/Invalidität (§ 23a Abs 1 AngG)'), ('eingefroren', 'Eingefrorener Altanspruch (§ 47 BMSVG)'), ('manuell', 'Sonstiger nachgewiesener Anspruch')], string='Anspruchsart', default='normal') abf_alt_dienstzeit_monate = fields.Integer( string='Nachgewiesene Abfertigungsdienstzeit (Monate)') abf_alt_monatsentgelt = fields.Monetary( string='Maßgebliches Monatsentgelt', currency_field='currency_id', help='Bestätigte arbeitsrechtliche Bemessungsgrundlage: letzter ' 'Monat einschließlich regelmäßiger Bezugsteile, Sachbezüge ' 'und aliquoter Sonderzahlungen.') abf_alt_laufender_arbeitslohn = fields.Monetary( string='Laufender Monatsarbeitslohn (§ 67 Abs 3)', currency_field='currency_id', help='Laufender Arbeitslohn für den steuerlichen Vervielfacher; ' 'aliquote Sonderzahlungen gehören hier nicht dazu.') abf_alt_monatslohnsteuer = fields.Monetary( string='Tarif-LSt des laufenden Monats', currency_field='currency_id', help='Die auf den maßgeblichen laufenden Monatsarbeitslohn ' 'entfallende tarifmäßige Lohnsteuer nach den persönlichen ' 'Verhältnissen. Bei steuerfreiem Monatslohn ausdrücklich 0 ' 'bestätigen.') abf_alt_mehrfach = fields.Float( string='Bestätigter Anspruchsfaktor', help='§-23-Staffel, gegebenenfalls halbiert/gedeckelt oder als ' 'eingefrorener beziehungsweise nachgewiesener Faktor.') abf_alt_nachweis = fields.Text( string='Nachweis/Begründung', help='Quelle der Dienstzeiten, Anspruchsprüfung, Alt-/Neu-Status ' 'und allfällige manuelle Abweichung dokumentieren.') abf_frei_aktiv = fields.Boolean( string='Freiwillige Abfertigung (§ 67 Abs 6)') abf_frei_anspruch_bestaetigt = fields.Boolean( string='Prüfung und Nachweise bestätigt', help='Bestätigung, dass es sich um eine Beendigungsleistung ohne ' 'schädliche Anspruchsumwandlung handelt (KE/UE/SZ/Zeitguthaben ' 'müssen getrennt abrechnet werden — GPLB-Risiko, lb-end-15), ' 'dass die (Vor-)Dienstzeiten lückenlos zum Lohnkonto ' '(§ 76 EStG) nachgewiesen sind und erhaltene Abfertigungen ' 'angegeben wurden.') abf_frei_betrag = fields.Monetary( string='Bruttobetrag', currency_field='currency_id') abf_frei_basis_12 = fields.Monetary( string='Laufende Bezüge letzte 12 Monate', currency_field='currency_id', help='Basis der Viertel-/Zwölftelregelung: alle steuerbaren ' 'Bezugsteile (auch steuerfreie Zuschläge, Sachbezüge), ' 'ohne Sonderzahlungen und ohne §-26-Z-4-Reisevergütungen; ' 'wird aus den letzten zwölf validierten Läufen vorgeschlagen ' 'und ist bestätigbar (lb-end-13).') abf_frei_dienstzeit_monate = fields.Integer( string='Nachgewiesene Dienstzeit (Monate)') abf_frei_erhaltene_abfertigungen = fields.Monetary( string='Bereits erhaltene Abfertigungen', currency_field='currency_id', help='In den nachgewiesenen Dienstzeiten erhaltene Abfertigungen ' '(§ 67 Abs 3 oder Abs 6 EStG) — Kürzung nach Z 3.') abf_frei_gesetzlicher_anspruch = fields.Monetary( string='Bestehender gesetzlicher Anspruch', currency_field='currency_id', help='Anspruch auf eine gesetzliche Abfertigung am aktuellen ' 'Dienstverhältnis-Ende (Z 3); bei aktiver Alt-Abfertigung ' 'automatisch aus dem Alt-Anspruch vorgeschlagen.') abf_frei_nachweis = fields.Text( string='Nachweis/Begründung', help='Vordienstzeiten-Nachweis (Lohnkonto § 76 EStG), erhaltene ' 'Abfertigungen, Basis-Abweichungen und Anspruchsgrundlage ' 'dokumentieren.') vergleich_aktiv = fields.Boolean( string='Vergleich (§ 67 Abs 8 lit a)', help='Auf gerichtlichen oder außergerichtlichen Vergleichen ' 'beruhende Vergleichssummen sowie Bereinigungen/Nachzahlungen ' 'aus Urteilen/Bescheiden und Zahlungen bei ' 'Kündigungsanfechtung (lb-end-21). Erkennbare ' 'Abfertigungsanteile werden getrennt über die Abschnitte ' 'Abfertigung Alt (§ 67 Abs 3) bzw. freiwillige Abfertigung ' '(§ 67 Abs 6) erfasst; Pensionsabfindungen (lit e) und ' 'Sozialplan-Zahlungen (lit f) gehören nicht hierher.') vergleich_rest_betrag = fields.Monetary( string='Vergleichssumme — Rest (Fünftelregelung)', currency_field='currency_id', help='Nach Abzug der §-62-Beiträge ist ein Fünftel steuerfrei ' '(Deckel 1/5 × 9 × monatliche HBG), 4/5 laufen tarifisch im ' 'Zuflussmonat — ohne Jahressechstel-Effekt (§ 67 Abs 8 lit ' 'a iVm Abs 10).') vergleich_bv_betrag = fields.Monetary( string='Vergleichssumme — BV-Anwartschafts-Teil', currency_field='currency_id', help='Anteil für Zeiträume mit BV-Kassen-Anwartschaft: bis zum ' 'Deckel (2026: 7 500 €) mit 6 %, der übersteigende Teil ' 'fällt in die Fünftelregelung zurück. Der Alt-/Neu-Übertritt ' 'steuert die Anwendung (lb-end-21: eingefrorene Zeiträume ' 'kein 6 %-Teil).') vergleich_bv_anwartschaft_bestaetigt = fields.Boolean( string='BV-Anwartschaft für die betroffenen Zeiträume', help='Voraussetzung des 6-%-Teils: die Vergleichssumme wird für ' 'Zeiträume ausbezahlt, für die eine Anwartschaft gegenüber ' 'einer BV-Kasse besteht (§ 67 Abs 8 lit a letzter Satz).') vergleich_aufteilung_bestaetigt = fields.Boolean( string='Aufgliederung und Strittigkeit bestätigt', help='Bestätigung der Aufgliederung in der Vergleichsvereinbarung ' '(Praxistipp lb-end-21): Widmungen sind abgabenrechtlich nur ' 'anerkannt, wenn die Ansprüche strittig waren (lb-end-22, ' 'VwGH); beim Pauschalvergleich ist der Verhältnisfaktor ' 'Forderungsbetrag : Vergleichssumme zu dokumentieren ' '(E-MVB 011-02-00-001).') vergleich_nachweis = fields.Text( string='Nachweis/Begründung', help='Vergleichstext, strittige Ansprüche, erkennbare ' 'Abfertigungsanteile, BV-Zeiträume und allfälliger ' 'Verhältnisfaktor dokumentieren.') currency_id = fields.Many2one( 'res.currency', default=lambda self: self.env.company.currency_id) uel_lfd_vorschau = fields.Monetary( string='UEL laufender Anteil (Vorschau)', currency_field='currency_id', compute='_compute_vorschau') uel_sz_vorschau = fields.Monetary( string='UEL Sonderzahlungsanteil (Vorschau)', currency_field='currency_id', compute='_compute_vorschau') sv_tage_vorschau = fields.Integer( string='SV-Verlängerungstage (§ 11 Abs 2)', compute='_compute_vorschau') abf_alt_betrag_vorschau = fields.Monetary( string='Abfertigung Alt brutto', currency_field='currency_id', compute='_compute_vorschau') abf_alt_steuer_vervielfachung_vorschau = fields.Monetary( string='LSt Vervielfachungsmethode', currency_field='currency_id', compute='_compute_vorschau') abf_alt_steuer_6_vorschau = fields.Monetary( string='LSt 6 %', currency_field='currency_id', compute='_compute_vorschau') abf_alt_lohnsteuer_vorschau = fields.Monetary( string='Angewendete günstigere LSt', currency_field='currency_id', compute='_compute_vorschau') abf_alt_netto_vorschau = fields.Monetary( string='Abfertigung Alt netto', currency_field='currency_id', compute='_compute_vorschau') abf_frei_z1_vorschau = fields.Monetary( string='Z 1 Viertelregelung (Vorschau)', currency_field='currency_id', compute='_compute_vorschau') abf_frei_z2_vorschau = fields.Monetary( string='Z 2 Zwölftelregelung (Vorschau)', currency_field='currency_id', compute='_compute_vorschau') abf_frei_kuerzung_vorschau = fields.Monetary( string='Z 3 Kürzung (Vorschau)', currency_field='currency_id', compute='_compute_vorschau') abf_frei_beguenstigt_vorschau = fields.Monetary( string='Begünstigter Teil 6 % (Vorschau)', currency_field='currency_id', compute='_compute_vorschau') abf_frei_ueberhang_vorschau = fields.Monetary( string='Überhang zum Tarif (Vorschau)', currency_field='currency_id', compute='_compute_vorschau') abf_frei_lst_vorschau = fields.Monetary( string='LSt 6 % (Vorschau)', currency_field='currency_id', compute='_compute_vorschau') abf_frei_netto_vorschau = fields.Monetary( string='Begünstigter Teil netto (Vorschau)', currency_field='currency_id', compute='_compute_vorschau') vergleich_fuenftel_frei_vorschau = fields.Monetary( string='Steuerfreies Fünftel (Vorschau)', currency_field='currency_id', compute='_compute_vorschau') vergleich_tarif_basis_vorschau = fields.Monetary( string='Fünftel-Bezüge tarifischer Teil (Vorschau)', currency_field='currency_id', compute='_compute_vorschau') vergleich_bv_lst_vorschau = fields.Monetary( string='LSt 6 % BV-Teil (Vorschau)', currency_field='currency_id', compute='_compute_vorschau') @api.onchange('employee_id') def _onchange_employee(self): for wizard in self: if not wizard.employee_id: continue version = wizard.employee_id.version_id wizard.austrittsdatum = ( version.contract_date_end or fields.Date.today()) wizard.departure_reason_id = version.departure_reason_id wizard._at_abf_alt_vorschlag_setzen() wizard._at_abf_frei_vorschlag_setzen() @api.onchange('austrittsdatum', 'abf_alt_anspruchsart', 'abf_alt_dienstzeit_monate', 'abf_alt_aktiv', 'abf_alt_monatsentgelt', 'abf_alt_mehrfach', 'abf_frei_aktiv', 'abf_frei_dienstzeit_monate') def _onchange_abf_alt(self): for wizard in self: wizard._at_abf_alt_vorschlag_setzen() wizard._at_abf_frei_vorschlag_setzen() def _at_abf_alt_vorschlag_setzen(self): self.ensure_one() if not self.employee_id or not self.austrittsdatum: return version = self.employee_id.version_id start = version.contract_date_start if start and not self.abf_alt_dienstzeit_monate: dauer = relativedelta( self.austrittsdatum + timedelta(days=1), start) self.abf_alt_dienstzeit_monate = dauer.years * 12 + dauer.months slip = self._vorschau_payslip() if not self.abf_alt_laufender_arbeitslohn: self.abf_alt_laufender_arbeitslohn = slip._at_kv_gehalt_basis() if not self.abf_alt_monatsentgelt: self.abf_alt_monatsentgelt = ( self.abf_alt_laufender_arbeitslohn + 2.0 * slip._at_sz_basis(self.austrittsdatum) / 12.0) vorgeschlagen = slip._at_abf_alt_mehrfach_vorschlag( self.abf_alt_dienstzeit_monate, self.abf_alt_anspruchsart) if vorgeschlagen or not self.abf_alt_mehrfach: self.abf_alt_mehrfach = vorgeschlagen def _at_abf_frei_vorschlag_setzen(self): self.ensure_one() if not self.employee_id or not self.austrittsdatum: return slip = self._vorschau_payslip() if self.abf_frei_aktiv and not self.abf_frei_basis_12: self.abf_frei_basis_12 = slip._at_bezuege_12_monate() if (self.abf_frei_aktiv and self.abf_alt_aktiv and not self.abf_frei_gesetzlicher_anspruch): self.abf_frei_gesetzlicher_anspruch = ( self.abf_alt_monatsentgelt * self.abf_alt_mehrfach) @api.depends('employee_id', 'austrittsdatum', 'uel_tage', 'abf_alt_aktiv', 'abf_alt_monatsentgelt', 'abf_alt_laufender_arbeitslohn', 'abf_alt_monatslohnsteuer', 'abf_alt_mehrfach', 'abf_alt_dienstzeit_monate', 'abf_alt_anspruchsart', 'abf_frei_aktiv', 'abf_frei_betrag', 'abf_frei_basis_12', 'abf_frei_dienstzeit_monate', 'abf_frei_erhaltene_abfertigungen', 'abf_frei_gesetzlicher_anspruch', 'vergleich_aktiv', 'vergleich_rest_betrag', 'vergleich_bv_betrag') def _compute_vorschau(self): for wizard in self: wizard.uel_lfd_vorschau = 0.0 wizard.uel_sz_vorschau = 0.0 wizard.sv_tage_vorschau = 0 wizard.abf_alt_betrag_vorschau = 0.0 wizard.abf_alt_steuer_vervielfachung_vorschau = 0.0 wizard.abf_alt_steuer_6_vorschau = 0.0 wizard.abf_alt_lohnsteuer_vorschau = 0.0 wizard.abf_alt_netto_vorschau = 0.0 wizard.abf_frei_z1_vorschau = 0.0 wizard.abf_frei_z2_vorschau = 0.0 wizard.abf_frei_kuerzung_vorschau = 0.0 wizard.abf_frei_beguenstigt_vorschau = 0.0 wizard.abf_frei_ueberhang_vorschau = 0.0 wizard.abf_frei_lst_vorschau = 0.0 wizard.abf_frei_netto_vorschau = 0.0 wizard.vergleich_fuenftel_frei_vorschau = 0.0 wizard.vergleich_tarif_basis_vorschau = 0.0 wizard.vergleich_bv_lst_vorschau = 0.0 if not wizard.employee_id or not wizard.austrittsdatum: continue slip = wizard._vorschau_payslip() wizard.uel_lfd_vorschau = slip._at_uel_lfd() wizard.uel_sz_vorschau = slip._at_uel_sz() wizard.sv_tage_vorschau = slip._at_end_sv_tage( wizard.uel_tage) if wizard.abf_alt_aktiv: betrag = slip._at_abf_alt_betrag() steuer_vervielfachung = \ slip._at_abf_alt_steuer_vervielfachung() steuer_6 = slip._at_abf_alt_steuer_6() steuer = min(steuer_vervielfachung, steuer_6) wizard.abf_alt_betrag_vorschau = betrag wizard.abf_alt_steuer_vervielfachung_vorschau = \ steuer_vervielfachung wizard.abf_alt_steuer_6_vorschau = steuer_6 wizard.abf_alt_lohnsteuer_vorschau = steuer wizard.abf_alt_netto_vorschau = betrag - steuer if wizard.abf_frei_aktiv: split = slip._at_abf_frei_split() satz = float(slip._l10n_at_param( 'at_endabrechnung')['abfertigung_steuersatz']) wizard.abf_frei_z1_vorschau = split['z1'] wizard.abf_frei_z2_vorschau = split['z2'] wizard.abf_frei_kuerzung_vorschau = split['kuerzung'] wizard.abf_frei_beguenstigt_vorschau = split['beguenstigt'] wizard.abf_frei_ueberhang_vorschau = split['ueberhang'] wizard.abf_frei_lst_vorschau = \ split['beguenstigt'] * satz / 100.0 wizard.abf_frei_netto_vorschau = \ split['beguenstigt'] - wizard.abf_frei_lst_vorschau if wizard.vergleich_aktiv: # Der tarifische Fünftel-Teil läuft über die laufende # Bemessung (LST_LFD); die Vorschau zeigt die Pfade # getrennt (Fünftel-Pool inkl. ABGANG, falls erfasst). wizard.vergleich_fuenftel_frei_vorschau = \ slip._at_fuenftel_frei_betr() wizard.vergleich_tarif_basis_vorschau = \ slip._at_fuenftel_tarif_basis() wizard.vergleich_bv_lst_vorschau = \ slip._at_vergleich_bv_lst_6() def _vorschau_payslip(self): """Flüchtiger Payslip-Kontext für die Vorschau-Berechnung (kein Speichern): trägt Austrittsmonat, Struktur und die Eingaben.""" self.ensure_one() erster = self.austrittsdatum.replace(day=1) letzter = (erster + timedelta(days=32)).replace(day=1) \ - timedelta(days=1) struct = self.employee_id.version_id.structure_id if not struct: struct = self.env.ref( 'l10n_at_hr_payroll_private.structure_at_privat') slip = self.env['hr.payslip'].new({ 'employee_id': self.employee_id.id, 'struct_id': struct.id, 'date_from': erster, 'date_to': letzter, 'l10n_at_abf_alt_anspruchsart': self.abf_alt_anspruchsart, 'l10n_at_abf_alt_dienstzeit_monate': self.abf_alt_dienstzeit_monate, 'l10n_at_abf_alt_monatsentgelt': self.abf_alt_monatsentgelt, 'l10n_at_abf_alt_laufender_arbeitslohn': self.abf_alt_laufender_arbeitslohn, 'l10n_at_abf_alt_monatslohnsteuer': self.abf_alt_monatslohnsteuer, 'l10n_at_abf_alt_mehrfach': self.abf_alt_mehrfach, 'l10n_at_abf_alt_nachweis': self.abf_alt_nachweis, 'l10n_at_abf_frei_basis_12': self.abf_frei_basis_12, 'l10n_at_abf_frei_dienstzeit_monate': self.abf_frei_dienstzeit_monate, 'l10n_at_abf_frei_erhaltene_abfertigungen': self.abf_frei_erhaltene_abfertigungen, 'l10n_at_abf_frei_gesetzlicher_anspruch': self.abf_frei_gesetzlicher_anspruch, 'l10n_at_abf_frei_nachweis': self.abf_frei_nachweis, 'l10n_at_vergleich_nachweis': self.vergleich_nachweis, }) eingaben = [ ('ATP_UEL_TAGE', self.uel_tage), ('ATP_ABF_ALT', self.abf_alt_monatsentgelt * self.abf_alt_mehrfach if self.abf_alt_aktiv else 0.0), ('ATP_ABF_FREI', self.abf_frei_betrag if self.abf_frei_aktiv else 0.0), ('ATP_VERGLEICH', self.vergleich_rest_betrag if self.vergleich_aktiv else 0.0), ('ATP_VERGLEICH_BV', self.vergleich_bv_betrag if self.vergleich_aktiv else 0.0), ] slip.input_line_ids = [ (0, 0, { 'input_type_id': self.env['hr.payslip.input.type'] .search([('code', '=', code)], limit=1).id, 'amount': betrag, }) for code, betrag in eingaben if betrag ] return slip def _eingaben(self): self.ensure_one() return [ ('ATP_UEL_TAGE', self.uel_tage), ('ATP_ABGANG', self.abgang_betrag), ('ATP_ABGANG_FFRIST_TAGE', self.abgang_ffrist_tage), ('ATP_ZEIT_GUTHABEN', self.zeit_guthaben_stunden), ('ATP_ZEIT_SCHULDEN', self.zeit_schulden_stunden), ('ATP_ABRECHNUNG_VORSCHUSS', self.vorschuss_saldo), ('ATP_ABF_ALT', self.abf_alt_monatsentgelt * self.abf_alt_mehrfach if self.abf_alt_aktiv else 0.0), ('ATP_ABF_FREI', self.abf_frei_betrag if self.abf_frei_aktiv else 0.0), ('ATP_VERGLEICH', self.vergleich_rest_betrag if self.vergleich_aktiv else 0.0), ('ATP_VERGLEICH_BV', self.vergleich_bv_betrag if self.vergleich_aktiv else 0.0), ] def _abf_alt_validieren(self): self.ensure_one() if not self.abf_alt_aktiv: return if not self.abf_alt_anspruch_bestaetigt: raise UserError(_('Bitte Anspruch und Nachweise der ' 'Abfertigung Alt bestätigen.')) if self.employee_id.version_id.l10n_at_abfertigung_neu \ and self.abf_alt_anspruchsart != 'eingefroren': raise UserError(_( 'Das Dienstverhältnis ist als Abfertigung Neu markiert. ' 'Eine Alt-Abfertigung ist nur als nachgewiesener ' 'eingefrorener Anspruch zulässig.')) if self.abf_alt_anspruchsart == 'eltern' \ and self.abf_alt_dienstzeit_monate < 60: raise UserError(_( 'Ein Elternaustritt nach § 23a Abs. 3/4 AngG setzt ein ' 'ununterbrochenes Dienstverhältnis von fünf Jahren voraus.')) if (self.abf_alt_dienstzeit_monate <= 0 or self.abf_alt_monatsentgelt <= 0 or self.abf_alt_laufender_arbeitslohn <= 0 or self.abf_alt_monatslohnsteuer < 0 or self.abf_alt_mehrfach <= 0): raise UserError(_('Dienstzeit, Monatsentgelt, laufender ' 'Arbeitslohn und Anspruchsfaktor müssen ' 'größer als null sein; die Tarif-LSt darf ' 'nicht negativ sein.')) agrd = self.departure_reason_id.l10n_at_agrd if agrd == '02' and self.abf_alt_anspruchsart not in ( 'eltern', 'pension', 'manuell'): raise UserError(_( 'Eine gewöhnliche Arbeitnehmerkündigung begründet keinen ' 'Anspruch nach § 23 AngG. Bitte einen §-23a-Sonderfall ' 'wählen oder den Anspruch manuell nachweisen.')) if agrd == '06' and self.abf_alt_anspruchsart != 'manuell': raise UserError(_( 'Der Abgangsgrund „Fristlose Entlassung“ unterscheidet ' 'nicht zwischen verschuldeter und unverschuldeter ' 'Entlassung. Bitte den Anspruch manuell bestätigen und ' 'begründen.')) def _abf_frei_validieren(self): self.ensure_one() if not self.abf_frei_aktiv: return if not self.abf_frei_anspruch_bestaetigt: raise UserError(_('Bitte Prüfung und Nachweise der freiwilligen ' 'Abfertigung bestätigen.')) if self.abf_frei_betrag <= 0: raise UserError(_('Der Bruttobetrag der freiwilligen ' 'Abfertigung muss größer als null sein.')) if (self.abf_frei_basis_12 < 0 or self.abf_frei_erhaltene_abfertigungen < 0 or self.abf_frei_gesetzlicher_anspruch < 0 or self.abf_frei_dienstzeit_monate < 0): raise UserError(_('12-Monats-Basis, erhaltene Abfertigungen, ' 'gesetzlicher Anspruch und Dienstzeit dürfen ' 'nicht negativ sein.')) def _vergleich_validieren(self): self.ensure_one() if not self.vergleich_aktiv: return if not self.vergleich_aufteilung_bestaetigt: raise UserError(_('Bitte die Aufgliederung der Vergleichs-' 'summe und die Strittigkeit der Ansprüche ' 'bestätigen (lb-end-21/22).')) if self.vergleich_rest_betrag < 0 or self.vergleich_bv_betrag < 0: raise UserError(_('Vergleichsbeträge dürfen nicht negativ ' 'sein.')) if not self.vergleich_rest_betrag and not self.vergleich_bv_betrag: raise UserError(_('Bitte den Rest der Vergleichssumme oder ' 'den BV-Anwartschafts-Teil erfassen; ' 'erkennbare Abfertigungsanteile gehören in ' 'die Abfertigungs-Abschnitte.')) slip = self._vorschau_payslip() deckel = float(slip._l10n_at_param('at_lst_67_abs8_vergleich_bv')) if self.vergleich_bv_betrag > deckel: raise UserError(_( 'Der BV-Anwartschafts-Teil ist mit %.2f € gedeckelt ' '(§ 67 Abs 8 lit a letzter Satz); den übersteigenden ' 'Betrag als Rest (Fünftelregelung) erfassen.') % deckel) if self.vergleich_bv_betrag > 0 \ and not self.vergleich_bv_anwartschaft_bestaetigt: raise UserError(_('Der 6-%-Teil setzt eine bestätigte ' 'BV-Anwartschaft für die betroffenen ' 'Zeiträume voraus (§ 67 Abs 8 lit a letzter ' 'Satz).')) def action_payslip(self): """Endabrechnungs-Lauf im Austrittsmonat anlegen und die gesammelten offenen Posten als Eingaben vorbefüllen.""" self.ensure_one() self._abf_alt_validieren() self._abf_frei_validieren() self._vergleich_validieren() erster = self.austrittsdatum.replace(day=1) letzter = (erster + timedelta(days=32)).replace(day=1) \ - timedelta(days=1) version = self.employee_id.version_id version.write({ 'contract_date_end': self.austrittsdatum, 'departure_reason_id': self.departure_reason_id.id, }) struct = version.structure_id \ or self.env.ref('l10n_at_hr_payroll_private.structure_at_privat') slip = self.env['hr.payslip'].create({ 'name': _('Endabrechnung %s') % self.employee_id.name, 'employee_id': self.employee_id.id, 'struct_id': struct.id, 'date_from': erster, 'date_to': letzter, 'l10n_at_abf_alt_anspruchsart': self.abf_alt_anspruchsart if self.abf_alt_aktiv else False, 'l10n_at_abf_alt_dienstzeit_monate': self.abf_alt_dienstzeit_monate if self.abf_alt_aktiv else 0, 'l10n_at_abf_alt_monatsentgelt': self.abf_alt_monatsentgelt if self.abf_alt_aktiv else 0.0, 'l10n_at_abf_alt_laufender_arbeitslohn': self.abf_alt_laufender_arbeitslohn if self.abf_alt_aktiv else 0.0, 'l10n_at_abf_alt_monatslohnsteuer': self.abf_alt_monatslohnsteuer if self.abf_alt_aktiv else 0.0, 'l10n_at_abf_alt_mehrfach': self.abf_alt_mehrfach if self.abf_alt_aktiv else 0.0, 'l10n_at_abf_alt_nachweis': self.abf_alt_nachweis if self.abf_alt_aktiv else False, 'l10n_at_abf_frei_basis_12': self.abf_frei_basis_12 if self.abf_frei_aktiv else 0.0, 'l10n_at_abf_frei_dienstzeit_monate': self.abf_frei_dienstzeit_monate if self.abf_frei_aktiv else 0, 'l10n_at_abf_frei_erhaltene_abfertigungen': self.abf_frei_erhaltene_abfertigungen if self.abf_frei_aktiv else 0.0, 'l10n_at_abf_frei_gesetzlicher_anspruch': self.abf_frei_gesetzlicher_anspruch if self.abf_frei_aktiv else 0.0, 'l10n_at_abf_frei_nachweis': self.abf_frei_nachweis if self.abf_frei_aktiv else False, 'l10n_at_vergleich_nachweis': self.vergleich_nachweis if self.vergleich_aktiv else False, }) typen = {code: self.env['hr.payslip.input.type'].search( [('code', '=', code)], limit=1) for code, _betrag in self._eingaben()} self.env['hr.payslip.input'].create([{ 'payslip_id': slip.id, 'input_type_id': typen[code].id, 'amount': betrag, } for code, betrag in self._eingaben() if betrag]) slip.compute_sheet() return { 'type': 'ir.actions.act_window', 'name': _('Endabrechnung'), 'res_model': 'hr.payslip', 'view_mode': 'form', 'res_id': slip.id, }