契約の状態を日付から計算する
目標
契約の状態を保存されたカラムから読まず、払込履歴と基準日から直接計算できるようになります。また、同じクエリが基準日と認知日によって異なる答えを出す理由を、データで証明できるようになります。
なぜ重要なのか
保険会社で「失効処理がされていない」という問い合わせを受けると、まずログを見ることになります。しかし、その原因はたいていログにありません。statusカラムは昨夜のバッチが計算して入れた意見であり、事実は払込履歴にあります。2つがずれた場所が、そのまま問い合わせが入る場所です。
そして失効はイベントではなく計算結果です。どの表にも「失効した」という記録はなく、未払込の回次と猶予ルールから失効日が出てきます。このラボで使うルールは次のとおりです。
납입기일 해당월 10일
유예 만료일 납입 해당월의 다음 달 말일 (2026-02 회차 → 2026-03-31)
실효일 유예 만료일 다음 날 (2026-04-01)
これに2つの優先順位が付きます。解約・満期イベントが未払込の計算より先です。すでに終わった契約に失効を問いません。そして復活後の回次だけを失効判定に入れます。復活は滞納分を払い込んだという意味ではなく、復活申込と審査をやり直したという意味なので、それより前の未払込はもう契約を切りません。
最後に、時間軸がもう1つあります。7月5日に入った収納訂正は6月30日の状態を変えます。基準日が同じでも、いつ知った事実で計算したかによって答えが変わります。
ステップ
- 右の例にあるジェネレーターを
/root/ins/make_policy.pyとして保存し、python3 make_policy.pyで実行してください。契約40件、払込480回次、契約イベント7件、収納訂正3件が作られます。 - 未払込の回次がある契約ごとに、最初の未払込回次と失効日を計算し、
/root/ins/lapse_calc.csvにpolicy_no,first_unpaid_ym,due_date,grace_end,lapse_dateのヘッダー行を付けて書いてください。 - 基準日
2026-06-30の契約状態を計算し、/root/ins/status_20260630.csvにpolicy_no,calc_statusとして書いてください。契約40件がすべて入っている必要があります。 policy.statusとステップ3の計算結果を突き合わせ、ずれている契約だけを/root/ins/mismatch.csvにpolicy_no,stored_status,calc_statusとして書いてください。- 同じクエリを基準日
2026-09-30でも実行し、/root/ins/drift.txtにasof_20260630_normal=、asof_20260630_lapsed=、asof_20260930_normal=、asof_20260930_lapsed=、moved=の5行を書いてください。 - 基準日を
2026-06-30に固定したまま、認知日2026-06-30と2026-08-31でそれぞれ計算し、/root/ins/restate.txtにknown_20260630_lapsed=、known_20260831_lapsed=、restated=、restated_policies=の4行を書いてください。 policy.dbの中にstatus_snapshot(as_of_date, known_as_of, policy_no, calc_status)テーブルを作り、ステップ6の2つの結果をそれぞれ40行ずつ、合わせて80行入れてください。/root/ins/policy_report.mdに、## 확인한 것、## 상태가 어긋난 계약、## 기준일과 인지일、## 권고の4つの節で報告書を書いてください(見出しは順に、韓国語で「確認したこと」「状態がずれた契約」「基準日と認知日」「勧告」を意味する語です)。
参考
sqlite3 -readonly <파일> "<쿼리>"で読むと、誤って書き込むことを防げます(プレースホルダーはファイル名とクエリです)。- 月末は
date('2026-02-01','+2 months','-1 day')で求めます。月ごとに末日が違うので、手で足さないでください。 - よくある間違い1:
.mode csvで出力すると、韓国語の値に二重引用符が付きます。.mode listと.separator ,を使うときれいに出ます(採点ツールはどちらも受け付けます)。 - よくある間違い2: 猶予満了日の当日を失効として数えてしまうことです。
2026-06-30の基準日に猶予満了日が2026-06-30の契約は、まだ正常です。 - よくある間違い3: 復活前の未払込まで失効判定に入れてしまうことです。そうすると、復活した契約3件がすべて失効として出ます。
- よくある間違い4: ステップ8で、数字を目で見て手で書き写してしまうことです。手で書き写した数字は、必ず1桁間違います。
契約データを作る
右の例にあるジェネレーターを/root/ins/make_policy.pyとして保存し、python3 make_policy.pyで実行してください。契約40件、払込480回次、契約イベント7件、収納訂正3件が作られます。
ラボの案内にあるジェネレーターを/root/ins/make_policy.pyとして保存し、python3で実行してください。手で直すと、後のステップの数字がすべてずれます。
猶予期間と失効日を計算する
未払込の回次がある契約ごとに、最初の未払込回次と失効日を計算し、/root/ins/lapse_calc.csvにpolicy_no,first_unpaid_ym,due_date,grace_end,lapse_dateのヘッダー行を付けて書いてください。
未払込の回次は、paid_dateがNULLの行です。猶予満了日は払込該当月の翌月末日で、失効日はその翌日です。sqlite3のdate()関数に'+2 months'と'-1 day'を付けてみてください。
基準日の契約状態を計算する
基準日2026-06-30の契約状態を計算し、/root/ins/status_20260630.csvにpolicy_no,calc_statusとして書いてください。契約40件がすべて入っている必要があります。
優先順位があります。解約・満期などの終了イベントが先で、その次が未払込による失効です。そして、復活後の回次だけを失効判定に入れる必要があります。
状態カラムとずれた契約を探す
policy.statusとステップ3の計算結果を突き合わせ、ずれている契約だけを/root/ins/mismatch.csvにpolicy_no,stored_status,calc_statusとして書いてください。
policy.statusと前のステップで計算した状態を契約番号で突き合わせ、違うものだけを残してください。ずれの方向が1つではない点に注意してください。
基準日を動かして数字が分かれるのを見る
同じクエリを基準日2026-09-30でも実行し、/root/ins/drift.txtにasof_20260630_normal=、asof_20260630_lapsed=、asof_20260930_normal=、asof_20260930_lapsed=、moved=の5行を書いてください。
ステップ3で作ったクエリをそのまま使い、日付だけを変えてください。クエリを新しく書くとその間に誤字が潜り、誤字はいつも実行される側にあります。
認知日を変えて遡及訂正を浮かび上がらせる
基準日を2026-06-30に固定したまま、認知日2026-06-30と2026-08-31でそれぞれ計算し、/root/ins/restate.txtにknown_20260630_lapsed=、known_20260831_lapsed=、restated=、restated_policies=の4行を書いてください。
基準日は2026-06-30に固定します。認知日以降に入った訂正は、そのときは知らなかった事実なので、payment_correctionのold_paid_dateに戻して計算する必要があります。
再現できるスナップショットに固める
policy.dbの中にstatus_snapshot(as_of_date, known_as_of, policy_no, calc_status)テーブルを作り、ステップ6の2つの結果をそれぞれ40行ずつ、合わせて80行入れてください。
クエリを保存しても再現できません。計算結果を基準日・認知日と一緒にテーブルに入れてください。sqlite3の.importでCSVを表に取り込めます。
担当者が読む報告書を書く
/root/ins/policy_report.mdに、## 확인한 것、## 상태가 어긋난 계약、## 기준일과 인지일、## 권고の4つの節で報告書を書いてください(見出しは順に、韓国語で「確認したこと」「状態がずれた契約」「基準日と認知日」「勧告」を意味する語です)。
4つの節が必要です。数字は手で写さず、前のステップが残したファイルから読んで埋めてください。そして、このデータには被保険者の識別子が含まれています。