# 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')