Version 0.7.0: Kantonstarife 2026 (taxCH_AI/AR/SG/GR)

This commit is contained in:
wkh
2026-10-12 00:27:06 +02:00
parent a02e5cb109
commit 80941b6f03
11 changed files with 148 additions and 5 deletions
+10 -3
View File
@@ -7,10 +7,19 @@ folgen [Semantic Versioning](https://semver.org/lang/de/). Die Version steht in
## [Unveröffentlicht] ## [Unveröffentlicht]
## [0.6.0] - 2026-10-11 ## [0.7.0] - 2026-10-12
### Hinzugefügt ### Hinzugefügt
- `column -d Spalte ...` (und `project -d`) löscht Spalten: alle anderen bleiben in ihrer Reihenfolge. Fehler bei unbekannter Spalte oder wenn alle gelöscht würden. Dazu Abschnitt in `man/column.man`, Beispiel in `testall.cmds` und Tests. - `column -d Spalte ...` (und `project -d`) löscht Spalten: alle anderen bleiben in ihrer Reihenfolge. Fehler bei unbekannter Spalte oder wenn alle gelöscht würden. Dazu Abschnitt in `man/column.man`, Beispiel in `testall.cmds` und Tests.
- Kantonstarife 2026 für `tax`: `demo/taxCH_AI2026`, `taxCH_AR2026`, `taxCH_SG2026`, `taxCH_GR2026` (Präfix `taxCH_` für die Schweiz; einfache Staatssteuer für Alleinstehende, vor Steuerfuss, Gemeinde und Kirche), erzeugt mit `tools/gen_tax_tables.rb`. Mit Tests, Beispiel in `testall.cmds` und Erklärung zum Steuerfuss im README.
### Behoben
- `rdb man dashline` (und `headline`, `backup`, `dbase`, `accounting_ter`) zeigte als Überschrift eine Makrozeile (`dashline -- .PP`). Themen-Seiten ohne Beschreibung nehmen jetzt den Titel aus der `.TH`-Zeile.
- `splittable -0` und mehr als 676 Stücke endeten mit einem Ruby-Abbruch (im zweiten Fall nach 676 geschriebenen Dateien). Jetzt eine Fehlermeldung, und es wird vorher geprüft, damit nichts halb geschrieben wird.
## [0.6.0] - 2026-10-11
### Hinzugefügt
- Steuertabellen 2026 für `tax`: `demo/taxDE2026`, `taxAT2026`, `taxCH2026` (nur Bundessteuer), `taxLI2026` (nur Landessteuer), erzeugt mit `tools/gen_tax_tables.rb`, mit Tests. - Steuertabellen 2026 für `tax`: `demo/taxDE2026`, `taxAT2026`, `taxCH2026` (nur Bundessteuer), `taxLI2026` (nur Landessteuer), erzeugt mit `tools/gen_tax_tables.rb`, mit Tests.
### Geändert ### Geändert
@@ -18,8 +27,6 @@ folgen [Semantic Versioning](https://semver.org/lang/de/). Die Version steht in
- README und `man/row.man` erklären, warum `select` in bash/zsh/ksh einen Syntaxfehler gibt und wie man es trotzdem aufruft. - README und `man/row.man` erklären, warum `select` in bash/zsh/ksh einen Syntaxfehler gibt und wie man es trotzdem aufruft.
### Behoben ### Behoben
- `rdb man dashline` (und `headline`, `backup`, `dbase`, `accounting_ter`) zeigte als Überschrift eine Makrozeile (`dashline -- .PP`). Themen-Seiten ohne Beschreibung nehmen jetzt den Titel aus der `.TH`-Zeile.
- `splittable -0` und mehr als 676 Stücke endeten mit einem Ruby-Abbruch (im zweiten Fall nach 676 geschriebenen Dateien). Jetzt eine Fehlermeldung, und es wird vorher geprüft, damit nichts halb geschrieben wird.
- `rdb man` zeigte `\\select` in den Man-Pages als `\elect`: ein doppelter Backslash wird jetzt zuerst geschützt. - `rdb man` zeigte `\\select` in den Man-Pages als `\elect`: ein doppelter Backslash wird jetzt zuerst geschützt.
## [0.5.0] - 2026-10-11 ## [0.5.0] - 2026-10-11
+18
View File
@@ -130,6 +130,10 @@ Alleinstehende (Grundtarif). Erzeugt mit `ruby tools/gen_tax_tables.rb`.
| `taxAT2026` | Österreich, Einkommensteuer | § 33 EStG (0 % bis 13 539 €, dann 20/30/40/48/50/55 %) | exakt, vor Absetzbeträgen | | `taxAT2026` | Österreich, Einkommensteuer | § 33 EStG (0 % bis 13 539 €, dann 20/30/40/48/50/55 %) | exakt, vor Absetzbeträgen |
| `taxCH2026` | Schweiz, **nur direkte Bundessteuer** | Art. 36 DBG, ESTV-Rundschreiben 2-215-D-2025 | Stufen exakt, das Gesetz rechnet in 100-Franken-Schritten | | `taxCH2026` | Schweiz, **nur direkte Bundessteuer** | Art. 36 DBG, ESTV-Rundschreiben 2-215-D-2025 | Stufen exakt, das Gesetz rechnet in 100-Franken-Schritten |
| `taxLI2026` | Liechtenstein, **nur Landessteuer** | Art. 19 SteG (LR 640.0, Fassung 01.07.2026) | exakt | | `taxLI2026` | Liechtenstein, **nur Landessteuer** | Art. 19 SteG (LR 640.0, Fassung 01.07.2026) | exakt |
| `taxCH_AI2026` | Kanton Appenzell I.Rh., einfache Staatssteuer | Art. 38 StG (ESTV-Kantonsblatt) | exakt |
| `taxCH_AR2026` | Kanton Appenzell A.Rh., einfache Staatssteuer (Alleinstehendentarif) | Art. 39 StG, Beträge ab 2024 | exakt |
| `taxCH_SG2026` | Kanton St. Gallen, einfache Staatssteuer | Art. 50 StG, Beträge ab 2026 | exakt |
| `taxCH_GR2026` | Kanton Graubünden, einfache Kantonssteuer | Art. 39 StG, indexierte Beträge ab 2025 | exakt |
``` ```
tax 60000 < demo/taxDE2026 14233 tax 60000 < demo/taxDE2026 14233
@@ -143,6 +147,20 @@ Kirchensteuer und Soli sind nicht enthalten. Für die Schweiz kommen Kanton und
höher als der Bund), für Liechtenstein der Gemeindezuschlag: gesamt = Landessteuer × (1 + Zuschlag/100). höher als der Bund), für Liechtenstein der Gemeindezuschlag: gesamt = Landessteuer × (1 + Zuschlag/100).
Ehepaar- und Alleinerziehendentarife fehlen. Das ist keine Steuerberatung. Ehepaar- und Alleinerziehendentarife fehlen. Das ist keine Steuerberatung.
**Kantone (AI, AR, SG, GR):** Die Dateien tragen den Präfix `taxCH_` (Schweiz) vor dem Kantonskürzel; `taxCH2026` ist die Bundessteuer. Die Tabellen geben die *einfache Steuer* wieder. Sie wird mit dem Steuerfuss multipliziert,
und Gemeinde und Kirche kommen dazu. Der Kanton setzt den Steuerfuss pro Jahr fest:
| Kanton | Steuerfuss Kanton 2026 | Quelle |
|---|---|---|
| AI | 96 % | Kantonsblatt AI (Grossratsbeschluss 1.12.2025) |
| SG | 105 % | Budgetbeschluss des Kantonsrats (Websuche, nicht im Kantonsblatt) |
| GR | 92 % | Grosser Rat, Budget 2026 (Websuche) |
| AR | nicht ermittelt | AR rechnet in Steuereinheiten, die Zahl für 2026 habe ich nicht gefunden |
Beispiel AI bei 60 000: `tax 60000 < demo/taxCH_AI2026` = 3770, mal 0,96 = 3619 (Kantonssteuer, ohne Bezirk, Gemeinde und Kirche).
Oberhalb einer Grenze gilt bei allen vier Kantonen ein fester Satz auf das *ganze* Einkommen (AI ab 200 000 mit 8 %,
AR ab 260 800 mit 2,6 %, SG ab 264 500 mit 8,5 %, GR ab 780 440 mit 11 %); in der Tabelle ist das die letzte Zeile.
## Befehle ## Befehle
| Gruppe | Befehle | | Gruppe | Befehle |
+16
View File
@@ -0,0 +1,16 @@
income tax percent
------ --- -------
0 0.00 0
3000 0.00 1
6000 30.00 2
9000 90.00 3
12000 180.00 4
15000 300.00 5
18000 450.00 6
22000 690.00 7
26000 970.00 7.5
30000 1270.00 8
40000 2070.00 8.5
74000 4960.00 9
140000 10900.00 8.5
200000 16000.00 8
+14
View File
@@ -0,0 +1,14 @@
income tax percent
------ --- -------
0 0.00 0
8300 0.00 0.6
9900 9.60 1
11500 25.60 1.5
15700 88.60 1.8
27200 295.60 2.2
41800 616.80 2.4
54300 916.80 2.6
74100 1431.60 2.7
88700 1825.80 2.8
125200 2847.80 2.9
260800 6780.80 2.6
+21
View File
@@ -0,0 +1,21 @@
income tax percent
------ --- -------
0 0.00 0
16895 0.00 2.5
17985 27.25 4
19075 70.85 5
20165 125.35 6
21255 190.75 6.5
22345 261.60 7
24525 414.20 8
31065 937.40 8.5
35425 1308.00 9
39785 1700.40 9.5
44145 2114.60 10.3
65945 4360.00 10.6
87745 6670.80 10.7
109545 9003.40 11.2
218545 21211.40 11.3
327545 33528.40 11.4
436545 45954.40 11.6
780440 85848.40 11
+9
View File
@@ -0,0 +1,9 @@
income tax percent
------ --- -------
0 0.00 0
11600 0.00 4
15800 168.00 6
33800 1248.00 8
60300 3368.00 9.2
98400 6873.20 9.4
264500 22482.50 8.5
+5
View File
@@ -238,6 +238,11 @@ tax 60000 < taxDE2026
tax 60000 < taxAT2026 tax 60000 < taxAT2026
tax 60000 < taxCH2026 tax 60000 < taxCH2026
tax 60000 < taxLI2026 tax 60000 < taxLI2026
## Swiss cantons (einfache Staatssteuer, single; still times the cantonal Steuerfuss, plus Gemeinde): AI AR SG GR
tax 60000 < taxCH_AI2026
tax 60000 < taxCH_AR2026
tax 60000 < taxCH_SG2026
tax 60000 < taxCH_GR2026
## explode: parts list -> all subparts with counts (part, amount) ## explode: parts list -> all subparts with counts (part, amount)
explode 10001 2 parttable explode 10001 2 parttable
## chartdup: look for duplicate account numbers and short names in chart ## chartdup: look for duplicate account numbers and short names in chart
+10 -1
View File
@@ -880,7 +880,7 @@ Item Amount Cost Value Description
## tabletom4 / tabletosed / tabletostruct were shown above ## tabletom4 / tabletosed / tabletostruct were shown above
## ===== Help and the terminal ===== ## ===== Help and the terminal =====
$ rdb --version $ rdb --version
rdb 0.6.0 rdb 0.7.0
$ howmany $ howmany
136 136
$ whatis row $ whatis row
@@ -980,6 +980,15 @@ $ tax 60000 < taxCH2026
671 671
$ tax 60000 < taxLI2026 $ tax 60000 < taxLI2026
1396 1396
## Swiss cantons (einfache Staatssteuer, single; still times the cantonal Steuerfuss, plus Gemeinde): AI AR SG GR
$ tax 60000 < taxCH_AI2026
3770
$ tax 60000 < taxCH_AR2026
1065
$ tax 60000 < taxCH_SG2026
3344
$ tax 60000 < taxCH_GR2026
3748
## explode: parts list -> all subparts with counts (part, amount) ## explode: parts list -> all subparts with counts (part, amount)
$ explode 10001 2 parttable $ explode 10001 2 parttable
Part Amount Part Amount
+1 -1
View File
@@ -1,5 +1,5 @@
# frozen_string_literal: true # frozen_string_literal: true
module Rdb module Rdb
VERSION = '0.6.0' VERSION = '0.7.0'
end end
+13
View File
@@ -812,6 +812,19 @@ class TaxTablesTest < RdbCase
assert_equal 0, tax(15_855, 'taxLI2026') assert_equal 0, tax(15_855, 'taxLI2026')
end end
def test_swiss_cantons_2026_einfache_staatssteuer
assert_equal 3770, tax(60_000, 'taxCH_AI2026') # 2 070 + 8,5 % of 20 000
assert_equal 16_000, tax(200_000, 'taxCH_AI2026')
assert_equal 24_000, tax(300_000, 'taxCH_AI2026') # 8 % of the whole income
assert_equal 1065, tax(60_000, 'taxCH_AR2026')
assert_equal 3344, tax(60_000, 'taxCH_SG2026') # 1 248 + 8 % + 9,2 % pieces
assert_equal 25_500, tax(300_000, 'taxCH_SG2026') # 8,5 % of the whole income
assert_equal 3748, tax(60_000, 'taxCH_GR2026')
assert_equal 0, tax(16_895, 'taxCH_GR2026')
assert_equal 20, tax(5_000, 'taxCH_AI2026') # 1 % of 2 000
%w[AR SG GR].each { |c| assert_equal 0, tax(5_000, "taxCH_#{c}2026") }
end
# § 32a EStG: the table is a straight-line approximation of the curved zones # § 32a EStG: the table is a straight-line approximation of the curved zones
def test_de_2026_follows_the_formula def test_de_2026_follows_the_formula
[12_348, 14_000, 17_799, 20_000, 33_333, 50_000, 69_878, 100_000, 277_825, 300_000].each do |x| [12_348, 14_000, 17_799, 20_000, 33_333, 50_000, 69_878, 100_000, 277_825, 300_000].each do |x|
+31
View File
@@ -61,3 +61,34 @@ li_rates.each do |limit, rate|
prev = [limit, rate] prev = [limit, rate]
end end
write_table('taxLI2026', pts) write_table('taxLI2026', pts)
# --- Swiss cantons: the "einfache Staatssteuer" for single persons, before the Steuerfuss and
# without municipal and church tax. Source: ESTV Kantonsblätter (Steuerinformationen 2026).
# Each tariff is a list of [width, rate] pieces; above the last piece one flat rate applies to
# the whole income (the cantons call this "für das ganze Einkommen").
def staircase(pieces, flat_rate, expect_limit)
acc = 0.0
start = 0
pts = []
pieces.each do |width, rate|
pts << [start, acc, rate]
acc += width * rate / 100.0
start += width
end
raise "limit #{start} != #{expect_limit}" unless start == expect_limit
pts << [start, start * flat_rate / 100.0, flat_rate]
end
# AI: Art. 38 StG
write_table('taxCH_AI2026', staircase([[3_000, 0], [3_000, 1], [3_000, 2], [3_000, 3], [3_000, 4], [3_000, 5], [4_000, 6],
[4_000, 7], [4_000, 7.5], [10_000, 8], [34_000, 8.5], [66_000, 9], [60_000, 8.5]], 8, 200_000))
# AR: Art. 39 Abs. 1 lit. b StG (Alleinstehendentarif, Beträge ab Steuerperiode 2024); multiplied by the Steuereinheiten
write_table('taxCH_AR2026', staircase([[8_300, 0], [1_600, 0.6], [1_600, 1.0], [4_200, 1.5], [11_500, 1.8], [14_600, 2.2],
[12_500, 2.4], [19_800, 2.6], [14_600, 2.7], [36_500, 2.8], [135_600, 2.9]], 2.6, 260_800))
# SG: Art. 50 StG (Beträge ab Steuerperiode 2026)
write_table('taxCH_SG2026', staircase([[11_600, 0], [4_200, 4], [18_000, 6], [26_500, 8], [38_100, 9.2], [166_100, 9.4]], 8.5, 264_500))
# GR: Art. 39 Abs. 1 StG (indexierte Beträge ab Steuerperiode 2025)
write_table('taxCH_GR2026', staircase([[16_895, 0], [1_090, 2.5], [1_090, 4], [1_090, 5], [1_090, 6], [1_090, 6.5], [2_180, 7],
[6_540, 8], [4_360, 8.5], [4_360, 9], [4_360, 9.5], [21_800, 10.3], [21_800, 10.6],
[21_800, 10.7], [109_000, 11.2], [109_000, 11.3], [109_000, 11.4], [343_895, 11.6]], 11.0, 780_440))