Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
15 changes: 7 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,19 +113,18 @@ The `TaxConfig` dataclass provides type-safe configuration:

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `year` | int | 2022 | Tax year (2018-2025 supported) |
| `year` | int | 2025 | Tax year (2018-2026 supported) |
| `is_married` | bool | False | Married status (Ehegattensplitting) |
| `has_children` | bool | False | Has children (affects nursing insurance) |
| `church_tax` | float | 0.09 | Church tax rate (0.0-0.09, set to 0.0 for none) |
| `extra_health_insurance` | float | 0.014 | Additional health insurance rate |
| `extra_health_insurance` | float | 0.025 | Additional health insurance rate |

## Supported Tax Years

| Year | Status | Notes |
|------|--------|-------|
| 2018-2022 | Fully supported | Complete tax data |
| 2023-2025 | Fully supported | Complete tax data |
| 2026-2027 | Planned | To be added |
| Year | Status | Notes |
|-----------|--------|-------|
| 2018-2026 | Fully supported | Complete tax data (2026 uses 2025 estimates) |
| 2027+ | Planned | To be added |

## Documentation

Expand All @@ -135,7 +134,7 @@ Full documentation is available at [netto.readthedocs.io](https://netto.readthed

All tax calculations are based on official German government sources:

- **Tax Calculation Formulas**: [BMF Tarifhistorie](https://www.bmf-steuerrechner.de/Tarifhistorie_Steuerrechner.pdf)
- **Tax Calculation Formulas**: [BMF Tarifhistorie](https://www.bmf-steuerrechner.de/javax.faces.resource/2025_1_14_Tarifhistorie_Steuerrechner.pdf.xhtml)
- **Wage Tax Calculator**: [BMF Lohnsteuerrechner](https://www.bmf-steuerrechner.de/)
- **Social Security Deductible**: [Vorsorgepauschale](https://www.lohn-info.de/vorsorgepauschale.html)
- **Social Security Rates**: [Sozialversicherungsbeiträge](https://www.lohn-info.de/sozialversicherungsbeitraege2024.html)
Expand Down
4 changes: 4 additions & 0 deletions data/pension_factors/2026.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"year": 2026,
"factor": 1.0
}
2 changes: 1 addition & 1 deletion data/social_security/2023.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
},
"unemployment": {
"limit": 87600,
"rate": 0.012
"rate": 0.013
},
"health": {
"limit": 59850,
Expand Down
6 changes: 3 additions & 3 deletions data/social_security/2024.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@
},
"unemployment": {
"limit": 90600,
"rate": 0.012
"rate": 0.013
},
"health": {
"limit": 62100,
"rate": 0.073
},
"nursing": {
"limit": 62100,
"rate": 0.01525,
"extra": 0.0035
"rate": 0.017,
"extra": 0.006
}
}
10 changes: 5 additions & 5 deletions data/social_security/2025.json
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
{
"year": 2025,
"pension": {
"limit": 96000,
"limit": 96600,
"rate": 0.093
},
"unemployment": {
"limit": 96000,
"rate": 0.012
"limit": 96600,
"rate": 0.013
},
"health": {
"limit": 66150,
"rate": 0.073
},
"nursing": {
"limit": 66150,
"rate": 0.01525,
"extra": 0.0035
"rate": 0.018,
"extra": 0.006
}
}
20 changes: 20 additions & 0 deletions data/social_security/2026.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"year": 2026,
"pension": {
"limit": 101400,
"rate": 0.093
},
"unemployment": {
"limit": 101400,
"rate": 0.013
},
"health": {
"limit": 69750,
"rate": 0.073
},
"nursing": {
"limit": 69750,
"rate": 0.018,
"extra": 0.006
}
}
2 changes: 1 addition & 1 deletion data/soli/2025.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"year": 2025,
"start_taxable_income": 19450,
"start_taxable_income": 19950,
"start_fraction": 0.119,
"end_rate": 0.055
}
6 changes: 6 additions & 0 deletions data/soli/2026.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"year": 2026,
"start_taxable_income": 20350,
"start_fraction": 0.119,
"end_rate": 0.055
}
2 changes: 1 addition & 1 deletion data/tax_curves/2021.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"const": [208.85, 2397, 950.96]
},
"3": {
"step": 274613,
"step": 274612,
"rate": 0.45,
"const": [9136.63, 17374.99]
}
Expand Down
8 changes: 4 additions & 4 deletions data/tax_curves/2023.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,24 @@
"year": 2023,
"brackets": {
"0": {
"step": 10909,
"step": 10908,
"rate": 0.14,
"const": null
},
"1": {
"step": 15999,
"rate": 0.2397,
"const": null
"const": [979.18, 1400]
},
"2": {
"step": 62809,
"rate": 0.42,
"const": null
"const": [192.59, 2397, 966.53]
},
"3": {
"step": 277826,
"rate": 0.45,
"const": null
"const": [9972.98, 18307.73]
}
}
}
8 changes: 4 additions & 4 deletions data/tax_curves/2024.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,24 @@
"year": 2024,
"brackets": {
"0": {
"step": 11605,
"step": 11784,
"rate": 0.14,
"const": null
},
"1": {
"step": 17005,
"rate": 0.2397,
"const": null
"const": [954.80, 1400]
},
"2": {
"step": 66760,
"rate": 0.42,
"const": null
"const": [181.19, 2397, 991.21]
},
"3": {
"step": 277826,
"rate": 0.45,
"const": null
"const": [10636.31, 18971.06]
}
}
}
12 changes: 6 additions & 6 deletions data/tax_curves/2025.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,24 @@
"year": 2025,
"brackets": {
"0": {
"step": 12086,
"step": 12096,
"rate": 0.14,
"const": null
},
"1": {
"step": 17430,
"step": 17443,
"rate": 0.2397,
"const": null
"const": [932.30, 1400]
},
"2": {
"step": 68430,
"step": 68480,
"rate": 0.42,
"const": null
"const": [176.64, 2397, 1015.13]
},
"3": {
"step": 277826,
"rate": 0.45,
"const": null
"const": [10911.92, 19246.67]
}
}
}
25 changes: 25 additions & 0 deletions data/tax_curves/2026.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"year": 2026,
"brackets": {
"0": {
"step": 12348,
"rate": 0.14,
"const": null
},
"1": {
"step": 17799,
"rate": 0.2397,
"const": null
},
"2": {
"step": 69878,
"rate": 0.42,
"const": null
},
"3": {
"step": 277826,
"rate": 0.45,
"const": null
}
}
}
10 changes: 5 additions & 5 deletions netto/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class TaxConfig:
Parameters
----------
year : int
Tax year (2018-2025, default: 2022)
Tax year (2018-2026, default: 2025)
has_children : bool
Has children (affects nursing insurance)
is_married : bool
Expand All @@ -26,18 +26,18 @@ class TaxConfig:
>>> TaxConfig(church_tax=0.0)
"""

year: int = 2022
year: int = 2025
has_children: bool = False
is_married: bool = False
extra_health_insurance: float = 0.014
extra_health_insurance: float = 0.025
church_tax: float = 0.09

def __post_init__(self):
"""Validate configuration values."""
if not isinstance(self.year, int):
raise TypeError(f"year must be int, got {type(self.year)}")
if self.year < 2018 or self.year > 2025:
raise ValueError(f"year must be between 2018 and 2025, got {self.year}")
if self.year < 2018 or self.year > 2026:
raise ValueError(f"year must be between 2018 and 2026, got {self.year}")
if not isinstance(self.has_children, bool):
raise TypeError(f"has_children must be bool, got {type(self.has_children)}")
if not isinstance(self.is_married, bool):
Expand Down
16 changes: 8 additions & 8 deletions netto/data_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,8 @@ def load_social_security(year: int) -> dict:

Examples
--------
>>> ss = load_social_security(2022)
>>> ss['pension']['limit']
>>> social_sec = load_social_security(2022)
>>> social_sec['pension']['limit']
84600
"""
file_path = DATA_DIR / "social_security" / f"{year}.json"
Expand Down Expand Up @@ -226,7 +226,7 @@ def load_all_tax_curves() -> dict[int, dict[int, dict]]:
Tax curves for all years
"""
tax_curves = {}
for year in range(2018, 2026): # 2018-2025
for year in range(2018, 2027): # 2018-2026
try:
tax_curves[year] = load_tax_curve(year)
except FileNotFoundError:
Expand All @@ -244,14 +244,14 @@ def load_all_social_security() -> dict[int, dict]:
Social security data for all years
"""
social_security = {}
for year in range(2018, 2026): # 2018-2025
for year in range(2018, 2027): # 2018-2026
try:
social_security[year] = load_social_security(year)
except (FileNotFoundError, NotImplementedError):
pass

