225 lines
12 KiB
Python
225 lines
12 KiB
Python
# Part of the odoo-at-payroll project. License: LGPL-3.
|
|
"""AP12b: wertgebundene Sachbezüge auf dem Dienstverhältnis.
|
|
|
|
Ein Fahrzeug bzw. eine Wohnung je Version ist bewusst MVP-Grenze. Wechsel,
|
|
Pool-/Mehrfachfahrzeuge, Mini-Sachbezug und Kleinwohnungs-Retroaktivität
|
|
bleiben dokumentierte Folgefälle (lb-sac-03/04/09/18/15/08).
|
|
"""
|
|
from dateutil.relativedelta import relativedelta
|
|
|
|
from odoo import fields, models
|
|
|
|
|
|
BUNDESLAENDER = [
|
|
('burgenland', 'Burgenland'), ('kaernten', 'Kärnten'),
|
|
('niederoesterreich', 'Niederösterreich'),
|
|
('oberoesterreich', 'Oberösterreich'), ('salzburg', 'Salzburg'),
|
|
('steiermark', 'Steiermark'), ('tirol', 'Tirol'),
|
|
('vorarlberg', 'Vorarlberg'), ('wien', 'Wien'),
|
|
]
|
|
|
|
|
|
class HrVersion(models.Model):
|
|
_inherit = 'hr.version'
|
|
|
|
l10n_at_sb_wagen_aktiv = fields.Boolean(
|
|
string='AT Dienstwagen privat nutzbar', tracking=True,
|
|
groups='hr_payroll.group_hr_payroll_user')
|
|
l10n_at_sb_wagen_ak = fields.Monetary(
|
|
string='AT Dienstwagen Anschaffungskosten', currency_field='currency_id',
|
|
tracking=True, groups='hr_payroll.group_hr_payroll_user',
|
|
help='Maßgebliche AK inkl. USt/NoVA nach § 4 SBWVO; bei '
|
|
'Gebrauchtwagen historischer Neupreis (lb-sac-03).')
|
|
l10n_at_sb_wagen_erstzulassung = fields.Date(
|
|
string='AT Dienstwagen Erstzulassung', tracking=True,
|
|
groups='hr_payroll.group_hr_payroll_user',
|
|
help='Bestimmt den CO₂-Grenzwert; bei Gebrauchtwagen ist das '
|
|
'Erstzulassungsjahr maßgeblich (lb-sac-03).')
|
|
l10n_at_sb_wagen_co2 = fields.Float(
|
|
string='AT Dienstwagen CO₂ (g/km)', tracking=True,
|
|
groups='hr_payroll.group_hr_payroll_user')
|
|
l10n_at_sb_wagen_halb = fields.Boolean(
|
|
string='AT Dienstwagen halber Sachbezug', tracking=True,
|
|
groups='hr_payroll.group_hr_payroll_user',
|
|
help='Nachweislich höchstens 500 Privat-km/Monat bzw. 6.000/Jahr; '
|
|
'Nachweis/Fahrtenbuch durch Arbeitgeber prüfen (lb-sac-03).')
|
|
l10n_at_sb_wagen_kostenbeitrag = fields.Monetary(
|
|
string='AT Dienstwagen Kostenbeitrag monatlich', currency_field='currency_id',
|
|
tracking=True, groups='hr_payroll.group_hr_payroll_user',
|
|
help='Laufender DN-Kostenbeitrag; mindert nach § 4 Abs 7 SBWVO '
|
|
'den Sachbezugswert (nicht einzelne Betriebskosten).')
|
|
|
|
l10n_at_sb_wohnung_aktiv = fields.Boolean(
|
|
string='AT Dienstwohnung überlassen', tracking=True,
|
|
groups='hr_payroll.group_hr_payroll_user')
|
|
l10n_at_sb_wohnung_flaeche = fields.Float(
|
|
string='AT Dienstwohnung Fläche (m²)', tracking=True,
|
|
groups='hr_payroll.group_hr_payroll_user')
|
|
l10n_at_sb_wohnung_bundesland = fields.Selection(
|
|
BUNDESLAENDER, string='AT Dienstwohnung Bundesland', tracking=True,
|
|
groups='hr_payroll.group_hr_payroll_user')
|
|
l10n_at_sb_wohnung_norm = fields.Boolean(
|
|
string='AT Dienstwohnung Normwohnung', default=True, tracking=True,
|
|
groups='hr_payroll.group_hr_payroll_user')
|
|
l10n_at_sb_wohnung_besorger = fields.Boolean(
|
|
string='AT Dienstwohnung Hausbesorger/Betreuer', tracking=True,
|
|
groups='hr_payroll.group_hr_payroll_user')
|
|
l10n_at_sb_wohnung_arbeitsplatznah = fields.Boolean(
|
|
string='AT Dienstwohnung arbeitsplatznah (≤ 15 Min.)', tracking=True,
|
|
groups='hr_payroll.group_hr_payroll_user')
|
|
l10n_at_sb_wohnung_kein_mittelpunkt = fields.Boolean(
|
|
string='AT Dienstwohnung nicht Mittelpunkt Lebensinteressen', tracking=True,
|
|
groups='hr_payroll.group_hr_payroll_user')
|
|
l10n_at_sb_wohnung_ueberlassung_ab = fields.Date(
|
|
string='AT Dienstwohnung überlassen ab', tracking=True,
|
|
groups='hr_payroll.group_hr_payroll_user')
|
|
l10n_at_sb_wohnung_kostenbeitrag = fields.Monetary(
|
|
string='AT Dienstwohnung Kostenbeitrag monatlich', currency_field='currency_id',
|
|
tracking=True, groups='hr_payroll.group_hr_payroll_user')
|
|
|
|
l10n_at_sb_parkplatz_aktiv = fields.Boolean(
|
|
string='AT Parkplatz in Kurzparkzone berechtigt', tracking=True,
|
|
groups='hr_payroll.group_hr_payroll_user')
|
|
l10n_at_sb_parkplatz_ausnahme = fields.Boolean(
|
|
string='AT Parkplatz sachbezugsbefreit', tracking=True,
|
|
groups='hr_payroll.group_hr_payroll_user',
|
|
help='Nachtdienst ohne Gebührenpflicht, Behinderung mit Pauschbetrag '
|
|
'oder kontrollierter Verzicht/Nichtparken (lb-sac-18).')
|
|
l10n_at_sb_parkplatz_kostenbeitrag = fields.Monetary(
|
|
string='AT Parkplatz Kostenbeitrag monatlich', currency_field='currency_id',
|
|
tracking=True, groups='hr_payroll.group_hr_payroll_user')
|
|
|
|
l10n_at_sb_handy_profil = fields.Selection(
|
|
[('beruflich', 'Beruflich/fallweise privat (kein Sachbezug)'),
|
|
('ueberwiegend_privat', 'Überwiegend privat (tatsächliche Kosten)'),
|
|
('private_ueberlassung', 'Private Überlassung (Rabatt-Regime)')],
|
|
string='AT Handy-/Arbeitsmittel-Nutzungsprofil', default='beruflich',
|
|
tracking=True, groups='hr_payroll.group_hr_payroll_user')
|
|
|
|
def _l10n_at_sb_wagen_wert(self, datum):
|
|
self.ensure_one()
|
|
if not (self.l10n_at_sb_wagen_aktiv and self.l10n_at_sb_wagen_ak):
|
|
return 0.0
|
|
co2 = self.l10n_at_sb_wagen_co2
|
|
if co2 == 0.0:
|
|
return 0.0
|
|
werte = self.env['hr.rule.parameter']._get_parameter_from_code(
|
|
'at_sb_wagen', datum, raise_if_not_found=False) or {}
|
|
grenzen = self.env['hr.rule.parameter']._get_parameter_from_code(
|
|
'at_sb_wagen_co2', datum, raise_if_not_found=False) or {}
|
|
ez_jahr = (self.l10n_at_sb_wagen_erstzulassung or datum).year
|
|
grenze = float(grenzen.get('ab_2025' if ez_jahr >= 2025
|
|
else str(ez_jahr),
|
|
grenzen.get('vor_2016', 0.0)))
|
|
niedrig = bool(grenze and co2 <= grenze)
|
|
art = 'niedrig' if niedrig else 'hoch'
|
|
prefix = 'halb' if self.l10n_at_sb_wagen_halb else 'voll'
|
|
wert = self.l10n_at_sb_wagen_ak * float(
|
|
werte.get('%s_%s_satz' % (prefix, art), 0.0)) / 100.0
|
|
wert = min(wert, float(werte.get('%s_%s_cap' % (prefix, art), 0.0)))
|
|
return max(wert - self.l10n_at_sb_wagen_kostenbeitrag, 0.0)
|
|
|
|
def _l10n_at_sb_wohnung_wert(self, datum):
|
|
self.ensure_one()
|
|
if not (self.l10n_at_sb_wohnung_aktiv
|
|
and self.l10n_at_sb_wohnung_flaeche
|
|
and self.l10n_at_sb_wohnung_bundesland):
|
|
return 0.0
|
|
werte = self.env['hr.rule.parameter']._get_parameter_from_code(
|
|
'at_sb_wohnung', datum, raise_if_not_found=False) or {}
|
|
flaeche = self.l10n_at_sb_wohnung_flaeche
|
|
klein = (self.l10n_at_sb_wohnung_arbeitsplatznah
|
|
and self.l10n_at_sb_wohnung_kein_mittelpunkt)
|
|
if klein and flaeche <= float(werte.get('klein_frei_m2', 0.0)):
|
|
return 0.0
|
|
wert = flaeche * float(werte.get(
|
|
self.l10n_at_sb_wohnung_bundesland, 0.0))
|
|
if (not self.l10n_at_sb_wohnung_norm
|
|
and self.l10n_at_sb_wohnung_besorger):
|
|
wert *= 1.0 - float(werte.get('kombiniert_abschlag', 0.0))
|
|
elif not self.l10n_at_sb_wohnung_norm:
|
|
wert *= 1.0 - float(werte.get('nicht_norm_abschlag', 0.0))
|
|
elif self.l10n_at_sb_wohnung_besorger:
|
|
wert *= 1.0 - float(werte.get('besorger_abschlag', 0.0))
|
|
if (klein and flaeche <= float(werte.get('klein_abschlag_bis_m2', 0.0))
|
|
and self.l10n_at_sb_wohnung_ueberlassung_ab
|
|
and datum < self.l10n_at_sb_wohnung_ueberlassung_ab
|
|
+ relativedelta(months=12)):
|
|
wert *= 1.0 - float(werte.get('klein_abschlag', 0.0))
|
|
return max(wert - self.l10n_at_sb_wohnung_kostenbeitrag, 0.0)
|
|
|
|
def _l10n_at_sb_parkplatz_wert(self, datum):
|
|
self.ensure_one()
|
|
if not (self.l10n_at_sb_parkplatz_aktiv
|
|
and not self.l10n_at_sb_parkplatz_ausnahme):
|
|
return 0.0
|
|
wert = float(self.env['hr.rule.parameter']._get_parameter_from_code(
|
|
'at_sb_parkplatz', datum, raise_if_not_found=False) or 0.0)
|
|
return max(wert - self.l10n_at_sb_parkplatz_kostenbeitrag, 0.0)
|
|
|
|
|
|
class HrEmployee(models.Model):
|
|
_inherit = 'hr.employee'
|
|
|
|
l10n_at_sb_wagen_aktiv = fields.Boolean(
|
|
related='version_id.l10n_at_sb_wagen_aktiv', inherited=True,
|
|
readonly=False, groups='hr_payroll.group_hr_payroll_user')
|
|
l10n_at_sb_wagen_ak = fields.Monetary(
|
|
related='version_id.l10n_at_sb_wagen_ak', inherited=True,
|
|
readonly=False, currency_field='currency_id',
|
|
groups='hr_payroll.group_hr_payroll_user')
|
|
l10n_at_sb_wagen_erstzulassung = fields.Date(
|
|
related='version_id.l10n_at_sb_wagen_erstzulassung', inherited=True,
|
|
readonly=False, groups='hr_payroll.group_hr_payroll_user')
|
|
l10n_at_sb_wagen_co2 = fields.Float(
|
|
related='version_id.l10n_at_sb_wagen_co2', inherited=True,
|
|
readonly=False, groups='hr_payroll.group_hr_payroll_user')
|
|
l10n_at_sb_wagen_halb = fields.Boolean(
|
|
related='version_id.l10n_at_sb_wagen_halb', inherited=True,
|
|
readonly=False, groups='hr_payroll.group_hr_payroll_user')
|
|
l10n_at_sb_wagen_kostenbeitrag = fields.Monetary(
|
|
related='version_id.l10n_at_sb_wagen_kostenbeitrag', inherited=True,
|
|
readonly=False, currency_field='currency_id',
|
|
groups='hr_payroll.group_hr_payroll_user')
|
|
l10n_at_sb_wohnung_aktiv = fields.Boolean(
|
|
related='version_id.l10n_at_sb_wohnung_aktiv', inherited=True,
|
|
readonly=False, groups='hr_payroll.group_hr_payroll_user')
|
|
l10n_at_sb_wohnung_flaeche = fields.Float(
|
|
related='version_id.l10n_at_sb_wohnung_flaeche', inherited=True,
|
|
readonly=False, groups='hr_payroll.group_hr_payroll_user')
|
|
l10n_at_sb_wohnung_bundesland = fields.Selection(
|
|
related='version_id.l10n_at_sb_wohnung_bundesland', inherited=True,
|
|
readonly=False, groups='hr_payroll.group_hr_payroll_user')
|
|
l10n_at_sb_wohnung_norm = fields.Boolean(
|
|
related='version_id.l10n_at_sb_wohnung_norm', inherited=True,
|
|
readonly=False, groups='hr_payroll.group_hr_payroll_user')
|
|
l10n_at_sb_wohnung_besorger = fields.Boolean(
|
|
related='version_id.l10n_at_sb_wohnung_besorger', inherited=True,
|
|
readonly=False, groups='hr_payroll.group_hr_payroll_user')
|
|
l10n_at_sb_wohnung_arbeitsplatznah = fields.Boolean(
|
|
related='version_id.l10n_at_sb_wohnung_arbeitsplatznah', inherited=True,
|
|
readonly=False, groups='hr_payroll.group_hr_payroll_user')
|
|
l10n_at_sb_wohnung_kein_mittelpunkt = fields.Boolean(
|
|
related='version_id.l10n_at_sb_wohnung_kein_mittelpunkt', inherited=True,
|
|
readonly=False, groups='hr_payroll.group_hr_payroll_user')
|
|
l10n_at_sb_wohnung_ueberlassung_ab = fields.Date(
|
|
related='version_id.l10n_at_sb_wohnung_ueberlassung_ab', inherited=True,
|
|
readonly=False, groups='hr_payroll.group_hr_payroll_user')
|
|
l10n_at_sb_wohnung_kostenbeitrag = fields.Monetary(
|
|
related='version_id.l10n_at_sb_wohnung_kostenbeitrag', inherited=True,
|
|
readonly=False, currency_field='currency_id',
|
|
groups='hr_payroll.group_hr_payroll_user')
|
|
l10n_at_sb_parkplatz_aktiv = fields.Boolean(
|
|
related='version_id.l10n_at_sb_parkplatz_aktiv', inherited=True,
|
|
readonly=False, groups='hr_payroll.group_hr_payroll_user')
|
|
l10n_at_sb_parkplatz_ausnahme = fields.Boolean(
|
|
related='version_id.l10n_at_sb_parkplatz_ausnahme', inherited=True,
|
|
readonly=False, groups='hr_payroll.group_hr_payroll_user')
|
|
l10n_at_sb_parkplatz_kostenbeitrag = fields.Monetary(
|
|
related='version_id.l10n_at_sb_parkplatz_kostenbeitrag', inherited=True,
|
|
readonly=False, currency_field='currency_id',
|
|
groups='hr_payroll.group_hr_payroll_user')
|
|
l10n_at_sb_handy_profil = fields.Selection(
|
|
related='version_id.l10n_at_sb_handy_profil', inherited=True,
|
|
readonly=False, groups='hr_payroll.group_hr_payroll_user')
|