ee94f4664f
Pay the untaken vacation, dismissal compensation, time balances and open advances from one exit run. The vacation replacement splits natively into a running and a special-payment component per § 67 Abs 8 lit d, only the running part extends the mandatory insurance under § 11 Abs 2 ASVG with sequential windows (dismissal compensation first) and daily-value ceilings. Dismissal compensation follows the one-fifth rule of § 67 Abs 8 lit b without raising the annual sixth, stays fully social-insurable by allocation over the notice period and fully subject to payroll taxes. Advances are offset against net pay only (§ 293 Abs 3 EO) and time credits carry the 50 % statutory surcharge without § 68 relief. Verified against the original statute texts in .ris/; § 67 Abs 6 lump-sum payments are deliberately deferred to AP15.
176 lines
8.0 KiB
Python
176 lines
8.0 KiB
Python
# 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."""
|
|
from datetime import timedelta
|
|
|
|
from odoo import _, api, fields, models
|
|
|
|
|
|
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)
|
|
beendigungsart = fields.Selection(
|
|
[('ag_kuendigung', 'Kündigung durch den Arbeitgeber'),
|
|
('an_kuendigung', 'Kündigung durch den Dienstnehmer'),
|
|
('einvernehmlich', 'Einvernehmliche Auflösung'),
|
|
('berechtigter_austritt', 'Berechtigter vorzeitiger Austritt'),
|
|
('unberechtigter_austritt',
|
|
'Unberechtigter vorzeitiger Austritt '
|
|
'(keine UEL für die 5./6. Woche, § 10 Abs 2 UrlG; '
|
|
'kein Zeitguthaben-Zuschlag, § 19e Abs 2 AZG)'),
|
|
('verschuldete_entlassung', 'Verschuldete Entlassung'),
|
|
('unverschuldete_entlassung', 'Unverschuldete Entlassung'),
|
|
('befristung', 'Zeitablauf (befristetes Dienstverhältnis)'),
|
|
('tod', 'Tod (UEL an die Erben, § 10 Abs 5 UrlG)')],
|
|
string='Beendigungsart', required=True)
|
|
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).')
|
|
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')
|
|
|
|
@api.onchange('employee_id')
|
|
def _onchange_employee(self):
|
|
for wizard in self:
|
|
if wizard.employee_id:
|
|
wizard.austrittsdatum = (
|
|
wizard.employee_id.version_id.contract_date_end
|
|
or fields.Date.today())
|
|
|
|
@api.depends('employee_id', 'austrittsdatum', 'uel_tage')
|
|
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
|
|
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)
|
|
|
|
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,
|
|
})
|
|
eingaben = [('ATP_UEL_TAGE', self.uel_tage)]
|
|
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),
|
|
]
|
|
|
|
def action_payslip(self):
|
|
"""Endabrechnungs-Lauf im Austrittsmonat anlegen und die
|
|
gesammelten offenen Posten als Eingaben vorbefüllen."""
|
|
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 \
|
|
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,
|
|
})
|
|
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,
|
|
} |