Computing Policy State From Dates
한국어 원문으로 표시합니다.
목표
계약의 상태를 저장된 컬럼에서 읽지 않고 납입 이력과 기준일에서 직접 계산할 수 있게 되고, 같은 질의가 기준일과 인지일에 따라 다른 답을 내는 이유를 데이터로 증명할 수 있게 됩니다.
왜 중요한가
보험사에서 "실효 처리가 안 됐다" 는 신고를 받으면 로그부터 보게 됩니다. 그런데 그 신고의 원인은 대개 로그에 없습니다. status 컬럼은 어젯밤 배치가 계산해 넣은 의견이고, 사실은 납입 이력에 있습니다. 둘이 갈라진 자리가 곧 신고가 들어오는 자리입니다.
그리고 실효는 이벤트가 아니라 계산 결과입니다. 어느 표에도 "실효했다" 는 기록이 없고, 미납 회차와 유예 규칙에서 실효일이 나옵니다. 이 실습에서 쓰는 규칙은 이렇습니다.
납입기일 해당월 10일
유예 만료일 납입 해당월의 다음 달 말일 (2026-02 회차 → 2026-03-31)
실효일 유예 만료일 다음 날 (2026-04-01)
여기에 두 가지 우선순위가 붙습니다. 해지·만기 이벤트가 미납 계산보다 먼저입니다. 이미 끝난 계약에 실효를 따지지 않습니다. 그리고 부활 이후 회차만 실효 판정에 넣습니다. 부활은 밀린 돈을 냈다는 뜻이 아니라 부활 청약과 심사를 다시 거쳤다는 뜻이라, 그 앞의 미납은 더 이상 계약을 끊지 않습니다.
마지막으로 시간 축이 하나 더 있습니다. 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=다섯 줄을 적으세요. - 기준일을
2026-06-30으로 고정한 채 인지일2026-06-30과2026-08-31로 각각 계산해/root/ins/restate.txt에known_20260630_lapsed=,known_20260831_lapsed=,restated=,restated_policies=네 줄을 적으세요. policy.db안에status_snapshot(as_of_date, known_as_of, policy_no, calc_status)테이블을 만들고 6단계의 두 결과를 각각 40행씩, 모두 80행 넣으세요./root/ins/policy_report.md에## 확인한 것,## 상태가 어긋난 계약,## 기준일과 인지일,## 권고네 절로 보고서를 쓰세요.
참고
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단계에서 숫자를 눈으로 보고 손으로 옮겨 적는 것. 손으로 옮긴 숫자는 반드시 한 자리가 틀립니다.
계약 데이터 만들기
오른쪽 예시의 생성기를 /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 와 앞 단계에서 계산한 상태를 계약 번호로 맞춰 보고 다른 것만 남기세요. 어긋난 방향이 한 가지가 아니라는 점에 주의하세요.
기준일을 옮겨 숫자가 갈라지는 것 보기
같은 질의를 기준일 2026-09-30 으로도 돌려 /root/ins/drift.txt 에 asof_20260630_normal=, asof_20260630_lapsed=, asof_20260930_normal=, asof_20260930_lapsed=, moved= 다섯 줄을 적으세요.
3단계에서 만든 질의를 그대로 쓰고 날짜만 바꾸세요. 질의를 새로 쓰면 그 사이에 오타가 숨고, 오타는 언제나 실행되는 쪽에 있습니다.
인지일을 바꿔 소급 정정 드러내기
기준일을 2026-06-30 으로 고정한 채 인지일 2026-06-30 과 2026-08-31 로 각각 계산해 /root/ins/restate.txt 에 known_20260630_lapsed=, known_20260831_lapsed=, restated=, restated_policies= 네 줄을 적으세요.
기준일은 2026-06-30 으로 고정합니다. 인지일 이후에 들어온 정정은 그때는 몰랐던 사실이므로 payment_correction 의 old_paid_date 로 되돌려 계산해야 합니다.
재현되는 스냅샷으로 굳히기
policy.db 안에 status_snapshot(as_of_date, known_as_of, policy_no, calc_status) 테이블을 만들고 6단계의 두 결과를 각각 40행씩, 모두 80행 넣으세요.
질의를 저장하는 것으로는 재현되지 않습니다. 계산 결과를 기준일·인지일과 함께 테이블에 넣으세요. sqlite3 의 .import 로 CSV 를 표에 실을 수 있습니다.
담당자가 읽을 보고서 쓰기
/root/ins/policy_report.md 에 ## 확인한 것, ## 상태가 어긋난 계약, ## 기준일과 인지일, ## 권고 네 절로 보고서를 쓰세요.
네 절이 필요합니다. 숫자는 손으로 옮기지 말고 앞 단계가 남긴 파일에서 읽어 채우세요. 그리고 이 데이터에는 피보험자 식별자가 들어 있습니다.