Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions interface-prototype.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,8 @@
" \"aus_selbstständiger_arbeit\": {\"betrag_m\": \"income_from_self_employment\"},\n",
" \"aus_kapitalvermögen\": {\"kapitalerträge_m\": \"income_from_capital\"},\n",
" \"sonstige\": {\n",
" \"ohne_renten_m\": \"income_from_other_sources\",\n",
" \"renteneinkünfte_m\": \"pension_income\",\n",
" \"alle_weiteren_m\": \"income_from_other_sources\",\n",
" \"betrag_m\": \"pension_income\",\n",
" },\n",
" },\n",
" \"abzüge\": {\n",
Expand Down
8 changes: 4 additions & 4 deletions src/_gettsim/arbeitslosengeld_2/einkommen.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,12 +82,12 @@ def nettoeinkommen_vor_abzug_freibetrag_m(
@policy_function(start_date="2005-01-01")
def bruttoeinkommen_m(
einkommensteuer__einkünfte__aus_nichtselbstständiger_arbeit__bruttolohn_m: float,
einkommensteuer__einkünfte__sonstige__ohne_renten_m: float,
einkommensteuer__einkünfte__aus_selbstständiger_arbeit__betrag_m: float,
einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: float,
einkommensteuer__einkünfte__sonstige__alle_weiteren_m: float,
einkommensteuer__einkünfte__sonstige__rente__einnahmen_aus_privaten_und_betrieblichen_renten: float,
einkommensteuer__einkünfte__aus_kapitalvermögen__kapitalerträge_m: float,
sozialversicherung__rente__altersrente__betrag_m: float,
sozialversicherung__rente__private_rente_betrag_m: float,
sozialversicherung__arbeitslosen__betrag_m: float,
elterngeld__betrag_m: float,
) -> float:
Expand All @@ -97,12 +97,12 @@ def bruttoeinkommen_m(
"""
return (
einkommensteuer__einkünfte__aus_nichtselbstständiger_arbeit__bruttolohn_m
+ einkommensteuer__einkünfte__sonstige__ohne_renten_m
+ einkommensteuer__einkünfte__aus_selbstständiger_arbeit__betrag_m
+ einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m
+ einkommensteuer__einkünfte__aus_kapitalvermögen__kapitalerträge_m
+ einkommensteuer__einkünfte__sonstige__alle_weiteren_m
+ einkommensteuer__einkünfte__sonstige__rente__einnahmen_aus_privaten_und_betrieblichen_renten
+ sozialversicherung__rente__altersrente__betrag_m
+ sozialversicherung__rente__private_rente_betrag_m
+ sozialversicherung__arbeitslosen__betrag_m
+ elterngeld__betrag_m
)
Expand Down
11 changes: 1 addition & 10 deletions src/_gettsim/einkommensteuer/einkünfte/sonstige/inputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,9 @@


@policy_input()
def ohne_renten_m() -> float:
def alle_weiteren_m() -> float:
"""Additional income: includes private and public transfers that are not yet
implemented in GETTSIM (e.g., BAföG, Kriegsopferfürsorge).

Excludes income from public and private pensions.
"""


@policy_input(start_date="2021-01-01")
def renteneinkünfte_vorjahr_m() -> float:
"""Income from private and public pensions in the previous year.

GETTSIM can calculate this input based on the data of the previous year using the
target `("einkommensteuer", "einkünfte", "sonstige", "renteneinkünfte_m")`.
"""
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from __future__ import annotations
46 changes: 46 additions & 0 deletions src/_gettsim/einkommensteuer/einkünfte/sonstige/rente/inputs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
"""Input columns."""

from __future__ import annotations

from ttsim.tt_dag_elements import policy_input


@policy_input()
def alle_weiteren_m() -> float:
"""Additional income: includes private and public transfers that are not yet
implemented in GETTSIM (e.g., BAföG, Kriegsopferfürsorge).

Excludes income from public and private pensions.
"""


@policy_input(start_date="2021-01-01")
def gesamtbetrag_vorjahr_m() -> float:
"""Income from private and public pensions in the previous year.

GETTSIM can calculate this input based on the data of the previous year using the
target `("einkommensteuer", "einkünfte", "sonstige", "betrag_m")`.
"""


@policy_input()
def sonstige_private_vorsorge_m() -> float:
"""Monthly payout from private pensions without tax-favored contributions.

This refers to pension payments from plans where the original
contributions were not tax-deductible (or tax-exempt).
"""


@policy_input()
def geförderte_private_vorsorge_m() -> float:
"""Monthly payout from private pensions with tax-favored contributions.

This refers to pension payments from plans where the original
contributions were tax-deductible (or tax-exempt). Primarily Riesterrente.
"""


@policy_input()
def betriebliche_altersvorsorge_m() -> float:
"""Amount of monthly occupational pension."""
65 changes: 65 additions & 0 deletions src/_gettsim/einkommensteuer/einkünfte/sonstige/rente/rente.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
"""Sonstige Einkünfte according to § 22 EStG."""

from __future__ import annotations

from typing import TYPE_CHECKING

if TYPE_CHECKING:
from types import ModuleType

from ttsim.tt_dag_elements import (
PiecewisePolynomialParamValue,
piecewise_polynomial,
policy_function,
)


@policy_function()
def betrag_m(
ertragsanteil: float,
sozialversicherung__rente__altersrente__betrag_m: float,
geförderte_private_vorsorge_m: float,
sonstige_private_vorsorge_m: float,
betriebliche_altersvorsorge_m: float,
) -> float:
"""Pension income counting towards taxable income.
Reference: § 22 EStG
"""
return (
ertragsanteil
* (
sozialversicherung__rente__altersrente__betrag_m
+ sonstige_private_vorsorge_m
)
+ betriebliche_altersvorsorge_m
+ geförderte_private_vorsorge_m
)


@policy_function()
def ertragsanteil(
sozialversicherung__rente__jahr_renteneintritt: int,
parameter_ertragsanteil: PiecewisePolynomialParamValue,
xnp: ModuleType,
) -> float:
"""Share of pensions subject to income taxation."""
return piecewise_polynomial(
x=sozialversicherung__rente__jahr_renteneintritt,
parameters=parameter_ertragsanteil,
xnp=xnp,
)


@policy_function()
def einnahmen_aus_privaten_und_betrieblichen_renten(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I would prefer removing this function. It has nothing to do with einkommensteuer and having einnahmen underneath the einkünfte namespace is a bit confusing. I'd just use the three components as arguments to the three functions where it is being used.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm indifferent, but I feel like we should make a general rule out of this to make it easier for future developers.

What do you think about a "GETTSIM style guide", i.e. conventions that are not strictly required but an ideal we aim for? I feel like there is a growing informal knowledge between the two of us that others will struggle to pick up.

Obv nothing that should be done right now.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

having einnahmen underneath the einkünfte namespace is a bit confusing

This speaks for adding an einnahmen namespace and put all the policy_inputs of einkünfte in there. Those are all einnahmen.

Copy link
Collaborator

@hmgaudecker hmgaudecker Jul 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, that would be a major change, which would seem conceptually very helpful though:

root
└── einnahmen
    ├── aus_nichtselbstständiger_arbeit
    |     └── bruttolohn_m
    ├── renten
    |   ├── gesetzliche_m(sozialversicherung...altersrente, sozialversicherung...erwerbsminderungsrente)
    |   ├── betriebliche_altersvorsorge_m
    |   ├── geförderte_private_vorsorge_m
    |   └── sonstige_private_vorsorge_m
    ⋮
    └── aus_forst_und_landwirtschaft_y
└── einkommensteuer
    ├── einkünfte
    |   ├── aus_nichtselbstständiger_arbeit
    |       ├── betrag_y(einnahmen__aus_nichtselbstständiger_arbeit__bruttolohn_y)
    ⋮
    |   └──  I feel like we should make a general rule out of this to make it easier for future developers.
    |       ├── betrag_y(einnahmen__aus_forst_und_landwirtschaft_y)
    ⋮

That is, the einnahmen only follow EStG in the classification, but they are otherwise detached from it and we may have things that are actually different.

Conceptually, in most places einnahmen__gesetzliche_m is relevant as opposed to the fact that it is in the sozialversicherung namespace. Would also make it clearer for people who want to override this.

Looking at it, we probably would leave out the einnahmen___aus_forst_und_landwirtschaft_y since we don't model the deductions, right?

I feel like we should make a general rule out of this to make it easier for future developers.

Not sure there can be a general rule in terms of "always sum up three values" or not. Maybe I'll have a different opinion on this one after the above change, too!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you add things like income from ALG2, Elterngeld etc. to this namespace?

The big upside of the current structure is that we adhere very closely to the law. If we have the top-level einnahmen namespace it becomes apparent what we mean with bruttolohn_m only when looking at the docstring.

However, if we add all kinds of income there (not only EStG relevant), this would end the struggle of many users to find the correct nodes of the TT DAG.

I tend towards the pros outweighing the cons.

Not sure there can be a general rule in terms of "always sum up three values" or not.

I'm thinking more about ordering of function arguments, ordering of functions inside the modules, naming conventions (Einnahmen/Einkünfte,...), etc.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure there can be a general rule in terms of "always sum up three values" or not.

I'm thinking more about ordering of function arguments, ordering of functions inside the modules, naming conventions (Einnahmen/Einkünfte,...), etc.

Ah, then maybe I was confused by the location of the comment... 😉 But yeah, will be useful to document somehow. Sounds like a job for an AI to tell us which rules we have been using predominantly!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you add things like income from ALG2, Elterngeld etc. to this namespace?

The big upside of the current structure is that we adhere very closely to the law. If we have the top-level einnahmen namespace it becomes apparent what we mean with bruttolohn_m only when looking at the docstring.

However, if we add all kinds of income there (not only EStG relevant), this would end the struggle of many users to find the correct nodes of the TT DAG.

I tend towards the pros outweighing the cons.

In the end, it is that a labels-based structure would be even better than our hierarchical structure. (I would not know how to do that, though). Same with wohnen, familie, ... We would love to give these things a bunch of labels, for whatever they are relevant. Instead, we need to file them in one drawer.

I guess the einnahmen are pretty close to things like familie -- i.e., state variables that are fairly independent of details of the taxes and transfers system. bruttolohn_[x] is a pretty good case in point. I count 38 occurrences in function interfaces outside of the einkommensteuer namespace...

Similar to the rente__gesetzliche_m suggestion above, we could think about copying over other sources of income. Though that might yield some room for confusion, so I'd do that only later.

sonstige_private_vorsorge_m: float,
geförderte_private_vorsorge_m: float,
betriebliche_altersvorsorge_m: float,
) -> float:
"""Private and occupational pension income."""
return (
sonstige_private_vorsorge_m
+ geförderte_private_vorsorge_m
+ betriebliche_altersvorsorge_m
)
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
parameter_ertragsanteil_der_rente:
parameter_ertragsanteil:
name:
de: >-
Ertragsanteil-Funktion für den Anteil an sozialversicherungspflichtigen
Expand Down
44 changes: 4 additions & 40 deletions src/_gettsim/einkommensteuer/einkünfte/sonstige/sonstige.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,49 +2,13 @@

from __future__ import annotations

from typing import TYPE_CHECKING

if TYPE_CHECKING:
from types import ModuleType

from ttsim.tt_dag_elements import (
PiecewisePolynomialParamValue,
piecewise_polynomial,
policy_function,
)
from ttsim.tt_dag_elements import policy_function


@policy_function()
def betrag_m(
ohne_renten_m: float,
renteneinkünfte_m: float,
alle_weiteren_m: float,
rente__betrag_m: float,
) -> float:
"""Total sonstige Einkünfte."""
return ohne_renten_m + renteneinkünfte_m


@policy_function()
def renteneinkünfte_m(
ertragsanteil_der_rente: float,
sozialversicherung__rente__altersrente__betrag_m: float,
sozialversicherung__rente__private_rente_betrag_m: float,
) -> float:
"""Pension income counting towards taxable income."""
return ertragsanteil_der_rente * (
sozialversicherung__rente__altersrente__betrag_m
+ sozialversicherung__rente__private_rente_betrag_m
)


@policy_function()
def ertragsanteil_der_rente(
sozialversicherung__rente__jahr_renteneintritt: int,
parameter_ertragsanteil_der_rente: PiecewisePolynomialParamValue,
xnp: ModuleType,
) -> float:
"""Share of pensions subject to income taxation."""
return piecewise_polynomial(
x=sozialversicherung__rente__jahr_renteneintritt,
parameters=parameter_ertragsanteil_der_rente,
xnp=xnp,
)
return alle_weiteren_m + rente__betrag_m
31 changes: 16 additions & 15 deletions src/_gettsim/grundsicherung/im_alter/einkommen.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@
@policy_function()
def einkommen_m(
erwerbseinkommen_m: float,
private_rente_betrag_m: float,
einkommen_aus_zusätzlicher_altersvorsorge_m: float,
gesetzliche_rente_m: float,
einkommensteuer__einkünfte__sonstige__ohne_renten_m: float,
einkommensteuer__einkünfte__sonstige__alle_weiteren_m: float,
einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: float,
kapitaleinkommen_brutto_m: float,
einkommensteuer__betrag_m_sn: float,
Expand All @@ -34,8 +34,8 @@ def einkommen_m(
total_income = (
erwerbseinkommen_m
+ gesetzliche_rente_m
+ private_rente_betrag_m
+ einkommensteuer__einkünfte__sonstige__ohne_renten_m
+ einkommen_aus_zusätzlicher_altersvorsorge_m
+ einkommensteuer__einkünfte__sonstige__alle_weiteren_m
+ einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m
+ kapitaleinkommen_brutto_m
+ elterngeld__anrechenbarer_betrag_m
Expand Down Expand Up @@ -110,8 +110,8 @@ def kapitaleinkommen_brutto_m_mit_freibetrag(


@policy_function(start_date="2011-01-01")
def private_rente_betrag_m(
sozialversicherung__rente__private_rente_betrag_m: float,
def einkommen_aus_zusätzlicher_altersvorsorge_m(
einkommensteuer__einkünfte__sonstige__rente__einnahmen_aus_privaten_und_betrieblichen_renten: float,
anrechnungsfreier_anteil_private_renteneinkünfte: PiecewisePolynomialParam,
grundsicherung__regelbedarfsstufen: Regelbedarfsstufen,
xnp: ModuleType,
Expand All @@ -121,18 +121,19 @@ def private_rente_betrag_m(

Legal reference: § 82 SGB XII Abs. 4
"""
sozialversicherung__rente__private_rente_betrag_m_amount_exempt = (
piecewise_polynomial(
x=sozialversicherung__rente__private_rente_betrag_m,
parameters=anrechnungsfreier_anteil_private_renteneinkünfte,
xnp=xnp,
)
freibetrag = piecewise_polynomial(
x=einkommensteuer__einkünfte__sonstige__rente__einnahmen_aus_privaten_und_betrieblichen_renten,
parameters=anrechnungsfreier_anteil_private_renteneinkünfte,
xnp=xnp,
)
upper = grundsicherung__regelbedarfsstufen.rbs_1 / 2

return sozialversicherung__rente__private_rente_betrag_m - min(
sozialversicherung__rente__private_rente_betrag_m_amount_exempt,
upper,
return (
einkommensteuer__einkünfte__sonstige__rente__einnahmen_aus_privaten_und_betrieblichen_renten
- min(
freibetrag,
upper,
)
)


Expand Down
4 changes: 2 additions & 2 deletions src/_gettsim/sozialversicherung/kranken/beitrag/einkommen.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,12 +106,12 @@ def bezugsgröße_selbstständige_m(
@policy_function()
def bemessungsgrundlage_rente_m(
sozialversicherung__rente__altersrente__betrag_m: float,
sozialversicherung__rente__private_rente_betrag_m: float,
einkommensteuer__einkünfte__sonstige__rente__betriebliche_altersvorsorge_m: float,
beitragsbemessungsgrenze_m: float,
) -> float:
"""Pension income which is subject to health insurance contribution."""
return min(
sozialversicherung__rente__altersrente__betrag_m
+ sozialversicherung__rente__private_rente_betrag_m,
+ einkommensteuer__einkünfte__sonstige__rente__betriebliche_altersvorsorge_m,
beitragsbemessungsgrenze_m,
)
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def betrag_m(basisbetrag_m: float, anzurechnendes_einkommen_m: float) -> float:

@policy_function(start_date="2021-01-01")
def einkommen_m(
einkommensteuer__einkünfte__sonstige__renteneinkünfte_vorjahr_m: float,
einkommensteuer__einkünfte__sonstige__rente__gesamtbetrag_vorjahr_m: float,
einkommensteuer__einkünfte__aus_nichtselbstständiger_arbeit__bruttolohn_vorjahr_m: float,
einkommensteuer__einkünfte__aus_selbstständiger_arbeit__betrag_m: float,
einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: float,
Expand Down Expand Up @@ -57,7 +57,7 @@ def einkommen_m(
"""
# Sum income over different income sources.
return (
einkommensteuer__einkünfte__sonstige__renteneinkünfte_vorjahr_m
einkommensteuer__einkünfte__sonstige__rente__gesamtbetrag_vorjahr_m
+ einkommensteuer__einkünfte__aus_nichtselbstständiger_arbeit__bruttolohn_vorjahr_m
+ einkommensteuer__einkünfte__aus_selbstständiger_arbeit__betrag_m # income from self-employment
+ einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m # rental income
Expand Down
5 changes: 0 additions & 5 deletions src/_gettsim/sozialversicherung/rente/inputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,6 @@ def pflichtbeitragsmonate() -> float:
"""Total months of mandatory pension insurance contributions."""


@policy_input()
def private_rente_betrag_m() -> float:
"""Amount of monthly private pension."""


@policy_input()
def pflegeberücksichtigungszeiten_monate() -> float:
"""Total months of home care provision (01.01.1992-31.03.1995)."""
8 changes: 4 additions & 4 deletions src/_gettsim/unterhaltsvorschuss/unterhaltsvorschuss.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,23 +300,23 @@ def mindesteinkommen_erreicht(
@policy_function(start_date="2017-07-01")
def einkommen_m(
einkommensteuer__einkünfte__aus_nichtselbstständiger_arbeit__bruttolohn_m: float,
einkommensteuer__einkünfte__sonstige__ohne_renten_m: float,
einkommensteuer__einkünfte__aus_selbstständiger_arbeit__betrag_m: float,
einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m: float,
einkommensteuer__einkünfte__aus_kapitalvermögen__kapitalerträge_m: float,
einkommensteuer__einkünfte__sonstige__alle_weiteren_m: float,
einkommensteuer__einkünfte__sonstige__rente__einnahmen_aus_privaten_und_betrieblichen_renten: float,
sozialversicherung__rente__altersrente__betrag_m: float,
sozialversicherung__rente__private_rente_betrag_m: float,
sozialversicherung__arbeitslosen__betrag_m: float,
) -> float:
"""Calculate relevant income for advance on alimony payment."""
return (
einkommensteuer__einkünfte__aus_nichtselbstständiger_arbeit__bruttolohn_m
+ einkommensteuer__einkünfte__sonstige__ohne_renten_m
+ einkommensteuer__einkünfte__sonstige__alle_weiteren_m
+ einkommensteuer__einkünfte__aus_selbstständiger_arbeit__betrag_m
+ einkommensteuer__einkünfte__aus_vermietung_und_verpachtung__betrag_m
+ einkommensteuer__einkünfte__aus_kapitalvermögen__kapitalerträge_m
+ sozialversicherung__rente__altersrente__betrag_m
+ sozialversicherung__rente__private_rente_betrag_m
+ einkommensteuer__einkünfte__sonstige__rente__einnahmen_aus_privaten_und_betrieblichen_renten
+ sozialversicherung__arbeitslosen__betrag_m
)

Expand Down
Loading
Loading