# -*- 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