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: add end-of-employment payroll cycle (AP14-A)
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.
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
# Part of the odoo-at-payroll project. License: LGPL-3.
|
||||
from . import kv_katalog_import
|
||||
from . import bv_endabrechnung
|
||||
from . import bv_endabrechnung
|
||||
from . import endabrechnung
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
# 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,
|
||||
}
|
||||
Reference in New Issue
Block a user