ふるさと納税の限度額を理解する

ふるさと納税はシミュレーションサイトで限度額を計算できるが、
理屈がよくわからないので理解してみる。

# 原文

1. 所得税からの控除 = （ふるさと納税額－2,000円）×「所得税の税率」
2. 住民税からの控除（基本分） = （ふるさと納税額－2,000円）×10％
3. 住民税からの控除（特例分） = （ふるさと納税額 - 2,000円）×（100％ - 10％（基本分） - 所得税の税率）
4. 住民税からの控除（特例分） = （住民税所得割額）×20％

1の控除の対象となるふるさと納税額は、総所得金額等の40％が上限で、復興特別所得税(2.1%)を加えたものになります。
2の控除の対象となるふるさと納税額は、総所得金額等の30％が上限です。
3の住民税からの控除の特例分は、この特例分が住民税所得割額の2割を超えない場合で超える場合は4になります。

# 解釈

よくわからんときは数式で解釈してみよ。
まず3と4を比較すると4に引っかかると控除額が少なくなるので
3が適用されることは絶対である。
また1と2の上限は大きいので無視して良さそう。
以降は100を基準とした％という記号は使わずに、
1を基準とした相対の数字を使うこととし、
ふるさと納税額を$x$とおき、所得税率を$r$とおく。
すると

```math
\begin{align}
所得税からの控除 & = r (x - 2000) \\
住民税からの控除 & = 0.1(x - 2000) \\
住民税からの控除特例 & = (1 - 0.1 - r)(x - 2000) \\
\end{align}
```

おお、わかりやすい。
$x-2000$でくくることができて
きれいに消えて控除額合計は$x-2000$になる。
ふるさと納税は実質自己負担額が2000円という意味が理解できます。

では3.が適用される上限はどうすれば求まるのか。
住民税所得割額を$j_0$とおくと、

$$
j_0 * 0.2 = (x - 2000)(1 - 0.1 - r)
$$

これを$x$について解くと、

$$
x = \frac{j_0}{5(0.9 - r)} + 2000
$$

$r$が0.1とか0.2だとか考えるとほぼほぼ差はないので、
ほとんど住民税比例と考えていいなと見当がつく。
ただ、$r$はともかく、$j_0$を実際の所得から導出するのは地獄が待っている。


```python
def calc_furusato_limit(jumin_syotoku, syotoku_rate):
    return jumin_syotoku / (5 * (0.9 - syotoku_rate)) + 2000
```


# 所得税と住民税の控除を導出する

各種控除が所得税と住民税で異なるというのが闇である。
また住民税は居住地によって微妙に異なるのも地獄。
まるでバージョンアップでどうにもならなくなったソフトウェアのよう。

## 給与所得控除

```python
# 総支給から合計所得金額を求める
def get_total_income(salary, is_syotoku):
    kojo = 0
    # 所得税または660万円未満の住民税の場合
    if is_syotoku or salary <= 6600000:
        if salary <= 550000:
            kojo = 550000
        elif salary <= 1800000:
            kojo =  0.4 * salary - 100000
        elif salary <= 3600000:
            kojo =  0.3 * salary + 80000
        elif salary <= 6600000:
            kojo =  0.2 * salary + 440000
        elif salary <= 8500000:
            kojo =  0.1 * salary + 1100000
        else:
            kojo =  1950000
    # 660万円以上で住民税の場合
    elif salary <= 10000000:
        kojo = 0.1 * salary + 1200000
    else:
        kojo = 2200000
    return salary - kojo
```

## 基礎控除

給与所得控除の合算額が2020年に改悪された。

* 所得税対象から48万円、住民税対象から43万円は基本的人権として除いて計算させてやる。
* 高額所得者に人権はない。

```python
def calc_basic_subsidy(salary, is_syotoku):
    if is_syotoku:
        if salary <= 24000000:
            return 480000
        if salary <= 24500000:
            return 320000
        if salary <= 25000000:
            return 160000
    if salary <= 24000000:
        return 430000
    if salary <= 24500000:
        return 290000
    if salary <= 25000000:
        return 150000
    return 0
```

## 生命保険料等控除

リファクタしなかったソースコードの悲鳴みたいな仕様になってる。

