Rubyで国民健康保険料を計算する

Okinawa.rb Advent Calendar 2017 1日目です。
https://qiita.com/advent-calendar/2017/okinawarb

沖縄県の各市町村の国民健康保険料の金額をRubyで計算します。

## 国民健康保険税

日本は国民皆保険制度なので国民全員がいずれかの公的医療保険制度に加入しています。[^1]

市町村国保とは、他の医療保険に加入していない住民を被保険者とする国民皆保険制度の基礎です。[^2]

国民健康保険料は国民健康保険税という税金として納める事が多いです。詳しくはWikipediaの国民健康保険税の記事[^3]や、freeeさんの国民健康保険料の計算方法をわかりやすく解説|知っておきたい税の基本[^4]を読んで下さい。

## 沖縄県の国民健康保険料

市町村が運営しているので自治体によって金額が違います。赤字なので保険料が高いです :up: 

> 国保、沖縄県内10市町村で保険料上昇予測　県は徴収料統一検討
http://www.okinawatimes.co.jp/articles/-/133399

> 沖縄戦の影響で全国と比べ前期高齢者（６５～７４歳）の割合が低く、前期高齢者の割合を基に算出される交付金が低いことが、国保財政の悪化につながっている。
https://ryukyushimpo.jp/news/entry-435651.html

## 計算方法

那覇市を例にします。以下の3つの区分の合算です。

> 保険税の年税額は、医療分と支援分と介護分（40歳～64歳の方）の合算額です。医療分と支援分と介護分はそれぞれ別々に計算されます。
http://www.city.naha.okinawa.jp/kakuka/kokuhotyoujyu/hokenzeinoufu.html

65歳からは介護分は掛かりませんが、介護保険料が別途掛かります(また、75歳からは国保ではなく後期高齢者医療制度に加入することになります)。

那覇市の場合はそれぞれの分について以下の3つを計算していきます。

- 所得割: 所得×税率
- 均等割: 加入者数×◯円
- 平等割: 世帯あたり◯円

自治体によっては所得割、均等割、平等割の他に資産割があったりします。その場合固定資産税×税率を計算します。

## 実装

### 計算機の初期化

国保は世帯ごとに請求されるので計算機の引数で国保に加入する世帯の構成員を取ります。詳しくは擬制世帯でググってください。

```rb
class JapaneseNationalHealthInsuranceCalculator
  def initialize(household)
    @household = household
  end
end
```

初期化する際に引数で渡す世帯の構成員を定義してみましょう。
国保税を計算するためには以下の情報が取れる必要があります。

- 介護分の計算: 年齢
- 所得割の計算: 所得
- 資産割の計算: 固定資産税額

それに加え、介護分が必要な年齢か調べるメソッドも追加しましょう

```rb
class HouseholdMember < Struct.new(:age, :income, :fixed_assets_tax)
  def long_term_care_insurance_premium?
    (40...65).cover?(age)
  end
end
```

これで以下のように世帯を渡して国民健康保険料の計算機を初期化できるようになりました。

```rb
household = [
  HouseholdMember.new(27, 2472120, 0),
  HouseholdMember.new(29, 0, 0),
  HouseholdMember.new(0, 0, 0)
]
calculator = JapaneseNationalHealthInsuranceCalculator.new(household)
```

### 国民健康保険料の計算

国民健康保険料の計算について、以下3つの金額の合計なので定義は簡単です。

- 医療分
- 支援分
- 介護分

```rb
class JapaneseNationalHealthInsuranceCalculator
  def calculate
    medical_insurance_premium + medical_care_assistance + long_term_care_insurance_premium
  end
end
```

問題は自治体によって、所得割・資産割の税率や均等割・平等割の金額が変わること、資産割がある/ないが変わることです。

- 所得割: 所得×税率
- 均等割: 加入者数×◯円
- 平等割: 世帯あたり◯円
- 資産割: 固定資産税×税率

