from _gettsim.piecewise_functions import piecewise_polynomial
from _gettsim.shared import add_rounding_spec
[docs]@add_rounding_spec(params_key="ges_rente")
def grundr_zuschlag_m(
grundr_zuschlag_vor_eink_anr_m: float, grundr_zuschlag_eink_m: float
) -> float:
"""Calculate Grundrentenzuschlag (additional monthly pensions payments resulting
from Grundrente)
Parameters
----------
grundr_zuschlag_vor_eink_anr_m
See :func:`grundr_zuschlag_vor_eink_anr_m`.
grundr_zuschlag_eink_m
See :func:`grundr_zuschlag_eink_m`.
Returns
-------
"""
out = grundr_zuschlag_vor_eink_anr_m - grundr_zuschlag_eink_m
return max(out, 0.0)
def _grundr_zuschlag_eink_vor_freibetrag_m(
rente_vorj_vor_grundr_proxy_m: float,
bruttolohn_vorj_m: float,
eink_selbst: float,
eink_vermietung: float,
kapitaleink: float,
) -> float:
"""Calculate total income relevant for Grundrentenzuschlag before deductions are
subtracted.
Some notes:
- The Grundrentenzuschlag (in previous years) is not part of the relevant income and
does not lower the Grundrentenzuschlag (reference: § 97a Abs. 2 S. 7 SGB VI).
- The Deutsche Rentenversicherung uses the income of the year two to three years
ago to be able to use administrative data on this income for the calculation:
"It can be assumed that the tax office regularly has the data two years after
the end of the assessment period, which can be retrieved from the pension
insurance."
- Warning: Currently, earnings of dependent work and pensions are based on the
last year, and other income on the current year instead of the year
two years ago to avoid the need for several new input variables.
# ToDo: Freibeträge for income are currently not considered
# ToDo: as freibeträge_tu depends on pension income through
# ToDo: `ges_krankenv_beitr_m` -> `vorsorgeaufw` -> `freibeträge`
Reference: § 97a Abs. 2 S. 1 SGB VI
Parameters
----------
rente_vorj_vor_grundr_proxy_m
See :func:`rente_vorj_vor_grundr_proxy_m`.
bruttolohn_vorj_m
See :func:`bruttolohn_vorj_m`.
eink_selbst
See :func:`eink_selbst`.
eink_vermietung
See :func:`eink_vermietung`.
kapitaleink
See :func:`kapitaleink`.
Returns
-------
"""
# Sum income over different income sources.
out = (
rente_vorj_vor_grundr_proxy_m
+ bruttolohn_vorj_m
+ eink_selbst / 12 # income from self-employment
+ eink_vermietung / 12 # rental income
+ kapitaleink / 12
)
return out
[docs]@add_rounding_spec(params_key="ges_rente")
def grundr_zuschlag_eink_m(
_grundr_zuschlag_eink_vor_freibetrag_m_tu: float,
gemeinsam_veranlagt_tu: bool,
rentenwert: float,
ges_rente_params: dict,
) -> float:
"""Calculate income which is deducted from Grundrentenzuschlag.
Apply allowances. There are upper and lower thresholds for singles and
couples. 60% of income between the upper and lower threshold is credited against
the Grundrentenzuschlag. All the income above the upper threshold is credited
against the Grundrentenzuschlag.
Reference: § 97a Abs. 4 S. 2, 4 SGB VI
Parameters
----------
_grundr_zuschlag_eink_vor_freibetrag_m_tu
See :func:`_grundr_zuschlag_eink_vor_freibetrag_m_tu`.
gemeinsam_veranlagt_tu
See :func:`gemeinsam_veranlagt_tu`.
rentenwert
See :func:`rentenwert`.
ges_rente_params
See params documentation :ref:`ges_rente_params <ges_rente_params>`.
Returns
-------
"""
# Calculate relevant income following the crediting rules using the values for
# singles and those for married subjects
# Note: Thresholds are defined relativ to rentenwert which is implemented by
# dividing the income by rentenwert and multiply rentenwert to the result.
if gemeinsam_veranlagt_tu:
einkommensanr_params = ges_rente_params["grundr_einkommensanr_verheiratet"]
else:
einkommensanr_params = ges_rente_params["grundr_einkommensanr_single"]
out = (
piecewise_polynomial(
x=_grundr_zuschlag_eink_vor_freibetrag_m_tu / rentenwert,
thresholds=einkommensanr_params["thresholds"],
rates=einkommensanr_params["rates"],
intercepts_at_lower_thresholds=einkommensanr_params[
"intercepts_at_lower_thresholds"
],
)
* rentenwert
)
return out
[docs]@add_rounding_spec(params_key="ges_rente")
def grundr_zuschlag_vor_eink_anr_m(
grundr_zuschlag_bonus_entgeltp: float,
grundr_bew_zeiten: int,
rentenwert: float,
ges_rente_zugangsfaktor: float,
ges_rente_params: dict,
) -> float:
"""Calculate additional monthly pensions payments resulting from Grundrente, without
taking into account income crediting rules.
The Zugangsfaktor is limited to 1 and considered Grundrentezeiten
are limited to 35 years (420 months).
Parameters
----------
grundr_zuschlag_bonus_entgeltp
See :func:`grundr_zuschlag_bonus_entgeltp`.
grundr_bew_zeiten
See basic input variable
:ref:`grundr_bew_zeiten <grundr_bew_zeiten>`.
rentenwert
See :func:`rentenwert`.
ges_rente_zugangsfaktor
See :func:`ges_rente_zugangsfaktor`.
ges_rente_params
See params documentation :ref:`ges_rente_params <ges_rente_params>`.
Returns
-------
"""
# Winsorize Bewertungszeiten and Zugangsfaktor at maximum values
grundr_bew_zeiten_wins = min(
grundr_bew_zeiten, ges_rente_params["grundr_zeiten"]["max"]
)
ges_rente_zugangsfaktor_wins = min(
ges_rente_zugangsfaktor, ges_rente_params["grundr_zugangsfaktor_max"]
)
out = (
grundr_zuschlag_bonus_entgeltp
* grundr_bew_zeiten_wins
* rentenwert
* ges_rente_zugangsfaktor_wins
)
return out
[docs]def grundr_bew_zeiten_avg_entgeltp(
grundr_entgeltp: float, grundr_bew_zeiten: int
) -> float:
"""Compute average number of Entgeltpunkte earned per month of
Grundrentenbewertungszeiten.
Parameters
----------
grundr_entgeltp
See basic input variable
:ref:`grundr_entgeltp <grundr_entgeltp>`.
grundr_bew_zeiten
See basic input variable
:ref:`grundr_bew_zeiten <grundr_bew_zeiten>`.
Returns
-------
"""
if grundr_bew_zeiten > 0:
out = grundr_entgeltp / grundr_bew_zeiten
# Return 0 if grundr_bew_zeiten is 0. Then, grundr_entgeltp should be 0, too.
else:
out = 0
return out
[docs]@add_rounding_spec(params_key="ges_rente")
def grundr_zuschlag_höchstwert_m(grundr_zeiten: int, ges_rente_params: dict) -> float:
"""Calculate the maximum allowed number of average Entgeltpunkte (per month) after
adding bonus of Entgeltpunkte for a given number of Grundrentenzeiten.
Parameters
----------
grundr_zeiten
See basic input variable :ref:`grundr_zeiten <grundr_zeiten>`.
ges_rente_params
See params documentation :ref:`ges_rente_params <ges_rente_params>`.
Returns
-------
"""
# Calculate number of months above minimum threshold
months_above_thresh = (
min(grundr_zeiten, ges_rente_params["grundr_zeiten"]["max"])
- ges_rente_params["grundr_zeiten"]["min"]
)
# Calculate höchstwert
out = (
ges_rente_params["grundr_höchstwert"]["base"]
+ ges_rente_params["grundr_höchstwert"]["increment"] * months_above_thresh
)
return out
[docs]@add_rounding_spec(params_key="ges_rente")
def grundr_zuschlag_bonus_entgeltp(
grundr_bew_zeiten_avg_entgeltp: float,
grundr_zuschlag_höchstwert_m: float,
grundr_zeiten: int,
ges_rente_params: dict,
) -> float:
"""Calculate additional Entgeltpunkte for pensioner.
In general, the average of monthly Entgeltpunkte earnd in Grundrentenzeiten is
doubled, or extended to the individual Höchstwert if doubling would exceed the
Höchstwert. Then, the value is multiplied by 0.875.
Legal reference: § 76g SGB VI
Parameters
----------
grundr_bew_zeiten_avg_entgeltp
See :func:`grundr_bew_zeiten_avg_entgeltp`.
grundr_zuschlag_höchstwert_m
See :func:`grundr_zuschlag_höchstwert_m`.
grundr_zeiten
See basic input variable :ref:`grundr_zeiten <grundr_zeiten>`.
ges_rente_params
See params documentation :ref:`ges_rente_params <ges_rente_params>`.
Returns
-------
"""
# Return 0 if Grundrentenzeiten below minimum
if grundr_zeiten < ges_rente_params["grundr_zeiten"]["min"]:
out = 0.0
else:
# Case 1: Entgeltpunkte less than half of Höchstwert
if grundr_bew_zeiten_avg_entgeltp <= (0.5 * grundr_zuschlag_höchstwert_m):
out = grundr_bew_zeiten_avg_entgeltp
# Case 2: Entgeltpunkte more than half of Höchstwert, but below Höchstwert
elif grundr_bew_zeiten_avg_entgeltp < grundr_zuschlag_höchstwert_m:
out = grundr_zuschlag_höchstwert_m - grundr_bew_zeiten_avg_entgeltp
# Case 3: Entgeltpunkte above Höchstwert
elif grundr_bew_zeiten_avg_entgeltp > grundr_zuschlag_höchstwert_m:
out = 0.0
# Multiply additional Engeltpunkte by factor
out = out * ges_rente_params["grundr_faktor_bonus"]
return out
[docs]@add_rounding_spec(params_key="ges_rente")
def rente_vorj_vor_grundr_proxy_m( # noqa: PLR0913
rentner: bool,
rentenwert_vorjahr: float,
priv_rente_m: float,
jahr_renteneintr: int,
geburtsjahr: int,
alter: int,
entgeltp: float,
ges_rente_zugangsfaktor: float,
) -> float:
"""Estimated amount of public pensions of last year excluding Grundrentenzuschlag.
Parameters
----------
rentner
See basic input variable :ref:`rentner <rentner>`.
rentenwert_vorjahr
See :func:`rentenwert_vorjahr`.
priv_rente_m
See basic input variable :ref:`priv_rente_m <priv_rente_m>`.
jahr_renteneintr
See basic input variable :ref:`jahr_renteneintr <jahr_renteneintr>`.
geburtsjahr
See basic input variable :ref:`geburtsjahr <geburtsjahr>`.
alter
See basic input variable :ref:`alter <alter>`.
entgeltp
See basic input variable :ref:`entgeltp <entgeltp>`.
ges_rente_zugangsfaktor
See :func:`ges_rente_zugangsfaktor`.
Returns
-------
"""
# Assume priv_rente_m did not change
# ToDo: Use current_year as argument of this function once we addressed
# ToDo: issue #211
# Calculate if subect was retired last year
if rentner:
rentner_vorjahr = jahr_renteneintr < geburtsjahr + alter
else:
rentner_vorjahr = False
if rentner_vorjahr:
out = entgeltp * ges_rente_zugangsfaktor * rentenwert_vorjahr + priv_rente_m
else:
out = 0.0
return out
[docs]def grundr_berechtigt(grundr_zeiten: int, ges_rente_params: dict) -> bool:
"""Whether person has accumulated enough insured years to be eligible.
Parameters
----------
grundr_zeiten
See :func:`grundr_zeiten`.
ges_rente_params
See params documentation :ref:`ges_rente_params <ges_rente_params>`.
Returns
-------
"""
out = grundr_zeiten >= ges_rente_params["grundr_zeiten"]["min"]
return out