* 旧制度は一般・年金の2分類で、所得税なら最大5万円ずつ控除してやんよ。
* 新制度は一般・年金・介護医療の3分類で4万円ずつや。最大控除2万円増えたよ。よかったな。

```python
def calc_insurance_subsidy(old_normal, old_pension, new_normal, new_pension, new_care, is_syotoku):
    def old_syotoku(hokenryo):
        if hokenryo <= 25000:
            return hokenryo
        if hokenryo <= 50000:
            return hokenryo / 2 + 12500
        if hokenryo <= 100000:
            return hokenryo / 4 + 25000
        return 50000
    def old_jumin(hokenryo):
        if hokenryo <= 15000:
            return hokenryo
        if hokenryo <= 40000:
            return hokenryo / 2 + 7500
        if hokenryo <= 70000:
            return hokenryo / 4 + 17500
        return 35000
    def new_syotoku(hokenryo):
        if hokenryo <= 20000:
            return hokenryo
        if hokenryo <= 40000:
            return hokenryo / 2 + 10000
        if hokenryo <= 80000:
            return hokenryo / 4 + 20000
        return 40000
    def new_jumin(hokenryo):
        if hokenryo <= 12000:
            return hokenryo
        if hokenryo <= 32000:
            return hokenryo / 2 + 6000
        if hokenryo <= 56000:
            return hokenryo / 4 + 14000
        return 28000
    if is_syotoku:
        normal = old_syotoku(old_normal)
        if normal < 40000 and new_normal > 0:
            normal = new_syotoku(old_normal + new_normal)
        pension = old_syotoku(old_pension)
        if pension < 40000 and new_pension > 0:
            pension = new_syotoku(old_pension + new_pension)
        care = new_syotoku(new_care)
        r = normal + pension + care
        if r > 120000:
            r = 120000
        return r
    normal = old_jumin(old_normal)
    if normal < 28000 and new_normal > 0:
        normal = new_jumin(old_normal + new_normal)
    pension = old_jumin(old_pension)
    if pension < 28000 and new_pension > 0:
        pension = new_jumin(old_pension + new_pension)
    care = new_jumin(new_care)
    r = normal + pension + care
    if r > 70000:
        r = 70000
    return r
```

## 地震保険料控除

コードの短さが仕様の簡潔さを表している。
すべてこういう仕様ならまだいいのだが。

```python
def calc_earthquake_subsidy(payment, is_syotoku):
    p = 50000 if payment > 50000 else payment
    return p if is_syotoku else p / 2
```

## 小規模企業共済等掛金控除

* 全額を所得税と住民税から控除するから投資しろよ愚民ども。
* 受取時に退職金枠として食い潰す、とはいえ素で受け取るよりかは退職金の所得税は優遇されている。

## 配偶者(特別)控除

* partnerがニートあるいは収入が少ない場合は税金負けたるで。
* partnerが老人の場合はさらに割増やけどここでは省略や。

```python
def calc_partner_subsidy(salary, has_partner, partner_salary, is_syotoku):
    total_income = get_total_income(salary, is_syotoku)
    partner_income = get_total_income(partner_salary, is_syotoku)
    if not has_partner or total_income > 10000000:
        return 0
    if partner_income <= 950000:
        if total_income <= 9000000:
            return 380000 if is_syotoku else 330000
        if total_income <= 9500000:
            return 260000 if is_syotoku else 220000
        return 130000 if is_syotoku else 110000
    if partner_income <= 1000000:
        if total_income <= 9000000:
            return 360000 if is_syotoku else 330000
        if total_income <= 9500000:
            return 240000 if is_syotoku else 220000
        return 120000 if is_syotoku else 110000
    if partner_income <= 1050000:
        if total_income <= 9000000:
            return 310000
        if total_income <= 9500000:
            return 210000
        return 110000
    if partner_income <= 1100000:
        if total_income <= 9000000:
            return 260000
        if total_income <= 9500000:
            return 180000
        return 90000
    if partner_income <= 1150000:
        if total_income <= 9000000:
            return 210000
        if total_income <= 9500000:
            return 240000
        return 70000
    if partner_income <= 1200000:
        if total_income <= 9000000:
            return 160000
        if total_income <= 9500000:
            return 110000
        return 60000
    if partner_income <= 1250000:
        if total_income <= 9000000:
            return 110000
        if total_income <= 9500000:
            return 80000
        return 40000
    if partner_income <= 1300000:
        if total_income <= 9000000:
            return 60000
        if total_income <= 9500000:
            return 40000
        return 20000
    if partner_income <= 1330000:
        if total_income <= 9000000:
            return 30000
        if total_income <= 9500000:
            return 20000
        return 10000
    return 0
```