### 部分ごとの計算機をつくる

以下の4種類の計算機を作りましょう。

- 所得割
- 均等割
- 平等割
- 資産割

それぞれの計算機に以下の3つを計算するAPIを実装します。

- 医療分
- 支援分
- 介護分

例えば所得割の場合、以下のように所得割の税率を引数で受け取るようにすると、自治体ごとに個別の計算機を作る必要がなくなるので便利でしょう。

```rb
class IncomeLevyCalculator < Struct.new(:household_member, :medical_insurance_premium_ratio, :medical_care_assistance_ratio, :long_term_care_insurance_premium_ratio)
  def medical_insurance_premium
    income * medical_insurance_premium_ratio
  end

  def medical_care_assistance
    income * medical_care_assistance_ratio
  end

  def long_term_care_insurance_premium
    if household_member.long_term_care_insurance_premium?
      income * long_term_care_insurance_ratio
    else
      0
    end
  end

  private

  def income
    [household_member.income - basic_deduction, 0].max
  end

  def basic_deduction
    330_000
  end
end
```

同じような感じで他の計算機も作ります。

```rb
# 均等割
class PerCapitaBasisLevyCalculator < Struct.new(:medical_insurance_premium, :medical_care_assistance, :long_term_care_insurance_premium)
end

# 平等割
class FlatLevyCalculator < Struct.new(:medical_insurance_premium, :medical_care_assistance, :long_term_care_insurance_premium)
end

# 資産割
class PropertyLevyCalculator
  def initialize(fixed_assets_tax, medical_insurance_premium_ratio, medical_care_assistance_ratio, long_term_care_insurance_premium_ratio)
    @fixed_assets_tax                       = fixed_assets_tax
    @medical_insurance_premium_ratio        = medical_insurance_premium_ratio
    @medical_care_assistance_ratio          = medical_care_assistance_ratio
    @long_term_care_insurance_premium_ratio = long_term_care_insurance_premium_ratio
  end

  def medical_insurance_premium
    @medical_insurance_premium_ratio * @fixed_assets_tax
  end

  def medical_care_assistance
    @medical_care_assistance_ratio * @fixed_assets_tax
  end

  def long_term_care_insurance_premium
    @long_term_care_insurance_ratio * @fixed_assets_tax
  end
end
```

### 計算機を組み合わせる

それぞれの市町村に必要な計算機を作ります。

例えば那覇市の場合

所得割

医療分 | 支援分 | 介護分
--- | --- | ---
9.70% | 1.59% | 1.56%

均等割

医療分 | 支援分 | 介護分
--- | --- | ---
18,200 | 3,300 | 7,700

平等割

 医療分 | 支援分 | 介護分
 --- | --- | ---
25,400 | 5,300 | 4,600

なので以下のような組み合わせです。

```rb
calculators = [
  household.map { |household_member| IncomeLevyCalculator.new(household_member, 970/10000r, 159/10000r, 156/10000r) },
  PerCapitaBasisLevyCalculator.new(household, 18_200, 3_300, 7_700),
  FlatLevyCalculator.new(household, 25_400, 5_300, 4_600)
].flatten
```

国保計算機にこれらの子計算機を入れられるようにします。

```rb
class JapaneseNationalHealthInsuranceCalculator
  attr_accessor :calculators
end

calculator.calculators = calculators
```

子計算機を使って計算するようメソッドを定義します。

```rb
class JapaneseNationalHealthInsuranceCalculator
  def medical_insurance_premium
    calculators.sum(&:medical_insurance_premium)
  end

  def medical_care_assistance
    calculators.sum(&:medical_care_assistance)
  end

  def long_term_care_insurance_premium
    calculators.sum(&:long_term_care_insurance_premium)
  end
end
```

結果

```rb
puts calculator.calculate.to_i
# 337045
```

無事国民健康保険料が計算できました!

## 沖縄県の各市町村で計算してみる

