Haskell ochintin-daicho で年末調整プログラミング

12月といえば年末調整の時期です。
ということで、年末調整の事務処理をプログラミングできるように [ochintin-daicho](https://hackage.haskell.org/package/ochintin-daicho) というライブラリを作りました。

以前、[フリーランスプログラマが複式簿記プログラミング](https://qiita.com/arowM/items/69c0305a7ae5b953f21d)を行うライブラリとして [bookkeeping](https://hackage.haskell.org/package/bookkeeping)を作ったのですが、「さすがにこんなライブラリ需要ないだろう」と思っていたら、hackageのダウンロード数が意外に多くて「マジかよ」と思いました。

しかたないので、もっと誰も使わないライブラリを目指して、今回は法人の経理・労務事務をターゲットにします。

## お賃金台帳

企業はすべての労働者に対して、「おちんぎん台帳」と呼ばれる帳簿を記帳し保管することが義務付けられています。
年末調整の際にはこの帳簿の記録をもとに、支払った賃金や天引きした税額などを計算します。

しかし、年末調整額を計算する際に使用する源泉徴収簿や仕訳帳への記帳と重複するデータがたくさんあります。
「Single Source of Truth なんて知らないぜ」って感じで、日本の役所に提出する無駄しかない書類と通じるものを感じます。

## ochintin-daicho

そこで `ochintin-daicho` ライブラリの登場です。
このライブラリを使ってお賃金台帳を記帳すると、

* プレインテキストとしてお賃金台帳を出力
* 年末調整額の計算に必要な値の集計
* `bookkeeping` ライブラリがサポートする複式簿記形式の仕訳に変換

することができます。
法律上、お賃金台帳はデータとして保持しておいて、労働局などの求めに応じてすぐに印刷できる状態になっていればよいそうです。
`ochintin-daicho` でお賃金台帳を記録すると、法律上定められている項目がすべて含まれた形式でプレインテキストととして出力できます。

もちろん、「いつもお前らがつくったキモいフォーマットの書類を書いてやってるんだから、お前らもプログラムを読めよ」と言って、記帳内容が含まれたソースコードをそのまま印刷して渡すのも楽しいでしょう。

地味に嬉しい点として、ただのソースコードなのでお賃金台帳を git で気軽に管理できます。
弊社は立替経費の精算を `bookkeeping` ライブラリを使ったソースコードに対してプルリクを送るというフローをとっているため、
そのソースコードをそのままインポートするだけで、毎月の給与振込み額に対して自動的に経費の精算分が上乗せされて計算されます。

[Stackageにも反映](https://www.stackage.org/nightly-2017-12-14/package/ochintin-daicho-0.1.0.0)されているので、nightly で利用可能です。

### 記帳

こんな感じ。

```haskell
taroYamada :: Person
taroYamada = Person
  { name = "山田 太郎"
  , sex = "男"
  , payments = \y -> case y of
    2017 ->
      [ ( (1, 10)
        , Payment
          { _賃金計算期間 = (2016, 12)
          , _労働日数 = 13
          , _労働時間数 = 13 * 5
          , _休日労働時間数 = 0
          , _早出残業時間数 = 0
          , _深夜労働時間数 = 0
          , _課税支給額 = 250000
          , _控除社会保険料 = \debit amount -> do
            dateTrans debit
              (CreditCategory $ Category "預り金" Liabilities)
              "健康保険料(社員負担分)"
              $ if (200000 < amount && amount < 300000)
                then 15000
                else error "undefined 健康保険料"
            dateTrans debit
              (CreditCategory $ Category "預り金" Liabilities)
              "厚生年金(社員負担分)"
              $ if (200000 < amount && amount < 300000)
                then 20000
                else error "undefined 厚生年金"
            dateTrans debit
              (CreditCategory $ Category "預り金" Liabilities)
              "雇用保険料(社員負担分)"
              $ if (200000 < amount && amount < 300000)
                then 1000
                else error "undefined 雇用保険料"
          , _所得税額 = \debit amount ->
            dateTrans debit
              (CreditCategory $ Category "預り金" Liabilities)
              "源泉所得税"
              $ if (200000 < amount && amount < 250000)
                then 5000
                else error $ "undefined 所得税額 for " <> show amount
          , _非課税支給額 = \credit -> do
            dateTrans
              (DebitCategory $ Category "旅費・交通費" Expenses)
              credit
              "立替交通費"
              3000
          , _その他控除 = \debit ->
            dateTrans debit
              (CreditCategory $ Category "預り金" Liabilities)
              "親睦会費"
              5000
          }
        )
      , ( (2, 10)
        , Payment
          { _賃金計算期間 = (2017, 1)
          , _労働日数 = 12
          , _労働時間数 = 12 * 5
          , _休日労働時間数 = 0
          , _早出残業時間数 = 0
          , _深夜労働時間数 = 0
          , _課税支給額 = 250000
          , _控除社会保険料 = \debit amount -> do
            dateTrans debit
              (CreditCategory $ Category "預り金" Liabilities)
              "健康保険料(社員負担分)"
              $ if (200000 < amount && amount < 300000)
                then 15000
                else error "undefined 健康保険料"
            dateTrans debit
              (CreditCategory $ Category "預り金" Liabilities)
              "厚生年金(社員負担分)"
              $ if (200000 < amount && amount < 300000)
                then 20000
                else error "undefined 厚生年金"
            dateTrans debit
              (CreditCategory $ Category "預り金" Liabilities)
              "雇用保険料(社員負担分)"
              $ if (200000 < amount && amount < 300000)
                then 1000
                else error "undefined 雇用保険料"
          , _所得税額 = \debit amount ->
            dateTrans debit
              (CreditCategory $ Category "預り金" Liabilities)
              "源泉所得税"
              $ if (200000 < amount && amount < 250000)
                then 5000
                else error $ "undefined 所得税額2 for " <> show amount
          , _非課税支給額 = \credit -> do
            dateTrans
              (DebitCategory $ Category "旅費・交通費" Expenses)
              credit
              "立替交通費"
              4000
          , _その他控除 = \debit ->
            dateTrans debit
              (CreditCategory $ Category "預り金" Liabilities)
              "親睦会費"
              5000
          }
        )
      ]
    _ ->
      []
  }
```

### プレインテキストで出力

こんな感じ。

```haskell
>>> ppr taroYamada 2017
```
```
氏名: 山田 太郎
性別: 男

== 2017年支給分 ==

支給日: 1月10日
賃金計算期間: 2016年12月
労働日数: 13日
労働時間数: 65時間
休日労働時間数: 0時間
早出残業時間数: 0時間
深夜労働時間数: 0時間
課税支給額: 250000円
控除社会保険料: 36000円
社会保険料等控除の金額: 214000円
所得税額: 5000円
非課税支給額: 3000円
その他控除: 5000円
--> 実支払額: 207000円

支給日: 2月10日
賃金計算期間: 2017年1月
労働日数: 12日
労働時間数: 60時間
休日労働時間数: 0時間
早出残業時間数: 0時間
深夜労働時間数: 0時間
課税支給額: 250000円
控除社会保険料: 36000円
社会保険料等控除の金額: 214000円
所得税額: 5000円
非課税支給額: 4000円
その他控除: 5000円
--> 実支払額: 208000円

```

### 年末調整額の計算に必要な値の集計

こんな感じ

```haskell
-- 対象年度内の課税支給額総計

>>> _年度内課税支給金額 taroYamada 2017
Amount {unAmount = 500000}

-- 対象年度内の社会保険料等控除額総計

>>> _年度内社会保険控除額 taroYamada 2017
Amount {unAmount = 72000}

-- 対象年度内の所得税控除額総計

>>> _年度内所得税控除額 taroYamada 2017
Amount {unAmount = 10000}
```

### `bookkeeping` ライブラリがサポートする複式簿記形式の仕訳に変換

こんな感じ

```haskell
>>> Business.Bookkeeping.ppr $ toBookkeeping taroYamada 2017
  (DebitCategory $ Category "給与手当" Expenses)
  (CreditCategory $ Category "普通預金" Liabilities)
```
```
tDay: 2017-01-10
tDescription: 12月度
tSubDescription: 立替交通費
tDebit: 旅費・交通費 (Expenses)
tCredit: 普通預金 (Liabilities)
tAmount: 3000

tDay: 2017-01-10
tDescription: 12月度
tSubDescription: 健康保険料(社員負担分)
tDebit: 給与手当 (Expenses)
tCredit: 預り金 (Liabilities)
tAmount: 15000

tDay: 2017-01-10
tDescription: 12月度
tSubDescription: 厚生年金(社員負担分)
tDebit: 給与手当 (Expenses)
tCredit: 預り金 (Liabilities)
tAmount: 20000

tDay: 2017-01-10
tDescription: 12月度
tSubDescription: 雇用保険料(社員負担分)
tDebit: 給与手当 (Expenses)
tCredit: 預り金 (Liabilities)
tAmount: 1000

tDay: 2017-01-10
tDescription: 12月度
tSubDescription: 源泉所得税
tDebit: 給与手当 (Expenses)
tCredit: 預り金 (Liabilities)
tAmount: 5000

tDay: 2017-01-10
tDescription: 12月度
tSubDescription: 親睦会費
tDebit: 給与手当 (Expenses)
tCredit: 預り金 (Liabilities)
tAmount: 5000

tDay: 2017-01-10
tDescription: 12月度
tSubDescription: 給与支払い
tDebit: 給与手当 (Expenses)
tCredit: 普通預金 (Liabilities)
tAmount: 207000

tDay: 2017-02-10
tDescription: 1月度
tSubDescription: 立替交通費
tDebit: 旅費・交通費 (Expenses)
tCredit: 普通預金 (Liabilities)
tAmount: 4000

tDay: 2017-02-10
tDescription: 1月度
tSubDescription: 健康保険料(社員負担分)
tDebit: 給与手当 (Expenses)
tCredit: 預り金 (Liabilities)
tAmount: 15000

tDay: 2017-02-10
tDescription: 1月度
tSubDescription: 厚生年金(社員負担分)
tDebit: 給与手当 (Expenses)
tCredit: 預り金 (Liabilities)
tAmount: 20000

tDay: 2017-02-10
tDescription: 1月度
tSubDescription: 雇用保険料(社員負担分)
tDebit: 給与手当 (Expenses)
tCredit: 預り金 (Liabilities)
tAmount: 1000

tDay: 2017-02-10
tDescription: 1月度
tSubDescription: 源泉所得税
tDebit: 給与手当 (Expenses)
tCredit: 預り金 (Liabilities)
tAmount: 5000

tDay: 2017-02-10
tDescription: 1月度
tSubDescription: 親睦会費
tDebit: 給与手当 (Expenses)
tCredit: 預り金 (Liabilities)
tAmount: 5000

tDay: 2017-02-10
tDescription: 1月度
tSubDescription: 給与支払い
tDebit: 給与手当 (Expenses)
tCredit: 普通預金 (Liabilities)
tAmount: 208000

```

## Future Work

「法定三帳簿」の1つ「出勤簿」もお賃金台帳と重複するデータがいっぱいあるので、次のステップとしてこれもプログラムで管理してお賃金台帳と連携したいと思っています。

![P_20170720_171817_vHDR_Auto.jpg](https://qiita-image-store.s3.amazonaws.com/0/16345/f751d840-d143-031d-8b60-1f6395892f69.jpeg)