## 扶養控除

* 15歳以下は子ども手当にするから扶養控除対象からは外すわ。
* あっ、高所得者は子ども手当の対象外やけどメンゴメンゴ。
* 16～18歳、23～69歳は一般扶養な。
* 70歳以上は老人扶養でやや多め。
* 70歳以上かつ同居かつ直系尊属なら同居老親等でさらに多め。
* 大学生ぐらいの19～22歳は特定扶養控除で最も高額の控除したる。

```python
def calc_support_subsidy(normal, student, old, old_special, is_syotoku):
    if is_syotoku:
        return (38 * normal + 63 * student + 48 * old + 58 * old_special) * 10000
    return (33 * normal + 45 * student + 38 * old + 45 * old_special) * 10000
```

## 健康保険料(賞与)

* 「会社が○○は半分負担してくれています」は天引きを少なくみせるための方便。
* 標準賞与額は総支給額の1000円未満切り捨て
* 標準賞与額上限は5730000円
* 厚生年金に対する標準賞与額の上限は150万＊3回までなのでここでの上限は150万*2回の支給を上限とする

```python
def calc_health_insurance_bonus(bonus):
    KOSEI_HOKEN_PERCENT = 0.183  # 厚生年金保険料
    KENKO_HOKEN_PERCENT = 0.1006  # 健康保険料率
    KAIGO_HOKEN_PERCENT = 0.00895  # 介護保険料率
    KOYO_HOKEN_PERCENT = 0.003  # 雇用保険料率
    basic_bonus = bonus - bonus % 1000
    s_0 = 3000000 if basic_bonus > 3000000 else basic_bonus
    s_1 = 5730000 if basic_bonus > 5730000 else basic_bonus
    kosei = s_0 * KOSEI_HOKEN_PERCENT * 0.5
    kenko = s_1 * KENKO_HOKEN_PERCENT * 0.5
    kaigo = s_1 * KAIGO_HOKEN_PERCENT
    koyo = s_1 * KOYO_HOKEN_PERCENT
    return s_1, kenko, kaigo, kosei, koyo
```

## 健康保険料(月額)

鬼のようなテーブル。
昔はこれが徐々に値上げされていくという悪夢。
今は据え置きになっているがどうだか。

