247 lines
9.5 KiB
Python
247 lines
9.5 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""通用模块"""
|
||
|
||
from decimal import Decimal, ROUND_HALF_UP
|
||
from pathlib import Path
|
||
from typing import Any, Dict, List
|
||
from datetime import datetime
|
||
from jinja2 import Environment, FileSystemLoader
|
||
import pandas
|
||
|
||
from common import masterdata, rules_engine
|
||
|
||
|
||
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
|
||
|
||
# 赔案理算记录
|
||
receipts_adjustments = (
|
||
(
|
||
pandas.DataFrame(data=dossier["receipts_layer"]).assign(
|
||
receipt_adjustments=lambda dataframe: dataframe.apply(
|
||
lambda row: receipt_adjust(
|
||
row=row, liabilities=dossier["liabilities_layer"]
|
||
),
|
||
axis="columns",
|
||
) # 票据理算
|
||
)
|
||
)
|
||
.explode(column="receipt_adjustments", ignore_index=True)
|
||
.pipe(
|
||
lambda dataframe: pandas.concat(
|
||
[
|
||
dataframe.drop(
|
||
[
|
||
"receipt_adjustments",
|
||
],
|
||
axis="columns",
|
||
),
|
||
pandas.json_normalize(dataframe["receipt_adjustments"]),
|
||
],
|
||
axis="columns",
|
||
)
|
||
)
|
||
)
|
||
|
||
dossier["receipts_layer"] = receipts_adjustments.to_dict(orient="records")
|
||
|
||
print(dossier["receipts_layer"])
|
||
|
||
# 赔案理算金额
|
||
dossier["adjustment_layer"].update(
|
||
{
|
||
"adjustment_amount": (
|
||
receipts_adjustments["adjustment_amount"]
|
||
.sum()
|
||
.quantize(
|
||
Decimal("0.00"),
|
||
rounding=ROUND_HALF_UP,
|
||
)
|
||
), # 理算金额
|
||
}
|
||
)
|
||
|
||
# 实例化JINJA2
|
||
environment = Environment(
|
||
loader=FileSystemLoader(file_path := Path(__file__).parent)
|
||
)
|
||
# 添加过滤器
|
||
environment.filters["DateTime"] = lambda i: (
|
||
i.strftime("%Y-%m-%d") if i != datetime(9999, 12, 31) else "长期"
|
||
)
|
||
# 加载赔案档案模版
|
||
template = environment.get_template("template.html")
|
||
|
||
with open(
|
||
file_path / f"dossiers/{dossier["report_layer"]["case_number"]}.html",
|
||
"w",
|
||
encoding="utf-8",
|
||
) as file:
|
||
file.write(
|
||
template.render(
|
||
{
|
||
"dossier": dossier,
|
||
}
|
||
)
|
||
)
|
||
|
||
|
||
def receipt_adjust(
|
||
row: pandas.Series, liabilities: List[Dict[str, Any]]
|
||
) -> List[Dict[str, Any]]:
|
||
"""
|
||
理算票据
|
||
:param row: 票据数据
|
||
:param liabilities: 理算责任
|
||
:return: 理算记录
|
||
"""
|
||
# 初始化票据理算记录
|
||
receipt_adjustments = []
|
||
|
||
# 初始化票据剩余可理算金额
|
||
remaining_adjustable_amount = (
|
||
row["personal_self_payment"]
|
||
+ row["non_medical_payment"]
|
||
+ row["reasonable_amount"]
|
||
).quantize( # type: ignore[reportAttributeAccessIssue]
|
||
Decimal("0.00"),
|
||
rounding=ROUND_HALF_UP,
|
||
)
|
||
|
||
# 遍历所有理赔责任,根据出险人、出险事故、查验状态和出险日期匹配理赔责任
|
||
for liability in liabilities:
|
||
if (
|
||
row["payer"] in [liability["insured_person"] for liability in liabilities]
|
||
and row["accident"] == liability["accident"]
|
||
and row["verification"] in ["真票", "无法查验"]
|
||
and liability["commencement_date"]
|
||
<= row["date"]
|
||
<= liability["termination_date"]
|
||
):
|
||
# 个单余额
|
||
remaining_amount = masterdata.query_remaining_amount(
|
||
person_policy_guid=liability["person_policy_guid"],
|
||
)
|
||
|
||
# 个人自费可理算金额
|
||
personal_self_adjustable_amount = (
|
||
row["personal_self_payment"]
|
||
* liability["personal_self_ratio"]
|
||
* Decimal("0.01")
|
||
)
|
||
# 个人自付可理算金额
|
||
non_medical_adjustable_amount = (
|
||
row["non_medical_payment"]
|
||
* liability["non_medical_ratio"]
|
||
* Decimal("0.01")
|
||
)
|
||
# 合理可理算金额
|
||
reasonable_adjustable_amount = (
|
||
row["reasonable_amount"]
|
||
* liability["reasonable_ratio"]
|
||
* Decimal("0.01")
|
||
)
|
||
|
||
# 理算金额
|
||
adjustment_amount = max(
|
||
Decimal("0.00"),
|
||
min(
|
||
remaining_adjustable_amount,
|
||
remaining_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_amount,
|
||
change_amount=adjustment_amount,
|
||
)
|
||
|
||
receipt_adjustments.append(
|
||
{
|
||
"person_policy": liability["person_policy"], # 个单号
|
||
"liability": liability["liability"], # 理赔责任名称
|
||
"personal_self_payment": row[
|
||
"personal_self_payment"
|
||
], # 个人自费金额
|
||
"personal_self_ratio": liability[
|
||
"personal_self_ratio"
|
||
], # 个人自费比例
|
||
"personal_self_adjustable_amount": personal_self_adjustable_amount, # 个人自费可理算金额
|
||
"non_medical_payment": row["non_medical_payment"], # 个人自付金额
|
||
"non_medical_ratio": liability["non_medical_ratio"], # 个人自付比例
|
||
"non_medical_adjustable_amount": non_medical_adjustable_amount, # 个人自付可理算金额
|
||
"reasonable_amount": row["reasonable_amount"], # 合理可理算金额
|
||
"reasonable_ratio": liability["reasonable_ratio"], # 合理部分比例
|
||
"reasonable_adjustable_amount": reasonable_adjustable_amount, # 合理可理算金额
|
||
"adjustment_amount": adjustment_amount, # 理算金额
|
||
"adjustment_explanation": f"""
|
||
1、应理算金额:{remaining_adjustable_amount:.2f};
|
||
2、个单余额:{remaining_amount:.2f};
|
||
3、可理算金额:{adjustable_amount:.2f},其中:
|
||
1)个人自费可理算金额:{personal_self_adjustable_amount:.2f}={row['personal_self_payment']:.2f}*{liability['personal_self_ratio']:.2f}%;
|
||
2)个人自付可理算金额:{non_medical_adjustable_amount:.2f}={row['non_medical_payment']:.2f}*{liability['non_medical_ratio']:.2f}%;
|
||
3)合理部分可理算金额:{reasonable_adjustable_amount:.2f}={row['reasonable_amount']:.2f}*{liability['reasonable_ratio']:.2f}%;
|
||
4、理算金额:{adjustment_amount:.2f},即上述应理算金额、个人余额和可理算金额的最小值。
|
||
""".replace(
|
||
"\n", ""
|
||
).replace(
|
||
" ", ""
|
||
), # 理算说明
|
||
}
|
||
)
|
||
|
||
remaining_adjustable_amount -= adjustment_amount
|
||
# 若剩余可理算金额小于等于0则跳出循环
|
||
if remaining_adjustable_amount <= Decimal("0.00"):
|
||
break
|
||
|
||
if not receipt_adjustments:
|
||
receipt_adjustments.append(
|
||
{
|
||
"person_policy": None, # 个单号
|
||
"liability": None, # 理赔责任名称
|
||
"personal_self_payment": None, # 个人自费金额
|
||
"personal_self_ratio": None, # 个人自费比例
|
||
"personal_self_adjustable_amount": None, # 个人自费可理算金额
|
||
"non_medical_payment": None, # 个人自付金额
|
||
"non_medical_ratio": None, # 个人自付比例
|
||
"non_medical_adjustable_amount": None, # 个人自付可理算金额
|
||
"reasonable_amount": None, # 合理可理算金额
|
||
"reasonable_ratio": None, # 合理部分比例
|
||
"reasonable_adjustable_amount": None, # 合理可理算金额
|
||
"adjustment_amount": Decimal("0.00"), # 理赔责任理算金额
|
||
"adjustment_explanation": "票据不予理算", # 理算说明
|
||
}
|
||
)
|
||
|
||
return receipt_adjustments
|