# Add NotImplementedError for 2026+ to maintain backward compatibility
social_security[2026] = NotImplementedError
# Add NotImplementedError for 2027+ to maintain backward compatibility
social_security[2027] = NotImplementedError

return social_security

Expand All @@ -266,7 +266,7 @@ def load_all_soli() -> dict[int, dict]:
Soli data for all years
"""
soli_data = {}
for year in range(2018, 2026): # 2018-2025
for year in range(2018, 2027): # 2018-2026
try:
soli_data[year] = load_soli(year)
except FileNotFoundError:
Expand All @@ -284,7 +284,7 @@ def load_all_pension_factors() -> dict[int, float]:
Pension correction factors for all years
"""
pension_factors = {}
for year in range(2018, 2026): # 2018-2025
for year in range(2018, 2027): # 2018-2026
try:
pension_factors[year] = load_pension_factor(year)
except FileNotFoundError:
Expand Down
6 changes: 3 additions & 3 deletions test/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
def test_taxconfig_defaults():
"""Test that TaxConfig uses correct default values"""
config = TaxConfig()
assert config.year == 2022
assert config.year == 2025
assert config.has_children is False
assert config.is_married is False
assert config.extra_health_insurance == 0.014
assert config.extra_health_insurance == 0.025
assert config.church_tax == 0.09


Expand All @@ -34,7 +34,7 @@ def test_taxconfig_validation_year_range():
with pytest.raises(ValueError):
TaxConfig(year=2017) # Too early
with pytest.raises(ValueError):
TaxConfig(year=2026) # Too late
TaxConfig(year=2027) # Too late


def test_taxconfig_validation_negative_rates():
Expand Down
Loading