259 lines
11 KiB
Python
259 lines
11 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""通用模块"""
|
||
|
||
from decimal import Decimal, ROUND_HALF_UP
|
||
from pathlib import Path
|
||
import sys
|
||
from typing import Any, Dict, List
|
||
|
||
from common import masterdata, rules_engine
|
||
|
||
sys.path.append(Path(__file__).parent.parent.as_posix())
|
||
from utils.html_render import HTMLRenderer
|
||
|
||
|
||
def case_adjust(dossier: Dict[str, Any]) -> None:
|
||
"""
|
||
理算赔案并整合至赔案档案
|
||
:param dossier: 赔案档案
|
||
:return: 无
|
||
"""
|
||
# 基于拒付决策规则评估
|
||
if not (result := rules_engine.evaluate(decision="拒付", inputs=dossier)):
|
||
raise RuntimeError("该保险分公司未配置拒付规则")
|
||
|
||
dossier["adjustment_layer"].update(
|
||
{
|
||
"conclusion": (conclusion := result["conclusion"]), # 理赔结论
|
||
"explanation": result["explanation"], # 结论说明
|
||
}
|
||
)
|
||
if conclusion == "拒付":
|
||
return
|
||
|
||
# 就票据层按照开票日期和票据号顺序排序
|
||
dossier["receipts_layer"].sort(key=lambda x: (x["date"], x["number"]))
|
||
|
||
# 遍历票据层内所有票据,票据理算并添加理算记录
|
||
for idx, receipt in enumerate(dossier["receipts_layer"]):
|
||
# 添加理算记录
|
||
dossier["receipts_layer"][idx]["adjustments"] = receipt_adjust(
|
||
receipt=receipt, liabilities=dossier["liabilities_layer"]
|
||
)
|
||
# 票据理算金额
|
||
dossier["receipts_layer"][idx]["adjustment_amount"] = Decimal(
|
||
sum(
|
||
[
|
||
adjustment["adjustment_amount"]
|
||
for adjustment in dossier["receipts_layer"][idx]["adjustments"]
|
||
]
|
||
)
|
||
).quantize(
|
||
exp=Decimal("0.00"),
|
||
rounding=ROUND_HALF_UP,
|
||
)
|
||
|
||
# 赔案理算金额
|
||
dossier["adjustment_layer"]["adjustment_amount"] = Decimal(
|
||
sum(
|
||
[
|
||
adjustment["adjustment_amount"]
|
||
for receipt in dossier["receipts_layer"]
|
||
for adjustment in receipt["adjustments"]
|
||
]
|
||
)
|
||
).quantize(
|
||
exp=Decimal("0.00"),
|
||
rounding=ROUND_HALF_UP,
|
||
)
|
||
|
||
# 实例化HTML渲染器
|
||
html_renderer = HTMLRenderer(template_path=Path(__file__).parent / "template.html")
|
||
# 根据赔案档案渲染HTML文档
|
||
html_renderer.render(
|
||
obj=dossier,
|
||
output_path=Path(__file__).parent
|
||
/ "dossiers"
|
||
/ f"{dossier["report_layer"]["case_number"]}.html",
|
||
)
|
||
|
||
|
||
def receipt_adjust(
|
||
receipt: Dict[str, Any], liabilities: List[Dict[str, Any]]
|
||
) -> List[Dict[str, Any]]:
|
||
"""
|
||
理算票据
|
||
:param receipt: 票据数据字典
|
||
:param liabilities: 理算责任
|
||
:return: 理算记录
|
||
"""
|
||
# 初始化理算记录
|
||
adjustments = []
|
||
|
||
# 初始化票据剩余理算金额
|
||
remaining_adjustment_amount = masterdata.query_remaining_adjustment_amount(
|
||
receipt_number=receipt["number"],
|
||
)
|
||
if remaining_adjustment_amount is None:
|
||
remaining_adjustment_amount = (
|
||
receipt["personal_self_payment"] # 个人自费金额
|
||
+ receipt["non_medical_payment"] # 个人自付金额
|
||
+ receipt["reasonable_amount"] # 合理金额
|
||
).quantize(
|
||
exp=Decimal("0.00"),
|
||
rounding=ROUND_HALF_UP,
|
||
)
|
||
|
||
# 遍历所有理赔责任,根据出险人、理赔类型、查验状态和出险日期匹配理赔责任
|
||
for liability in liabilities:
|
||
if (
|
||
receipt["payer"]
|
||
in [liability["insured_person"] for liability in liabilities]
|
||
and receipt["accident"] == liability["accident"]
|
||
and receipt["verification"] in ["真票", "无法查验"]
|
||
and liability["commencement_date"]
|
||
<= receipt["date"]
|
||
<= liability["termination_date"]
|
||
):
|
||
# 个单剩余保额
|
||
remaining_coverage_amount = masterdata.query_after_change_amount(
|
||
person_policy_guid=liability["person_policy_guid"],
|
||
)
|
||
|
||
# 个人自费可理算金额
|
||
personal_self_adjustable_amount = (
|
||
receipt["personal_self_payment"] # 个人自费金额
|
||
* liability["personal_self_ratio"] # 个人自费理算比例
|
||
* Decimal("0.01")
|
||
).quantize(
|
||
exp=Decimal("0.00"),
|
||
rounding=ROUND_HALF_UP,
|
||
)
|
||
# 个人自付可理算金额
|
||
non_medical_adjustable_amount = (
|
||
receipt["non_medical_payment"] # 个人自付金额
|
||
* liability["non_medical_ratio"] # 个人自付理算比例
|
||
* Decimal("0.01")
|
||
).quantize(
|
||
exp=Decimal("0.00"),
|
||
rounding=ROUND_HALF_UP,
|
||
)
|
||
# 合理可理算金额
|
||
reasonable_adjustable_amount = (
|
||
receipt["reasonable_amount"] # 合理金额
|
||
* liability["reasonable_ratio"] # 合理理算比例
|
||
* Decimal("0.01")
|
||
).quantize(
|
||
exp=Decimal("0.00"),
|
||
rounding=ROUND_HALF_UP,
|
||
)
|
||
|
||
# 理算金额
|
||
adjustment_amount = max(
|
||
Decimal("0.00"),
|
||
min(
|
||
remaining_adjustment_amount, # 剩余理算金额
|
||
remaining_coverage_amount, # 个单剩余保额
|
||
adjustable_amount := (
|
||
(
|
||
personal_self_adjustable_amount # 个人自费可理算金额
|
||
+ non_medical_adjustable_amount # 个人自付可理算金额
|
||
+ reasonable_adjustable_amount # 合理可理算金额
|
||
).quantize(
|
||
Decimal("0.00"),
|
||
rounding=ROUND_HALF_UP,
|
||
)
|
||
), # 可理算金额
|
||
),
|
||
)
|
||
|
||
# 若理算金额大于0则新增保额扣减记录
|
||
if adjustment_amount > Decimal("0.00"):
|
||
masterdata.add_coverage_change(
|
||
person_policy_guid=liability["person_policy_guid"],
|
||
before_change_amount=remaining_coverage_amount,
|
||
change_amount=adjustment_amount,
|
||
)
|
||
|
||
adjustments.append(
|
||
{
|
||
"group_policy": liability["group_policy"], # 团单号
|
||
"person_policy": liability["person_policy"], # 个单号
|
||
"liability": liability["liability"], # 理赔责任名称
|
||
"accident": liability["accident"], # 理赔类型
|
||
"personal_self_payment": receipt[
|
||
"personal_self_payment"
|
||
], # 个人自费金额
|
||
"personal_self_ratio": liability[
|
||
"personal_self_ratio"
|
||
], # 个人自费理算比例
|
||
"personal_self_adjustable_amount": personal_self_adjustable_amount, # 个人自费可理算金额
|
||
"non_medical_payment": receipt[
|
||
"non_medical_payment"
|
||
], # 个人自付金额
|
||
"non_medical_ratio": liability[
|
||
"non_medical_ratio"
|
||
], # 个人自付理算比例
|
||
"non_medical_adjustable_amount": non_medical_adjustable_amount, # 个人自付可理算金额
|
||
"reasonable_amount": receipt["reasonable_amount"], # 合理金额
|
||
"reasonable_ratio": liability["reasonable_ratio"], # 合理理算比例
|
||
"reasonable_adjustable_amount": reasonable_adjustable_amount, # 合理可理算金额
|
||
"remaining_adjustment_amount": remaining_adjustment_amount, # 剩余理算金额
|
||
"remaining_coverage_amount": remaining_coverage_amount, # 个单剩余保额
|
||
"adjustable_amount": adjustable_amount, # 可理算金额
|
||
"adjustment_amount": adjustment_amount, # 理算金额
|
||
"adjustment_explanation": f"""
|
||
1、剩余理算金额:{remaining_adjustment_amount:.2f};
|
||
2、个单剩余保额:{remaining_coverage_amount:.2f};
|
||
3、可理算金额:{adjustable_amount:.2f},其中:
|
||
1)个人自费可理算金额:{personal_self_adjustable_amount:.2f}={receipt['personal_self_payment']:.2f}*{liability['personal_self_ratio']:.2f}%;
|
||
2)个人自付可理算金额:{non_medical_adjustable_amount:.2f}={receipt['non_medical_payment']:.2f}*{liability['non_medical_ratio']:.2f}%;
|
||
3)合理部分可理算金额:{reasonable_adjustable_amount:.2f}={receipt['reasonable_amount']:.2f}*{liability['reasonable_ratio']:.2f}%;
|
||
4、理算金额:{adjustment_amount:.2f},即上述应理算金额、个人余额和可理算金额的最小值。
|
||
""".replace(
|
||
"\n", ""
|
||
).replace(
|
||
" ", ""
|
||
), # 理算说明
|
||
}
|
||
)
|
||
|
||
remaining_adjustment_amount -= adjustment_amount
|
||
# 若剩余理算金额小于等于0则跳出循环
|
||
if remaining_adjustment_amount <= Decimal("0.00"):
|
||
break
|
||
|
||
if not adjustments:
|
||
adjustments.append(
|
||
{
|
||
"group_policy": None, # 团单号
|
||
"person_policy": None, # 个单号
|
||
"liability": None, # 理赔责任名称
|
||
"accident": None, # 理赔类型
|
||
"personal_self_payment": receipt[
|
||
"personal_self_payment"
|
||
], # 个人自费金额
|
||
"personal_self_ratio": None, # 个人自费理算比例
|
||
"personal_self_adjustable_amount": None, # 个人自费可理算金额
|
||
"non_medical_payment": receipt["non_medical_payment"], # 个人自付金额
|
||
"non_medical_ratio": None, # 个人自付理算比例
|
||
"non_medical_adjustable_amount": None, # 个人自付可理算金额
|
||
"reasonable_amount": receipt["reasonable_amount"], # 合理金额
|
||
"reasonable_ratio": None, # 合理理算比例
|
||
"reasonable_adjustable_amount": None, # 合理可理算金额
|
||
"remaining_adjustment_amount": remaining_adjustment_amount, # 剩余理算金额
|
||
"remaining_coverage_amount": None, # 个单剩余保额
|
||
"adjustable_amount": None, # 可理算金额
|
||
"adjustment_amount": Decimal("0.00"), # 理算金额
|
||
"adjustment_explanation": "票据不予理算", # 理算说明
|
||
}
|
||
)
|
||
|
||
# 新增理算记录
|
||
masterdata.add_ajustment(
|
||
receipt_number=receipt["number"],
|
||
remaining_adjustment_amount=remaining_adjustment_amount,
|
||
)
|
||
|
||
return adjustments
|