How Much Does One Claim Get Paid?
Goal
You move the coverage conditions of the policy terms into code to build an adjudicator that produces a claim's payable amount and its basis. It applies the deductible, coinsurance rate, per-claim cap, and annual cap in a set order, denies by waiting period and exclusion, sums multiple claims of the same accident, and then adjudicates all 141 claims to produce a tally and a report.
Why it matters
Policy terms are written in words and do not state the order. The payable amount of the same claim differs depending on whether you deduct first or multiply the ratio first, whether the deductible is per claim or per accident, and whether you count the annual cap by the accident date or the filing date. Yet neither way raises an error. So before being right in value, this adjudication must first be explainable. If you pull the rule figures out into a table and leave for each claim which rule cut how much, that record becomes the customer notice and the expected value of the regression test. The policy figures of this lab (a deductible of 10,000 to 30,000 won, coinsurance of 10% to 20%, a per-claim cap of 300,000 to 1,000,000 won, an annual cap of 1,000,000 to 5,000,000 won, waiting periods of 30, 90, and 180 days) are not a real product but a synthetic table made up for this lab. The grader does not trust your adjudication wording. It sets up the policy terms, contracts, and claims the grader made in a temporary directory and actually runs your adjudicator with different figures each time, checking the payable amount and the adjudication basis against the values the grader computes separately.
Steps
- Create and run /root/coverage/gen_claims.py to build /root/coverage/claims.db. It has 3 policy terms, 12 contracts, 5 exclusion lines, and 141 claims.
- Compare the totals of the two application orders and write them to /root/coverage/order_compare.json as claims, order_a_total, order_b_total, and difference.
- Create /root/coverage/adjudicate.py to cut in the order deductible → coinsurance → per-claim cap, and leave the amounts cut as steps.
- Deny claims outside the coverage period, within the waiting period, and for excluded items. decision is denied, payable is 0, and put all the reasons in reasons.
- Make the deductible apply only once per accident. The second claim of the same accident is cut only by the remaining deductible.
- Add the annual cap. It accumulates per contract by accident year, is exhausted in filing date order, and comes back to life when the year changes.
- Make the policy figures read only from the table, and pin the rounding of the coinsurance calculation to round half up (ROUND_HALF_UP).
- Adjudicate all 141 claims with /root/coverage/make_payout.py to create /root/coverage/payout.json and /root/coverage/coverage_report.md.
Notes
- Execution contract:
python3 /root/coverage/adjudicate.py --db <DB> --claim <청구번호>(the placeholders are the DB and the claim number) prints one adjudication JSON chunk to standard output and ends with 0. For a claim number that does not exist it is 2.--allprints the adjudication of every claim, one per line. - Adjudication JSON:
{"claim_id": …, "decision": "paid"|"denied", "billed": 정수, "payable": 정수, "reasons": [문자열], "steps": [{"rule": …, "amount": 깎은 금액, "after": 적용 뒤 금액}]}(the placeholders are an integer, a list of strings, the amount cut, and the amount after application) - The rule names in steps are
deductible,coinsurance,per_claim_cap,annual_cap, and you put them in the order applied. Even if the amount cut is 0, you leave the step. The last step's after is payable. - A denied claim has payable 0 and an empty steps list, and in reasons you put all those that apply among
out_of_period,waiting_period,excluded. - Adjudication order: claims are processed by filing date (filed_date), and by claim number order on the same day. The cumulative cap depends on this order.
- The waiting period is seen as the distance between the accident date and the contract start date. It is not the filing date.
- Common mistakes: subtracting the deductible per claim, multiplying the ratio first, judging the annual cap by looking at just one claim, and leaving the rounding to Python's default.
- Do not write the policy figures in the code. The grader swaps in different values in the table and checks whether the adjudication follows.
Generate the policy terms table and the claims snapshot
Create and run /root/coverage/gen_claims.py to build /root/coverage/claims.db. It has 3 policy terms, 12 contracts, 5 exclusion lines, and 141 claims (31 accidents with multiple claims).
First create /root/coverage and build the sqlite DB with python3 inside it. There are four tables: plan, policy, exclusion, and claim. The claims are attached to 100 accidents, and there are 31 accidents with two or more claims.
How much does the application order spread the total
Write claims, order_a_total, order_b_total, and difference to /root/coverage/order_compare.json. A is the sum of the values with the deductible subtracted first and then the coinsurance rate applied, and B is the sum of the values with the coinsurance rate applied first and then the deductible subtracted; both cover all 141 claims and negatives are set to 0.
This step is a comparison, not an adjudication. Do not look at caps, waiting periods, or exclusions. A is the value of max(0, billed amount - deductible) minus the coinsurance share, and B is the value of the billed amount minus the coinsurance share, then minus the deductible, cut off at 0. Make the coinsurance share an integer in won by round half up.
Deductible, coinsurance, and per-claim cap in order
Create /root/coverage/adjudicate.py to calculate the payable amount of the claim received by --claim in the order deductible → coinsurance → per-claim cap, and leave the amount each rule cut in steps.
Read the policy figures from the plan table. The deductible can be larger than the billed amount, so cut it with min, and the per-claim cap cuts only the excess. Each element of steps has the three keys rule, amount, and after, and the last after is payable.
Deny by coverage period, waiting period, and exclusion items
Deny accidents outside the coverage period, accidents within the waiting period, and claims for excluded items. decision is denied, payable is 0, steps is an empty list, and in reasons you put all those that apply among out_of_period, waiting_period, excluded.
The waiting period is the distance between the accident date and the contract start date. It is not the filing date. Handle dates with date.fromisoformat and timedelta. If it is outside the coverage period there is no need to even consider the waiting period, but exclusion items can be written together, separately from that.
The deductible once per accident
Make the claims of the same accident_id share the deductible. If the first claim uses it all, the deductible step of the second claim is 0, and if the first claim is smaller than the deductible, the next claim uses up the remaining share.
Carry along the deductible already used per accident, and cut the smaller of the remaining share and the billed amount. You must process claims in filing date order to get the same answer — if the order changes, which claim bears the deductible changes.
Exhaust the annual cap
Add the annual cap as the fourth rule. It accumulates per contract by accident year, cuts only the amount beyond the remaining room, and the cap comes back to life when the year changes. The last of steps is annual_cap.
The annual cap depends on how much the earlier claims of that contract used. Going through the adjudications in filing date order, accumulate the amount used per (contract, accident year). A claim after the cap is used up has a payable of 0 but is not a denial — decision is still paid and annual_cap cuts the whole amount.
Figures in the table, rounding as policy
Make the policy figures read only from the plan table (do not write values in the code), and pin the rounding of the coinsurance calculation to round half up (ROUND_HALF_UP).
Python's round() and the decimal default round exactly-half values to the even side. If the policy terms say round half up, you must specify rounding in Decimal's quantize. The grader swaps the figures in the table and checks whether the adjudication follows, and whether exactly-half values round up.
Full adjudication and the payout report
Adjudicate all 141 claims with /root/coverage/make_payout.py to create /root/coverage/payout.json (claims, paid, denied, billed_total, payable_total, denied_by_reason, cut_by_rule) and /root/coverage/coverage_report.md (## 무엇을 판정했나 ## 어떤 규칙이 얼마를 깎았나 ## 거절한 건과 이유 ## 남은 위험) (the four Korean section titles mean: what was adjudicated, which rule cut how much, denied cases and reasons, and remaining risks).
Do not reimplement the adjudicator; collect the output of --all. cut_by_rule is the amounts in steps summed per rule, and denied_by_reason is reasons counted per reason. A claim with two reasons is counted once under each of the two reasons. Write the payout total in the report as a number.