```python
def calc_health_insurance_month(month_money):
    KOYO_HOKEN_PERCENT = 0.003 # 雇用保険料率
    koyo = month_money * KOYO_HOKEN_PERCENT
    if month_money < 63000:
        return 58000, 2917, 522, 8052, koyo
    if month_money < 73000:
        return 68000, 3420, 612, 8052, koyo
    if month_money < 83000:
        return 78000, 3923, 702, 8052, koyo
    if month_money < 93000:
        return 88000, 4426, 792, 8052, koyo
    if month_money < 101000:
        return 98000, 4929, 882, 8967, koyo
    if month_money < 107000:
        return 104000, 5231, 936, 9516, koyo
    if month_money < 114000:
        return 110000, 5533, 990, 10065, koyo
    if month_money < 122000:
        return 118000, 5935, 1062, 10797, koyo
    if month_money < 130000:
        return 126000, 6338, 1134, 11529, koyo
    if month_money < 138000:
        return 134000, 6740, 1206, 12261, koyo
    if month_money < 146000:
        return 142000, 7143, 1278, 12993, koyo
    if month_money < 155000:
        return 150000, 7545, 1350, 13725, koyo
    if month_money < 165000:
        return 160000, 8048, 1440, 14640, koyo
    if month_money < 175000:
        return 170000, 8551, 1530, 15555, koyo
    if month_money < 185000:
        return 180000, 9054, 1620, 16470, koyo
    if month_money < 195000:
        return 190000, 9557, 1710, 17385, koyo
    if month_money < 210000:
        return 200000, 10060, 1800, 18300, koyo
    if month_money < 230000:
        return 220000, 11066, 1980, 20130, koyo
    if month_money < 250000:
        return 240000, 12072, 2160, 21960, koyo
    if month_money < 270000:
        return 260000, 13078, 2340, 23790, koyo
    if month_money < 290000:
        return 280000, 14084, 2520, 25620, koyo
    if month_money < 310000:
        return 300000, 15090, 2700, 27450, koyo
    if month_money < 330000:
        return 320000, 16096, 2880, 29280, koyo
    if month_money < 350000:
        return 340000, 17102, 3060, 31110, koyo
    if month_money < 370000:
        return 360000, 18108, 3240, 32940, koyo
    if month_money < 395000:
        return 380000, 19114, 3420, 34770, koyo
    if month_money < 425000:
        return 410000, 20623, 3690, 37515, koyo
    if month_money < 455000:
        return 440000, 22132, 3960, 40260, koyo
    if month_money < 485000:
        return 470000, 23641, 4230, 43005, koyo
    if month_money < 515000:
        return 500000, 25150, 4500, 45750, koyo
    if month_money < 545000:
        return 530000, 26659, 4770, 48495, koyo
    if month_money < 575000:
        return 560000, 28168, 5040, 51240, koyo
    if month_money < 605000:
        return 590000, 29677, 5310, 53985, koyo
    if month_money < 635000:
        return 620000, 31186, 5580, 56730, koyo
    if month_money < 665000:
        return 650000, 32695, 5850, 59475, koyo
    if month_money < 695000:
        return 680000, 34204, 6120, 59475, koyo
    if month_money < 730000:
        return 710000, 35713, 6390, 59475, koyo
    if month_money < 770000:
        return 750000, 37725, 6750, 59475, koyo
    if month_money < 810000:
        return 790000, 39737, 7110, 59475, koyo
    if month_money < 855000:
        return 830000, 41749, 7470, 59475, koyo
    if month_money < 905000:
        return 880000, 44264, 7920, 59475, koyo
    if month_money < 955000:
        return 930000, 46779, 8370, 59475, koyo
    if month_money < 1005000:
        return 980000, 49294, 8820, 59475, koyo
    if month_money < 1050000:
        return 1030000, 51809, 9270, 59475, koyo
    if month_money < 1115000:
        return 1090000, 54827, 9810, 59475, koyo
    if month_money < 1175000:
        return 1150000, 57845, 10350, 59475, koyo
    if month_money < 1235000:
        return 1210000, 60863, 10890, 59475, koyo
    if month_money < 1295000:
        return 1270000, 63881, 11430, 59475, koyo
    if month_money < 1355000:
        return 1330000, 66899, 11970, 59475, koyo
    return 1390000, 69917, 12510, 59475, koyo
```

# 所得税を導出する

## 課税所得金額(所得税)を導出する

```python
def get_kazei_syotoku_income(salary, is_kaigo, ideco, has_partner, partner_salary, old_normal, old_pension, new_normal, new_pension, new_care, earthquake_payment, normal, student, old, old_special):
    total_income = get_total_income(salary, True)
    basic = calc_basic_subsidy(total_income, True)
    insurance = calc_insurance_subsidy(old_normal, old_pension, new_normal, new_pension, new_care, True)
    earthquake = calc_earthquake_subsidy(earthquake_payment, True)
    partner = calc_partner_subsidy(salary, has_partner, partner_salary, True)
    fuyou = calc_support_subsidy(normal, student, old, old_special, True)
    # 月収を1/16年収としてbonus2ヶ月2回とする
    month = salary / 16
    bonus = month * 4
    standard_b, kenko_b, kaigo_b, kosei_b, koyo_b = calc_health_insurance_bonus(bonus)
    standard_m, kenko_m, kaigo_m, kosei_m, koyo_m = calc_health_insurance_month(month)
    kenko = kenko_b + kosei_b + koyo_b + (kenko_m + kosei_m + koyo_m) * 12
    if is_kaigo:
        kenko += (kaigo_b + kaigo_m * 12)
    return total_income - basic - ideco - insurance - earthquake - partner - fuyou - kenko
```