資産のことを気にしなければさっきの要領で子計算機を作る処理を使いまわせます。

ググったりして以下のようなTSVのデータを用意します。自治体によってはウェブに保険料が載っていないのでとりあえず市だけ調べました。

```rb
tsv = <<TSV
那覇市	9.70%	1.59%	1.56%	18200	3300	7700	25400	5300	4600				http://www.city.naha.okinawa.jp/kakuka/kokuhotyoujyu/hokenzeinoufu.html
浦添市	8.20%	2.40%	2.40%	19000	7500	9500	17000	6000	6000				http://www.city.urasoe.lg.jp/docs/2014110102709/
南城市	6.70%	2.60%	1.90%	16700	7000	5900	18200	7000	4300				http://www.city.nanjo.okinawa.jp/life/insurance/kokuho.html
沖縄市	9.12%	2.27%	1.86%	19712	5312	5686	20919	5637	3927				https://www.city.okinawa.okinawa.jp/kurashi/365/2286
豊見城市	8.44%	2.61%	2.10%	17200	6200	6600	23200	5400	3700				http://www.city.tomigusuku.okinawa.jp/userfiles/files/health/2016bookall.pdf
宜野湾市	6.63%	2.75%	1.62%	14300	5200	3900	19500	7100	5100				http://www.city.ginowan.okinawa.jp/life/health/02/37447.html
糸満市	9.00%	1.50%	2.20%	16000	3700	6900	20000	5000	4700	30.00%	6.50%	5.10%	http://www.city.itoman.lg.jp/docs/2013020102935/
うるま市	7.70%	2.50%	2.10%	14000	6500	7200	22000	6000	6000				http://www.city.uruma.lg.jp/kurashi/122/3149/492
宮古島市	8.35%	2.10%	2%	17500	4300	5000	15500	4000	3000	30%	7%	5.8	http://www.city.miyakojima.lg.jp/kurashi/kokumin/kenkouhoken/2017-0616-1306-81.html
石垣市	8.35%	2.20%	2.20%	18300	4300	6500	16600	5500	4000	19%	5%	4.20%	http://www.city.ishigaki.okinawa.jp/home/shiminhokenbu/kenkouhoken/index.htm#p2
名護市	6.30%	2.70%	1.20%	14500	5900	4300	12500	5100	3600	20.00%	9.00%	4.00%	http://www.city.nago.okinawa.jp/4/3835.html
TSV
```

おもむろにこねくり回す

```rb
puts "自治体 | 国保料"
puts "--- | ---"
tsv.lines.map { |l| l.chomp.split("\t") }.each do |row|
  local_gov = row[0]
  income_lavy_ratios = row[1..3].map {|percentage| percentage.delete('.%').to_i / 10000r }
  per_capita_basis = row[4..6].map(&:to_i)
  flat = row[7..9].map(&:to_i)

  calculator.calculators = [
    household.map { |household_member| IncomeLevyCalculator.new(household_member, *income_lavy_ratios) },
    PerCapitaBasisLevyCalculator.new(household, *per_capita_basis),
    FlatLevyCalculator.new(household, *flat)
  ].flatten
  tax = calculator.calculate.to_i
  puts "[#{local_gov}](#{row[13]}) | #{tax}"
end

```

はい