## 所得税率を導出する

```python
# 課税所得金額から所得税率を導出する
def get_syotoku_rating(kazei_syotoku_income, is_fukkou):
    f = 1.021 if is_fukkou else 1  # 復興特別所得税
    if kazei_syotoku_income < 1950000:
        return 0.05 * f
    if kazei_syotoku_income < 3300000:
        return 0.1 * f
    if kazei_syotoku_income < 6950000:
        return 0.2 * f
    if kazei_syotoku_income < 9900000:
        return 0.23 * f
    if kazei_syotoku_income < 18000000:
        return 0.33 * f
    if kazei_syotoku_income < 40000000:
        return 0.4 * f
    return 0.44 * f
```

## 所得税の導出

```python
def calc_syotoku_zei(salary, is_kaigo, ideco, has_partner, partner_salary, old_normal, old_pension, new_normal, new_pension, new_care, earthquake_payment, normal, student, old, old_special, is_fukkou):
    kazei_syotoku_income = get_kazei_syotoku_income(salary, is_kaigo, ideco, has_partner, partner_salary, old_normal, old_pension, new_normal, new_pension, new_care, earthquake_payment, normal, student, old, old_special)
    r = get_syotoku_rating(kazei_syotoku_income, is_fukkou)
    if kazei_syotoku_income < 1950000:
        return kazei_syotoku_income * r
    if kazei_syotoku_income < 3300000:
        return kazei_syotoku_income * r - 97500
    if kazei_syotoku_income < 6950000:
        return kazei_syotoku_income * r - 427500
    if kazei_syotoku_income < 9900000:
        return kazei_syotoku_income * r - 636000
    if kazei_syotoku_income < 18000000:
        return kazei_syotoku_income * r - 1536000
    if kazei_syotoku_income < 40000000:
        return kazei_syotoku_income * r - 2796000
    return kazei_syotoku_income * r - 4796000
```

# 住民税を導出する

## 均等割

```python
def get_jumin_kintowari():
    return 5600
```

## 所得割

### 課税所得金額(住民税)を導出する

```python
def get_kazei_jumin_income(salary, is_kaigo, ideco, has_partner, partner_salary, old_normal, old_pension, new_normal, new_pension, new_care, earthquake_payment, normal, student, old, old_special):
    total_income = get_total_income(salary, False)
    basic = calc_basic_subsidy(total_income, False)
    insurance = calc_insurance_subsidy(old_normal, old_pension, new_normal, new_pension, new_care, False)
    earthquake = calc_earthquake_subsidy(earthquake_payment, False)
    partner = calc_partner_subsidy(salary, has_partner, partner_salary, False)
    fuyou = calc_support_subsidy(normal, student, old, old_special, False)
    # 月収を1/16年収としてbonus2ヶ月2回とする
    month = salary / 16
    bonus = month * 4
    standard_b, kenko_b, kaigo_b, kosei_b, koyo_b = calc_health_insurance_bonus(bonus)
    standard_m, kenko_m, kaigo_m, kosei_m, koyo_m = calc_health_insurance_month(month)
    kenko = kenko_b + kosei_b + koyo_b + (kenko_m + kosei_m + koyo_m) * 12
    if is_kaigo:
        kenko += (kaigo_b + kaigo_m * 12)
    return total_income - basic - ideco - insurance - earthquake - partner - fuyou - kenko
```

### 調整控除前の所得割の導出

```python
def get_jumin_syotokuwari_base(kazei_jumin_income):
    return 0.1 * kazei_jumin_income
```

### 調整控除額の導出

人的控除額の差とは所得税と住民税の控除の差のうち人間関係部分のことです。
なんじゃそりゃ。そんな穴掘って埋めるみたいな計算するとかアホかよ。
あと高所得者は畜生以下の扱いじゃなかったっけ。

* (合計課税所得金額が200万円以下) 人的控除額の差の合計又は合計課税所得金額のいずれか少ない金額の5％
* (合計課税所得金額が200万円超)｛人的控除額の差の合計-（合計課税所得金額-200万円）｝の5％ 5万円を下回る場合には、5万円