自治体 | 国保料
--- | ---
[那覇市](http://www.city.naha.okinawa.jp/kakuka/kokuhotyoujyu/hokenzeinoufu.html) | 337045
[浦添市](http://www.city.urasoe.lg.jp/docs/2014110102709/) | 329564
[南城市](http://www.city.nanjo.okinawa.jp/life/insurance/kokuho.html) | 295517
[沖縄市](https://www.city.okinawa.okinawa.jp/kurashi/365/2286) | 345615
[豊見城市](http://www.city.tomigusuku.okinawa.jp/userfiles/files/health/2016bookall.pdf) | 335504
[宜野湾市](http://www.city.ginowan.okinawa.jp/life/health/02/37447.html) | 286030
[糸満市](http://www.city.itoman.lg.jp/docs/2013020102935/) | 309022
[うるま市](http://www.city.uruma.lg.jp/kurashi/122/3149/492) | 307996
[宮古島市](http://www.city.miyakojima.lg.jp/kurashi/kokumin/kenkouhoken/2017-0616-1306-81.html) | 308751
[石垣市](http://www.city.ishigaki.okinawa.jp/home/shiminhokenbu/kenkouhoken/index.htm#p2) | 315893
[名護市](http://www.city.nago.okinawa.jp/4/3835.html) | 271590

## まとめ

- Rubyを使うと国保税が計算できてべんり
- 沖縄の国保税意外と高い

明日の記事は[あっとんさん](https://atton.github.io/)です。


## コード

tsvだけ変えれば別の自治体でも使えます。[^5] [^6]

[^5]: 固定資産がある人はcalculators設定まわり直して下さい
[^6]: 実際は10期に支払いが分かれていたり年度中に年齢が変わる場合やx円未満切り捨てなどでもっと計算が難しい

```rb
class JapaneseNationalHealthInsuranceCalculator
  attr_accessor :calculators

  def initialize(household)
    @household = household
  end

  def calculate
    medical_insurance_premium + medical_care_assistance + long_term_care_insurance_premium
  end

  def medical_insurance_premium
    calculators.sum(&:medical_insurance_premium)
  end

  def medical_care_assistance
    calculators.sum(&:medical_care_assistance)
  end

  def long_term_care_insurance_premium
    calculators.sum(&:long_term_care_insurance_premium)
  end
end

class HouseholdMember < Struct.new(:age, :income, :fixed_assets_tax)
  def long_term_care_insurance_premium?
    (40...65).cover?(age)
  end
end

class IncomeLevyCalculator < Struct.new(:household_member, :medical_insurance_premium_ratio, :medical_care_assistance_ratio, :long_term_care_insurance_premium_ratio)
  def medical_insurance_premium
    income * medical_insurance_premium_ratio
  end

  def medical_care_assistance
    income * medical_care_assistance_ratio
  end

  def long_term_care_insurance_premium
    if household_member.long_term_care_insurance_premium?
      income * long_term_care_insurance_ratio
    else
      0
    end
  end

  private

  def income
    [household_member.income - basic_deduction, 0].max
  end

  def basic_deduction
    330_000
  end
end

# 均等割
class PerCapitaBasisLevyCalculator < Struct.new(:household_members, :medical_insurance_premium, :medical_care_assistance, :long_term_care_insurance_premium)
  def medical_insurance_premium
    super * household_member_num
  end

  def medical_care_assistance
    super * household_member_num
  end

  def long_term_care_insurance_premium
    super * long_term_care_insurance_premium_member_num
  end

  private

  def household_member_num
    household_members.size
  end

  def long_term_care_insurance_premium_member_num
    household_members.select(&:long_term_care_insurance_premium?).size
  end
end

# 平等割
class FlatLevyCalculator < Struct.new(:household_members, :medical_insurance_premium, :medical_care_assistance, :long_term_care_insurance_premium)
  def long_term_care_insurance_premium
    if household_members.any?(&:long_term_care_insurance_premium?)
      super
    else
      0
    end
  end
end

# 資産割
class PropertyLevyCalculator < Struct.new(:household_members, :fixed_assets_tax, :medical_insurance_premium_ratio, :medical_care_assistance_ratio, :long_term_care_insurance_premium_ratio)
  def medical_insurance_premium
    @medical_insurance_premium_ratio * @fixed_assets_tax
  end

  def medical_care_assistance
    @medical_care_assistance_ratio * @fixed_assets_tax
  end

  def long_term_care_insurance_premium
    if household_members.any?(&:long_term_care_insurance_premium?)
      @long_term_care_insurance_ratio * @fixed_assets_tax
    else
      0
    end
  end
end

household = [
  HouseholdMember.new(27, 2472120, 0),
  HouseholdMember.new(29, 0, 0),
  HouseholdMember.new(0, 0, 0)
]
calculator = JapaneseNationalHealthInsuranceCalculator.new(household)

tsv = <<TSV
那覇市	9.70%	1.59%	1.56%	18200	3300	7700	25400	5300	4600				http://www.city.naha.okinawa.jp/kakuka/kokuhotyoujyu/hokenzeinoufu.html
浦添市	8.20%	2.40%	2.40%	19000	7500	9500	17000	6000	6000				http://www.city.urasoe.lg.jp/docs/2014110102709/
南城市	6.70%	2.60%	1.90%	16700	7000	5900	18200	7000	4300				http://www.city.nanjo.okinawa.jp/life/insurance/kokuho.html
沖縄市	9.12%	2.27%	1.86%	19712	5312	5686	20919	5637	3927				https://www.city.okinawa.okinawa.jp/kurashi/365/2286
豊見城市	8.44%	2.61%	2.10%	17200	6200	6600	23200	5400	3700				http://www.city.tomigusuku.okinawa.jp/userfiles/files/health/2016bookall.pdf
宜野湾市	6.63%	2.75%	1.62%	14300	5200	3900	19500	7100	5100				http://www.city.ginowan.okinawa.jp/life/health/02/37447.html
糸満市	9.00%	1.50%	2.20%	16000	3700	6900	20000	5000	4700	30.00%	6.50%	5.10%	http://www.city.itoman.lg.jp/docs/2013020102935/
うるま市	7.70%	2.50%	2.10%	14000	6500	7200	22000	6000	6000				http://www.city.uruma.lg.jp/kurashi/122/3149/492
宮古島市	8.35%	2.10%	2%	17500	4300	5000	15500	4000	3000	30%	7%	5.8	http://www.city.miyakojima.lg.jp/kurashi/kokumin/kenkouhoken/2017-0616-1306-81.html
石垣市	8.35%	2.20%	2.20%	18300	4300	6500	16600	5500	4000	19%	5%	4.20%	http://www.city.ishigaki.okinawa.jp/home/shiminhokenbu/kenkouhoken/index.htm#p2
名護市	6.30%	2.70%	1.20%	14500	5900	4300	12500	5100	3600	20.00%	9.00%	4.00%	http://www.city.nago.okinawa.jp/4/3835.html
TSV

puts "自治体 | 国保料"
puts "--- | ---"
tsv.lines.map { |l| l.chomp.split("\t") }.each do |row|
  local_gov = row[0]
  income_lavy_ratios = row[1..3].map {|percentage| percentage.delete('.%').to_i / 10000r }
  per_capita_basis = row[4..6].map(&:to_i)
  flat = row[7..9].map(&:to_i)

  calculator.calculators = [
    household.map { |household_member| IncomeLevyCalculator.new(household_member, *income_lavy_ratios) },
    PerCapitaBasisLevyCalculator.new(household, *per_capita_basis),
    FlatLevyCalculator.new(household, *flat)
  ].flatten
  tax = calculator.calculate.to_i
  puts "[#{local_gov}](#{row[13]}) | #{tax}"
end
```

## 脚注

[^1]: http://www.mhlw.go.jp/houdou_kouhou/kouhou_shuppan/pdf/2015-jimu_13.pdf
[^2]: http://www.mhlw.go.jp/stf/seisakunitsuite/bunya/kenkou_iryou/iryouhoken/iryouhoken01/index.html
[^3]: https://ja.wikipedia.org/wiki/%E5%9B%BD%E6%B0%91%E5%81%A5%E5%BA%B7%E4%BF%9D%E9%99%BA%E7%A8%8E

[^4]: https://keiei.freee.co.jp/2015/06/26/kokuho-keisan/