```python
def get_jumin_syotokuwari_adjust(salary, is_kaigo, ideco, has_partner, partner_salary, old_normal, old_pension, new_normal, new_pension, new_care, earthquake_payment, normal, student, old, old_special):
    kazei = get_kazei_jumin_income(salary, is_kaigo, ideco, has_partner, partner_salary, old_normal, old_pension, new_normal, new_pension, new_care, earthquake_payment, normal, student, old, old_special)
    t_s = get_total_income(salary, True)
    b_s = calc_basic_subsidy(t_s, True)
    p_s = calc_partner_subsidy(t_s, has_partner, partner_salary, True)
    f_s = calc_support_subsidy(normal, student, old, old_special, True)
    t_j = get_total_income(salary, False)
    b_j = calc_basic_subsidy(t_j, False)
    p_j = calc_partner_subsidy(t_j, has_partner, partner_salary, False)
    f_j = calc_support_subsidy(normal, student, old, old_special, False)
    syotoku_kojo = b_s + p_s + f_s
    jumin_kojo = b_j + p_j + f_j
    jinteki = syotoku_kojo + jumin_kojo
    if kazei <= 2000000:
        r = jinteki if jinteki < kazei else kazei
        return r * 0.05
    r = jinteki - (kazei - 2000000)
    r *= 0.05
    return 50000 if r < 50000 else r
```

### 調整控除後の所得割の導出

```python
def get_jumin_syotokuwari(salary, is_kaigo, ideco, has_partner, partner_salary, old_normal, old_pension, new_normal, new_pension, new_care, earthquake_payment, normal, student, old, old_special):
    kazei = get_kazei_jumin_income(salary, is_kaigo, ideco, has_partner, partner_salary, old_normal, old_pension, new_normal, new_pension, new_care, earthquake_payment, normal, student, old, old_special)
    adjust = get_jumin_syotokuwari_adjust(salary, is_kaigo, ideco, has_partner, partner_salary, old_normal, old_pension, new_normal, new_pension, new_care, earthquake_payment, normal, student, old, old_special)
    return get_jumin_syotokuwari_base(kazei - adjust)
```

## 住民税の導出

```python
def calc_jumin_zei(salary, is_kaigo, ideco, has_partner, partner_salary, old_normal, old_pension, new_normal, new_pension, new_care, earthquake_payment, normal, student, old, old_special):
    return get_jumin_kintowari() + get_jumin_syotokuwari(salary, is_kaigo, ideco, has_partner, partner_salary, old_normal, old_pension, new_normal, new_pension, new_care, earthquake_payment, normal, student, old, old_special)
```


# ふるさと納税上限を導出する

```python
is_fukkou = True  # 復興特別所得税を加えるか
salary = 7000000  # 総支給額
is_kaigo = False  # 介護保険支払い対象か (40歳以上か)
ideco = 0  # iDeco年間搬出額
has_partner = True  # 配偶者が存在するか
partner_salary = 5000000  # 配偶者の総支給額
old_normal = 0  # 旧制度：一般の生命保険料支払額
old_pension= 0  # 旧制度：個人年金の保険料支払額
new_normal = 0  # 新制度：一般の生命保険料支払額
new_pension = 0  # 新制度：個人年金の保険料支払額
new_care = 0  # 新制度：介護医療の保険料支払額
earthquake_payment = 0  # 地震保険料の支払額
normal = 0  # 一般の扶養人数
student = 0  # 特定の扶養人数
old = 0  # 老人の扶養人数
old_special = 0  # 同居老親等の扶養人数

kazei_syotoku_income = get_kazei_syotoku_income(salary, is_kaigo, ideco, has_partner, partner_salary, old_normal, old_pension, new_normal, new_pension, new_care, earthquake_payment, normal, student, old, old_special)
syotoku_rate = get_syotoku_rating(kazei_syotoku_income, is_fukkou)
jumin_syotoku = get_jumin_syotokuwari(salary, is_kaigo, ideco, has_partner, partner_salary, old_normal, old_pension, new_normal, new_pension, new_care, earthquake_payment, normal, student, old, old_special)
calc_furusato_limit(jumin_syotoku, syotoku_rate)
```

計算結果はこうなった。
105539.69531474562

他にも障害者控除とか、
所得税・住民税にダイレクトアタックできる住宅ローン減税とかある。
ややこしすぎだろ。