This commit is contained in:
parent
b3060d111d
commit
87ac2da670
|
|
@ -0,0 +1,81 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
HTML渲染器
|
||||||
|
"""
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any, Dict
|
||||||
|
|
||||||
|
from jinja2 import Environment, FileSystemLoader
|
||||||
|
|
||||||
|
|
||||||
|
def datetime_to_str(field):
|
||||||
|
"""
|
||||||
|
渲染模板时,若字段为datetime对象则转为字符串
|
||||||
|
:param field: 字段
|
||||||
|
:return: 字符串
|
||||||
|
"""
|
||||||
|
if isinstance(field, datetime):
|
||||||
|
if field == datetime(9999, 12, 31):
|
||||||
|
return "长期"
|
||||||
|
if field.hour == 0 and field.minute == 0 and field.second == 0:
|
||||||
|
return field.strftime("%Y-%m-%d")
|
||||||
|
return field.strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
else:
|
||||||
|
return field
|
||||||
|
|
||||||
|
|
||||||
|
def str_to_str(field):
|
||||||
|
"""
|
||||||
|
渲染模板时,若字段为字符串则转空字符串
|
||||||
|
:param field: 字段
|
||||||
|
:return: 字符串
|
||||||
|
"""
|
||||||
|
if isinstance(field, str):
|
||||||
|
return field
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
class HTMLRenderer:
|
||||||
|
"""
|
||||||
|
HTML渲染器,支持:
|
||||||
|
基于模板,根据数据字典渲染HTML文档
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, template_path: Path):
|
||||||
|
"""
|
||||||
|
初始化HTML渲染器
|
||||||
|
:param template_path: 模板路径
|
||||||
|
"""
|
||||||
|
# 实例化jinja2环境
|
||||||
|
self.environment = Environment(
|
||||||
|
loader=FileSystemLoader(searchpath=template_path.parent)
|
||||||
|
)
|
||||||
|
# 设置过滤器
|
||||||
|
self.environment.filters.update(
|
||||||
|
{
|
||||||
|
"datetime_to_str": datetime_to_str,
|
||||||
|
"str_to_str": str_to_str,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
# 加载指定模板
|
||||||
|
self.template = self.environment.get_template(template_path.name)
|
||||||
|
|
||||||
|
def render(self, obj: Dict[str, Any], output_path: Path) -> None:
|
||||||
|
"""
|
||||||
|
根据数据字典渲染HTML文档
|
||||||
|
:param obj: 数据字典
|
||||||
|
:param output_path: HTML文档输出路径
|
||||||
|
:return: 无
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
with open(
|
||||||
|
file=output_path,
|
||||||
|
mode="w",
|
||||||
|
encoding="utf-8",
|
||||||
|
) as file:
|
||||||
|
file.write(self.template.render(obj=obj)) # 在模板中需以obj获取键值
|
||||||
|
|
||||||
|
except Exception as exception:
|
||||||
|
print(f"根据数据字典渲染HTML文档发生异常:{str(exception)}")
|
||||||
246
票据理赔自动化/case.py
246
票据理赔自动化/case.py
|
|
@ -3,13 +3,14 @@
|
||||||
|
|
||||||
from decimal import Decimal, ROUND_HALF_UP
|
from decimal import Decimal, ROUND_HALF_UP
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
import sys
|
||||||
from typing import Any, Dict, List
|
from typing import Any, Dict, List
|
||||||
from datetime import datetime
|
|
||||||
from jinja2 import Environment, FileSystemLoader
|
|
||||||
import pandas
|
|
||||||
|
|
||||||
from common import masterdata, rules_engine
|
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:
|
def case_adjust(dossier: Dict[str, Any]) -> None:
|
||||||
"""
|
"""
|
||||||
|
|
@ -30,139 +31,134 @@ def case_adjust(dossier: Dict[str, Any]) -> None:
|
||||||
if conclusion == "拒付":
|
if conclusion == "拒付":
|
||||||
return
|
return
|
||||||
|
|
||||||
# 赔案理算记录
|
# 就票据层按照开票日期和票据号顺序排序
|
||||||
receipts_adjustments = (
|
dossier["receipts_layer"].sort(key=lambda x: (x["date"], x["number"]))
|
||||||
(
|
|
||||||
pandas.DataFrame(data=dossier["receipts_layer"]).assign(
|
# 遍历票据层内所有票据,票据理算并添加理算记录
|
||||||
receipt_adjustments=lambda dataframe: dataframe.apply(
|
for idx, receipt in enumerate(dossier["receipts_layer"]):
|
||||||
lambda row: receipt_adjust(
|
# 添加理算记录
|
||||||
row=row, liabilities=dossier["liabilities_layer"]
|
dossier["receipts_layer"][idx]["adjustments"] = receipt_adjust(
|
||||||
),
|
receipt=receipt, liabilities=dossier["liabilities_layer"]
|
||||||
axis="columns",
|
|
||||||
) # 票据理算
|
|
||||||
)
|
)
|
||||||
)
|
# 票据理算金额
|
||||||
.explode(column="receipt_adjustments", ignore_index=True)
|
dossier["receipts_layer"][idx]["adjustment_amount"] = Decimal(
|
||||||
.pipe(
|
sum(
|
||||||
lambda dataframe: pandas.concat(
|
|
||||||
[
|
[
|
||||||
dataframe.drop(
|
adjustment["adjustment_amount"]
|
||||||
[
|
for adjustment in dossier["receipts_layer"][idx]["adjustments"]
|
||||||
"receipt_adjustments",
|
]
|
||||||
],
|
|
||||||
axis="columns",
|
|
||||||
),
|
|
||||||
pandas.json_normalize(dataframe["receipt_adjustments"]),
|
|
||||||
],
|
|
||||||
axis="columns",
|
|
||||||
)
|
)
|
||||||
)
|
).quantize(
|
||||||
)
|
exp=Decimal("0.00"),
|
||||||
|
|
||||||
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,
|
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,
|
||||||
)
|
)
|
||||||
|
|
||||||
# 实例化JINJA2
|
# 实例化HTML渲染器
|
||||||
environment = Environment(
|
html_renderer = HTMLRenderer(template_path=Path(__file__).parent / "template.html")
|
||||||
loader=FileSystemLoader(file_path := Path(__file__).parent)
|
# 根据赔案档案渲染HTML文档
|
||||||
|
html_renderer.render(
|
||||||
|
obj=dossier,
|
||||||
|
output_path=Path(__file__).parent
|
||||||
|
/ "dossiers"
|
||||||
|
/ f"{dossier["report_layer"]["case_number"]}.html",
|
||||||
)
|
)
|
||||||
# 添加过滤器
|
|
||||||
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(
|
def receipt_adjust(
|
||||||
row: pandas.Series, liabilities: List[Dict[str, Any]]
|
receipt: Dict[str, Any], liabilities: List[Dict[str, Any]]
|
||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
理算票据
|
理算票据
|
||||||
:param row: 票据数据
|
:param receipt: 票据数据字典
|
||||||
:param liabilities: 理算责任
|
:param liabilities: 理算责任
|
||||||
:return: 理算记录
|
:return: 理算记录
|
||||||
"""
|
"""
|
||||||
# 初始化票据理算记录
|
# 初始化理算记录
|
||||||
receipt_adjustments = []
|
adjustments = []
|
||||||
|
|
||||||
# 初始化票据剩余可理算金额
|
# 初始化票据剩余理算金额
|
||||||
remaining_adjustable_amount = (
|
remaining_adjustment_amount = masterdata.query_remaining_adjustment_amount(
|
||||||
row["personal_self_payment"]
|
receipt_number=receipt["number"],
|
||||||
+ row["non_medical_payment"]
|
)
|
||||||
+ row["reasonable_amount"]
|
if remaining_adjustment_amount is None:
|
||||||
).quantize( # type: ignore[reportAttributeAccessIssue]
|
remaining_adjustment_amount = (
|
||||||
Decimal("0.00"),
|
receipt["personal_self_payment"] # 个人自费金额
|
||||||
|
+ receipt["non_medical_payment"] # 个人自付金额
|
||||||
|
+ receipt["reasonable_amount"] # 合理金额
|
||||||
|
).quantize(
|
||||||
|
exp=Decimal("0.00"),
|
||||||
rounding=ROUND_HALF_UP,
|
rounding=ROUND_HALF_UP,
|
||||||
)
|
)
|
||||||
|
|
||||||
# 遍历所有理赔责任,根据出险人、出险事故、查验状态和出险日期匹配理赔责任
|
# 遍历所有理赔责任,根据出险人、理赔类型、查验状态和出险日期匹配理赔责任
|
||||||
for liability in liabilities:
|
for liability in liabilities:
|
||||||
if (
|
if (
|
||||||
row["payer"] in [liability["insured_person"] for liability in liabilities]
|
receipt["payer"]
|
||||||
and row["accident"] == liability["accident"]
|
in [liability["insured_person"] for liability in liabilities]
|
||||||
and row["verification"] in ["真票", "无法查验"]
|
and receipt["accident"] == liability["accident"]
|
||||||
|
and receipt["verification"] in ["真票", "无法查验"]
|
||||||
and liability["commencement_date"]
|
and liability["commencement_date"]
|
||||||
<= row["date"]
|
<= receipt["date"]
|
||||||
<= liability["termination_date"]
|
<= liability["termination_date"]
|
||||||
):
|
):
|
||||||
# 个单余额
|
# 个单剩余保额
|
||||||
remaining_amount = masterdata.query_remaining_amount(
|
remaining_coverage_amount = masterdata.query_after_change_amount(
|
||||||
person_policy_guid=liability["person_policy_guid"],
|
person_policy_guid=liability["person_policy_guid"],
|
||||||
)
|
)
|
||||||
|
|
||||||
# 个人自费可理算金额
|
# 个人自费可理算金额
|
||||||
personal_self_adjustable_amount = (
|
personal_self_adjustable_amount = (
|
||||||
row["personal_self_payment"]
|
receipt["personal_self_payment"] # 个人自费金额
|
||||||
* liability["personal_self_ratio"]
|
* liability["personal_self_ratio"] # 个人自费理算比例
|
||||||
* Decimal("0.01")
|
* Decimal("0.01")
|
||||||
|
).quantize(
|
||||||
|
exp=Decimal("0.00"),
|
||||||
|
rounding=ROUND_HALF_UP,
|
||||||
)
|
)
|
||||||
# 个人自付可理算金额
|
# 个人自付可理算金额
|
||||||
non_medical_adjustable_amount = (
|
non_medical_adjustable_amount = (
|
||||||
row["non_medical_payment"]
|
receipt["non_medical_payment"] # 个人自付金额
|
||||||
* liability["non_medical_ratio"]
|
* liability["non_medical_ratio"] # 个人自付理算比例
|
||||||
* Decimal("0.01")
|
* Decimal("0.01")
|
||||||
|
).quantize(
|
||||||
|
exp=Decimal("0.00"),
|
||||||
|
rounding=ROUND_HALF_UP,
|
||||||
)
|
)
|
||||||
# 合理可理算金额
|
# 合理可理算金额
|
||||||
reasonable_adjustable_amount = (
|
reasonable_adjustable_amount = (
|
||||||
row["reasonable_amount"]
|
receipt["reasonable_amount"] # 合理金额
|
||||||
* liability["reasonable_ratio"]
|
* liability["reasonable_ratio"] # 合理理算比例
|
||||||
* Decimal("0.01")
|
* Decimal("0.01")
|
||||||
|
).quantize(
|
||||||
|
exp=Decimal("0.00"),
|
||||||
|
rounding=ROUND_HALF_UP,
|
||||||
)
|
)
|
||||||
|
|
||||||
# 理算金额
|
# 理算金额
|
||||||
adjustment_amount = max(
|
adjustment_amount = max(
|
||||||
Decimal("0.00"),
|
Decimal("0.00"),
|
||||||
min(
|
min(
|
||||||
remaining_adjustable_amount,
|
remaining_adjustment_amount, # 剩余理算金额
|
||||||
remaining_amount,
|
remaining_coverage_amount, # 个单剩余保额
|
||||||
adjustable_amount := (
|
adjustable_amount := (
|
||||||
(
|
(
|
||||||
personal_self_adjustable_amount
|
personal_self_adjustable_amount # 个人自费可理算金额
|
||||||
+ non_medical_adjustable_amount
|
+ non_medical_adjustable_amount # 个人自付可理算金额
|
||||||
+ reasonable_adjustable_amount
|
+ reasonable_adjustable_amount # 合理可理算金额
|
||||||
).quantize(
|
).quantize(
|
||||||
Decimal("0.00"),
|
Decimal("0.00"),
|
||||||
rounding=ROUND_HALF_UP,
|
rounding=ROUND_HALF_UP,
|
||||||
|
|
@ -175,35 +171,44 @@ def receipt_adjust(
|
||||||
if adjustment_amount > Decimal("0.00"):
|
if adjustment_amount > Decimal("0.00"):
|
||||||
masterdata.add_coverage_change(
|
masterdata.add_coverage_change(
|
||||||
person_policy_guid=liability["person_policy_guid"],
|
person_policy_guid=liability["person_policy_guid"],
|
||||||
before_change_amount=remaining_amount,
|
before_change_amount=remaining_coverage_amount,
|
||||||
change_amount=adjustment_amount,
|
change_amount=adjustment_amount,
|
||||||
)
|
)
|
||||||
|
|
||||||
receipt_adjustments.append(
|
adjustments.append(
|
||||||
{
|
{
|
||||||
|
"group_policy": liability["group_policy"], # 团单号
|
||||||
"person_policy": liability["person_policy"], # 个单号
|
"person_policy": liability["person_policy"], # 个单号
|
||||||
"liability": liability["liability"], # 理赔责任名称
|
"liability": liability["liability"], # 理赔责任名称
|
||||||
"personal_self_payment": row[
|
"accident": liability["accident"], # 理赔类型
|
||||||
|
"personal_self_payment": receipt[
|
||||||
"personal_self_payment"
|
"personal_self_payment"
|
||||||
], # 个人自费金额
|
], # 个人自费金额
|
||||||
"personal_self_ratio": liability[
|
"personal_self_ratio": liability[
|
||||||
"personal_self_ratio"
|
"personal_self_ratio"
|
||||||
], # 个人自费比例
|
], # 个人自费理算比例
|
||||||
"personal_self_adjustable_amount": personal_self_adjustable_amount, # 个人自费可理算金额
|
"personal_self_adjustable_amount": personal_self_adjustable_amount, # 个人自费可理算金额
|
||||||
"non_medical_payment": row["non_medical_payment"], # 个人自付金额
|
"non_medical_payment": receipt[
|
||||||
"non_medical_ratio": liability["non_medical_ratio"], # 个人自付比例
|
"non_medical_payment"
|
||||||
|
], # 个人自付金额
|
||||||
|
"non_medical_ratio": liability[
|
||||||
|
"non_medical_ratio"
|
||||||
|
], # 个人自付理算比例
|
||||||
"non_medical_adjustable_amount": non_medical_adjustable_amount, # 个人自付可理算金额
|
"non_medical_adjustable_amount": non_medical_adjustable_amount, # 个人自付可理算金额
|
||||||
"reasonable_amount": row["reasonable_amount"], # 合理可理算金额
|
"reasonable_amount": receipt["reasonable_amount"], # 合理金额
|
||||||
"reasonable_ratio": liability["reasonable_ratio"], # 合理部分比例
|
"reasonable_ratio": liability["reasonable_ratio"], # 合理理算比例
|
||||||
"reasonable_adjustable_amount": reasonable_adjustable_amount, # 合理可理算金额
|
"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_amount": adjustment_amount, # 理算金额
|
||||||
"adjustment_explanation": f"""
|
"adjustment_explanation": f"""
|
||||||
1、应理算金额:{remaining_adjustable_amount:.2f};
|
1、剩余理算金额:{remaining_adjustment_amount:.2f};
|
||||||
2、个单余额:{remaining_amount:.2f};
|
2、个单剩余保额:{remaining_coverage_amount:.2f};
|
||||||
3、可理算金额:{adjustable_amount:.2f},其中:
|
3、可理算金额:{adjustable_amount:.2f},其中:
|
||||||
1)个人自费可理算金额:{personal_self_adjustable_amount:.2f}={row['personal_self_payment']:.2f}*{liability['personal_self_ratio']:.2f}%;
|
1)个人自费可理算金额:{personal_self_adjustable_amount:.2f}={receipt['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}%;
|
2)个人自付可理算金额:{non_medical_adjustable_amount:.2f}={receipt['non_medical_payment']:.2f}*{liability['non_medical_ratio']:.2f}%;
|
||||||
3)合理部分可理算金额:{reasonable_adjustable_amount:.2f}={row['reasonable_amount']:.2f}*{liability['reasonable_ratio']:.2f}%;
|
3)合理部分可理算金额:{reasonable_adjustable_amount:.2f}={receipt['reasonable_amount']:.2f}*{liability['reasonable_ratio']:.2f}%;
|
||||||
4、理算金额:{adjustment_amount:.2f},即上述应理算金额、个人余额和可理算金额的最小值。
|
4、理算金额:{adjustment_amount:.2f},即上述应理算金额、个人余额和可理算金额的最小值。
|
||||||
""".replace(
|
""".replace(
|
||||||
"\n", ""
|
"\n", ""
|
||||||
|
|
@ -213,28 +218,41 @@ def receipt_adjust(
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
remaining_adjustable_amount -= adjustment_amount
|
remaining_adjustment_amount -= adjustment_amount
|
||||||
# 若剩余可理算金额小于等于0则跳出循环
|
# 若剩余理算金额小于等于0则跳出循环
|
||||||
if remaining_adjustable_amount <= Decimal("0.00"):
|
if remaining_adjustment_amount <= Decimal("0.00"):
|
||||||
break
|
break
|
||||||
|
|
||||||
if not receipt_adjustments:
|
if not adjustments:
|
||||||
receipt_adjustments.append(
|
adjustments.append(
|
||||||
{
|
{
|
||||||
|
"group_policy": None, # 团单号
|
||||||
"person_policy": None, # 个单号
|
"person_policy": None, # 个单号
|
||||||
"liability": None, # 理赔责任名称
|
"liability": None, # 理赔责任名称
|
||||||
"personal_self_payment": None, # 个人自费金额
|
"accident": None, # 理赔类型
|
||||||
"personal_self_ratio": None, # 个人自费比例
|
"personal_self_payment": receipt[
|
||||||
|
"personal_self_payment"
|
||||||
|
], # 个人自费金额
|
||||||
|
"personal_self_ratio": None, # 个人自费理算比例
|
||||||
"personal_self_adjustable_amount": None, # 个人自费可理算金额
|
"personal_self_adjustable_amount": None, # 个人自费可理算金额
|
||||||
"non_medical_payment": None, # 个人自付金额
|
"non_medical_payment": receipt["non_medical_payment"], # 个人自付金额
|
||||||
"non_medical_ratio": None, # 个人自付比例
|
"non_medical_ratio": None, # 个人自付理算比例
|
||||||
"non_medical_adjustable_amount": None, # 个人自付可理算金额
|
"non_medical_adjustable_amount": None, # 个人自付可理算金额
|
||||||
"reasonable_amount": None, # 合理可理算金额
|
"reasonable_amount": receipt["reasonable_amount"], # 合理金额
|
||||||
"reasonable_ratio": None, # 合理部分比例
|
"reasonable_ratio": None, # 合理理算比例
|
||||||
"reasonable_adjustable_amount": None, # 合理可理算金额
|
"reasonable_adjustable_amount": None, # 合理可理算金额
|
||||||
"adjustment_amount": Decimal("0.00"), # 理赔责任理算金额
|
"remaining_adjustment_amount": remaining_adjustment_amount, # 剩余理算金额
|
||||||
|
"remaining_coverage_amount": None, # 个单剩余保额
|
||||||
|
"adjustable_amount": None, # 可理算金额
|
||||||
|
"adjustment_amount": Decimal("0.00"), # 理算金额
|
||||||
"adjustment_explanation": "票据不予理算", # 理算说明
|
"adjustment_explanation": "票据不予理算", # 理算说明
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
return receipt_adjustments
|
# 新增理算记录
|
||||||
|
masterdata.add_ajustment(
|
||||||
|
receipt_number=receipt["number"],
|
||||||
|
remaining_adjustment_amount=remaining_adjustment_amount,
|
||||||
|
)
|
||||||
|
|
||||||
|
return adjustments
|
||||||
|
|
|
||||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
|
@ -115,18 +115,15 @@ def image_classify(image_index: int, image_path: Path, dossier: Dict[str, Any])
|
||||||
image_format, image_ndarray, image_size_specified=2
|
image_format, image_ndarray, image_size_specified=2
|
||||||
)
|
)
|
||||||
|
|
||||||
# 将影像件添加至影像件层
|
# 将已分类影像件添加至影像件层
|
||||||
dossier["images_layer"].append(
|
dossier["images_layer"][f"{image_index:02d}"] = {
|
||||||
{
|
|
||||||
"image_index": f"{image_index:02d}", # 影像件编号
|
|
||||||
"image_path": image_path.as_posix(), # 影像件路径
|
"image_path": image_path.as_posix(), # 影像件路径
|
||||||
"image_name": image_path.stem, # 影像件名称
|
"image_relative_path": image_path.relative_to(image_path.parent.parent).as_posix(), # 影像件相对路径
|
||||||
"image_format": image_format, # 影像件格式
|
"image_format": image_format, # 影像件格式
|
||||||
"image_guid": image_guid, # 影像件唯一标识
|
"image_guid": image_guid, # 影像件唯一标识
|
||||||
"image_base64": image_base64, # 影像件BASE64编码
|
"image_base64": image_base64, # 影像件BASE64编码
|
||||||
"image_type": image_type, # 影像件类型
|
"image_type": image_type, # 影像件类型
|
||||||
}
|
} # 影像件编号作为键名
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def image_read(
|
def image_read(
|
||||||
|
|
@ -139,7 +136,7 @@ def image_read(
|
||||||
:return: 影像件图像数组
|
:return: 影像件图像数组
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
# 先使用读取影像件,再解码为单通道灰度图数组对象
|
# 先使用读取影像件,再解码为单通道灰度图数组对象(因在windows系统中,cv2.imread就包含中文的影像件路径兼容较差,估使用numpy.fromfile)
|
||||||
image_ndarray = cv2.imdecode(
|
image_ndarray = cv2.imdecode(
|
||||||
buf=numpy.fromfile(file=image_path, dtype=numpy.uint8),
|
buf=numpy.fromfile(file=image_path, dtype=numpy.uint8),
|
||||||
flags=cv2.IMREAD_GRAYSCALE,
|
flags=cv2.IMREAD_GRAYSCALE,
|
||||||
|
|
@ -218,13 +215,15 @@ def image_compress(
|
||||||
|
|
||||||
|
|
||||||
def image_recognize(
|
def image_recognize(
|
||||||
|
image_index: str,
|
||||||
image: Dict[str, Any],
|
image: Dict[str, Any],
|
||||||
insurer_company: str,
|
insurer_company: str,
|
||||||
dossier: Dict[str, Any],
|
dossier: Dict[str, Any],
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
识别影像件并整合至赔案档案
|
识别影像件并整合至赔案档案
|
||||||
:param image: 影像件
|
:param image_index: 影像件编号
|
||||||
|
:param image: 影像件数据字典
|
||||||
:param insurer_company: 保险分公司
|
:param insurer_company: 保险分公司
|
||||||
:param dossier: 赔案档案
|
:param dossier: 赔案档案
|
||||||
:return: 无
|
:return: 无
|
||||||
|
|
@ -237,19 +236,19 @@ def image_recognize(
|
||||||
"image_type": image["image_type"],
|
"image_type": image["image_type"],
|
||||||
},
|
},
|
||||||
)["recognize_enabled"]:
|
)["recognize_enabled"]:
|
||||||
|
dossier["images_layer"][image_index]["image_recognized"] = "否,无需识别"
|
||||||
return
|
return
|
||||||
|
|
||||||
# 根据影像件类型匹配影像件识别方法
|
# 根据影像件类型匹配影像件识别方法
|
||||||
match image["image_type"]:
|
match image["image_type"]:
|
||||||
case "居民户口簿":
|
|
||||||
raise RuntimeError("暂不支持居民户口簿")
|
|
||||||
case "居民身份证(国徽、头像面)" | "居民身份证(国徽面)" | "居民身份证(头像面)":
|
case "居民身份证(国徽、头像面)" | "居民身份证(国徽面)" | "居民身份证(头像面)":
|
||||||
# 居民身份证识别并整合至赔案档案
|
# 居民身份证识别并整合至赔案档案
|
||||||
identity_card_recognize(
|
identity_card_recognize(
|
||||||
image=image, insurer_company=insurer_company, dossier=dossier
|
image=image, insurer_company=insurer_company, dossier=dossier
|
||||||
)
|
)
|
||||||
case "中国港澳台地区及境外护照":
|
case "银行卡":
|
||||||
raise RuntimeError("暂不支持中国港澳台地区及境外护照")
|
# 银行卡识别并整合至赔案档案
|
||||||
|
bank_card_recognize(image=image, dossier=dossier)
|
||||||
case "理赔申请书":
|
case "理赔申请书":
|
||||||
application_recognize(
|
application_recognize(
|
||||||
image=image, insurer_company=insurer_company, dossier=dossier
|
image=image, insurer_company=insurer_company, dossier=dossier
|
||||||
|
|
@ -257,11 +256,15 @@ def image_recognize(
|
||||||
case "增值税发票" | "医疗门诊收费票据" | "医疗住院收费票据":
|
case "增值税发票" | "医疗门诊收费票据" | "医疗住院收费票据":
|
||||||
# 票据识别并整合至赔案档案
|
# 票据识别并整合至赔案档案
|
||||||
receipt_recognize(
|
receipt_recognize(
|
||||||
image=image, insurer_company=insurer_company, dossier=dossier
|
image_index=image_index,
|
||||||
|
image=image,
|
||||||
|
insurer_company=insurer_company,
|
||||||
|
dossier=dossier,
|
||||||
)
|
)
|
||||||
case "银行卡":
|
case _:
|
||||||
# 银行卡识别并整合至赔案档案
|
raise RuntimeError(f"影像件类型未配置影像件识别方法")
|
||||||
bank_card_recognize(image=image, dossier=dossier)
|
|
||||||
|
dossier["images_layer"][image_index]["image_recognized"] = "是"
|
||||||
|
|
||||||
|
|
||||||
def identity_card_recognize(
|
def identity_card_recognize(
|
||||||
|
|
@ -327,7 +330,7 @@ def identity_card_recognize(
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
# 根据保险分公司、被保险人、证件类型、证件号码和出险时间查询个单
|
# 根据保险分公司名称、被保险人姓名、证件类型、证件号码和报案时间查询被保险人的理赔责任
|
||||||
dossier["liabilities_layer"] = masterdata.query_liabilities(
|
dossier["liabilities_layer"] = masterdata.query_liabilities(
|
||||||
insurer_company=insurer_company,
|
insurer_company=insurer_company,
|
||||||
insured_person=insured_person,
|
insured_person=insured_person,
|
||||||
|
|
@ -577,17 +580,22 @@ def application_recognize(
|
||||||
|
|
||||||
|
|
||||||
def receipt_recognize(
|
def receipt_recognize(
|
||||||
image: Dict[str, Any], insurer_company: str, dossier: Dict[str, Any]
|
image_index: str,
|
||||||
|
image: Dict[str, Any],
|
||||||
|
insurer_company: str,
|
||||||
|
dossier: Dict[str, Any],
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
识别票据并整合至赔案档案
|
识别票据并整合至赔案档案
|
||||||
|
:param image_index: 影像件编号
|
||||||
:param image: 影像件
|
:param image: 影像件
|
||||||
:param insurer_company: 保险分公司
|
:param insurer_company: 保险分公司
|
||||||
:param dossier: 赔案档案
|
:param dossier: 赔案档案
|
||||||
:return: 空
|
:return: 空
|
||||||
"""
|
"""
|
||||||
# 初始化票据数据
|
# 初始化票据数据
|
||||||
receipt = {"image_index": image["image_index"]}
|
receipt = {"image_index": image_index, "image_path": image["image_path"]}
|
||||||
|
|
||||||
# 请求深圳快瞳票据查验接口(兼容增值税发票、医疗门诊/住院收费票据)
|
# 请求深圳快瞳票据查验接口(兼容增值税发票、医疗门诊/住院收费票据)
|
||||||
response = request.post(
|
response = request.post(
|
||||||
url=(url := "https://ai.inspirvision.cn/s/api/ocr/invoiceCheckAll"),
|
url=(url := "https://ai.inspirvision.cn/s/api/ocr/invoiceCheckAll"),
|
||||||
|
|
@ -613,25 +621,29 @@ def receipt_recognize(
|
||||||
"真票"
|
"真票"
|
||||||
if response["data"]["details"]["invoiceTypeNo"] == "0"
|
if response["data"]["details"]["invoiceTypeNo"] == "0"
|
||||||
else "红票"
|
else "红票"
|
||||||
), # 红票为状态为失控、作废、已红冲、部分红冲和全额红冲的票据
|
), # 查验状态,红票对应查验状态为失控、作废、已红冲、部分红冲和全额红冲
|
||||||
"number": response["data"]["details"]["number"],
|
"number": response["data"]["details"]["number"], # 票据号
|
||||||
"code": (
|
"code": (
|
||||||
response["data"]["details"]["code"]
|
response["data"]["details"]["code"]
|
||||||
if response["data"]["details"]["code"]
|
if response["data"]["details"]["code"]
|
||||||
else None
|
else None
|
||||||
),
|
), # 票据代码
|
||||||
"date": datetime.strptime(
|
"date": datetime.strptime(
|
||||||
response["data"]["details"]["date"], "%Y年%m月%d日"
|
response["data"]["details"]["date"], "%Y年%m月%d日"
|
||||||
), # 转为日期时间(datetime对象)
|
), # 开票日期
|
||||||
"verification_code": response["data"]["details"]["check_code"],
|
"check_code": response["data"]["details"][
|
||||||
|
"check_code"
|
||||||
|
], # 校验码
|
||||||
"amount": Decimal(
|
"amount": Decimal(
|
||||||
response["data"]["details"]["total"]
|
response["data"]["details"]["total"]
|
||||||
).quantize(
|
).quantize(
|
||||||
Decimal("0.00"),
|
Decimal("0.00"),
|
||||||
rounding=ROUND_HALF_UP,
|
rounding=ROUND_HALF_UP,
|
||||||
), # 深圳快瞳票据查验接口中开票金额由字符串转为Decimal,保留两位小数
|
), # 开票金额
|
||||||
"payer": response["data"]["details"]["buyer"],
|
"payer": response["data"]["details"]["buyer"], # 出险人
|
||||||
"institution": response["data"]["details"]["seller"],
|
"institution": response["data"]["details"][
|
||||||
|
"seller"
|
||||||
|
], # 购药及就医机构
|
||||||
"items": [
|
"items": [
|
||||||
{
|
{
|
||||||
"item": item["name"],
|
"item": item["name"],
|
||||||
|
|
@ -642,13 +654,13 @@ def receipt_recognize(
|
||||||
)
|
)
|
||||||
if item["quantity"]
|
if item["quantity"]
|
||||||
else Decimal("0.00")
|
else Decimal("0.00")
|
||||||
), # 深圳快瞳票据查验接口中明细单位由空字符转为None,若非空字符由字符串转为Decimal,保留两位小数
|
),
|
||||||
"amount": (
|
"amount": (
|
||||||
Decimal(item["total"]) + Decimal(item["tax"])
|
Decimal(item["total"]) + Decimal(item["tax"])
|
||||||
).quantize(
|
).quantize(
|
||||||
Decimal("0.00"),
|
Decimal("0.00"),
|
||||||
rounding=ROUND_HALF_UP,
|
rounding=ROUND_HALF_UP,
|
||||||
), # 深圳快瞳票据查验接口中明细的金额和税额由字符串转为Decimal,保留两位小数,并求和
|
),
|
||||||
}
|
}
|
||||||
for item in response["data"]["details"]["items"]
|
for item in response["data"]["details"]["items"]
|
||||||
],
|
],
|
||||||
|
|
@ -689,7 +701,7 @@ def receipt_recognize(
|
||||||
if response["data"]["hospitalizationDate"]
|
if response["data"]["hospitalizationDate"]
|
||||||
else None
|
else None
|
||||||
),
|
),
|
||||||
"verification_code": response["data"]["checkCode"],
|
"check_code": response["data"]["checkCode"],
|
||||||
"amount": Decimal(response["data"]["amount"]).quantize(
|
"amount": Decimal(response["data"]["amount"]).quantize(
|
||||||
Decimal("0.00"),
|
Decimal("0.00"),
|
||||||
rounding=ROUND_HALF_UP,
|
rounding=ROUND_HALF_UP,
|
||||||
|
|
@ -781,9 +793,7 @@ def receipt_recognize(
|
||||||
fuzzy_match(response["data"], "开票日期"),
|
fuzzy_match(response["data"], "开票日期"),
|
||||||
"%Y年%m月%d日",
|
"%Y年%m月%d日",
|
||||||
),
|
),
|
||||||
"verification_code": fuzzy_match(
|
"check_code": fuzzy_match(response["data"], "校验码"),
|
||||||
response["data"], "校验码"
|
|
||||||
),
|
|
||||||
"amount": Decimal(
|
"amount": Decimal(
|
||||||
fuzzy_match(response["data"], "小写金额").replace(
|
fuzzy_match(response["data"], "小写金额").replace(
|
||||||
"¥", ""
|
"¥", ""
|
||||||
|
|
@ -857,9 +867,7 @@ def receipt_recognize(
|
||||||
fuzzy_match(response["data"], "开票日期"),
|
fuzzy_match(response["data"], "开票日期"),
|
||||||
"%Y-%m-%d",
|
"%Y-%m-%d",
|
||||||
),
|
),
|
||||||
"verification_code": fuzzy_match(
|
"check_code": fuzzy_match(response["data"], "校验码"),
|
||||||
response["data"], "校验码"
|
|
||||||
),
|
|
||||||
"amount": Decimal(
|
"amount": Decimal(
|
||||||
fuzzy_match(
|
fuzzy_match(
|
||||||
response["data"], "合计金额(小写)"
|
response["data"], "合计金额(小写)"
|
||||||
|
|
@ -882,7 +890,7 @@ def receipt_recognize(
|
||||||
"amount": Decimal(amount).quantize(
|
"amount": Decimal(amount).quantize(
|
||||||
Decimal("0.00"),
|
Decimal("0.00"),
|
||||||
rounding=ROUND_HALF_UP,
|
rounding=ROUND_HALF_UP,
|
||||||
), # 深圳快瞳票据识别接口中明细的金额和税额由字符串转为Decimal,保留两位小数,并求和
|
),
|
||||||
}
|
}
|
||||||
for name, quantity, amount in zip(
|
for name, quantity, amount in zip(
|
||||||
[
|
[
|
||||||
|
|
@ -964,7 +972,7 @@ def receipt_recognize(
|
||||||
if isinstance(receipt["endtime"], dict)
|
if isinstance(receipt["endtime"], dict)
|
||||||
else None
|
else None
|
||||||
),
|
),
|
||||||
"verification_code": fuzzy_match(
|
"check_code": fuzzy_match(
|
||||||
receipt["global_detail"]["region_specific"],
|
receipt["global_detail"]["region_specific"],
|
||||||
"校验码",
|
"校验码",
|
||||||
),
|
),
|
||||||
|
|
@ -1063,6 +1071,8 @@ def receipt_recognize(
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.assign(
|
.assign(
|
||||||
|
personal_self_payment=Decimal("0.00"), # 个人自费项
|
||||||
|
non_medical_payment=Decimal("0.00"), # 个人自付项
|
||||||
reasonable_amount=lambda dataframe: dataframe.apply(
|
reasonable_amount=lambda dataframe: dataframe.apply(
|
||||||
lambda row: Decimal(
|
lambda row: Decimal(
|
||||||
# 基于扣除明细项不合理费用决策规则评估
|
# 基于扣除明细项不合理费用决策规则评估
|
||||||
|
|
@ -1080,8 +1090,8 @@ def receipt_recognize(
|
||||||
rounding=ROUND_HALF_UP,
|
rounding=ROUND_HALF_UP,
|
||||||
),
|
),
|
||||||
axis="columns",
|
axis="columns",
|
||||||
|
), # 合理项
|
||||||
)
|
)
|
||||||
) # 扣除明细项不合理费用
|
|
||||||
)
|
)
|
||||||
|
|
||||||
receipt.update(
|
receipt.update(
|
||||||
|
|
@ -1092,7 +1102,7 @@ def receipt_recognize(
|
||||||
in receipt["payer"]
|
in receipt["payer"]
|
||||||
else None
|
else None
|
||||||
), # 出险人姓名
|
), # 出险人姓名
|
||||||
"accident": "药店购药", # 出险事故
|
"accident": "药店购药", # 理赔类型
|
||||||
"diagnosis": "购药拟诊", # 医疗诊断
|
"diagnosis": "购药拟诊", # 医疗诊断
|
||||||
"personal_self_payment": Decimal("0.00"), # 个人自费金额
|
"personal_self_payment": Decimal("0.00"), # 个人自费金额
|
||||||
"non_medical_payment": Decimal("0.00"), # 个人自付金额
|
"non_medical_payment": Decimal("0.00"), # 个人自付金额
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ https://liubiren.feishu.cn/docx/WFjTdBpzroUjQvxxrNIcKvGnneh?from=from_copylink
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
import re
|
||||||
|
|
||||||
from case import case_adjust
|
from case import case_adjust
|
||||||
from image import image_classify, image_recognize
|
from image import image_classify, image_recognize
|
||||||
|
|
@ -24,15 +25,16 @@ if __name__ == "__main__":
|
||||||
# 初始化赔案档案(推送至TPA时,保险公司会提保险分公司名称、报案时间和影像件等,TPA签收后生成赔案号)
|
# 初始化赔案档案(推送至TPA时,保险公司会提保险分公司名称、报案时间和影像件等,TPA签收后生成赔案号)
|
||||||
dossier = {
|
dossier = {
|
||||||
"report_layer": {
|
"report_layer": {
|
||||||
"case_number": case_path.stem, # 默认为赔案文件夹名称
|
|
||||||
"insurer_company": (
|
"insurer_company": (
|
||||||
insurer_company := "中银保险有限公司苏州分公司"
|
insurer_company := "中银保险有限公司苏州分公司"
|
||||||
), # 默认为中银保险有限公司苏州分公司
|
), # 保险分公司名称默认为中银保险有限公司苏州分公司
|
||||||
"report_time": datetime(
|
"report_time": datetime(
|
||||||
2025, 7, 25, 12, 0, 0
|
2025, 7, 25, 12, 0, 0
|
||||||
), # 指定报案时间,默认为 datetime对象
|
), # 报案时间默认为2025-07-25 12:00:00
|
||||||
|
"images_counts": 15, # 影像件数默认为15
|
||||||
|
"case_number": case_path.stem, # 赔案号默认为赔案文件夹名称
|
||||||
}, # 报案层
|
}, # 报案层
|
||||||
"images_layer": [], # 影像件层
|
"images_layer": {}, # 影像件层
|
||||||
"insured_person_layer": {}, # 出险人层
|
"insured_person_layer": {}, # 出险人层
|
||||||
"liabilities_layer": [], # 理赔责任层
|
"liabilities_layer": [], # 理赔责任层
|
||||||
"receipts_layer": [], # 票据层
|
"receipts_layer": [], # 票据层
|
||||||
|
|
@ -43,11 +45,11 @@ if __name__ == "__main__":
|
||||||
for image_index, image_path in enumerate(
|
for image_index, image_path in enumerate(
|
||||||
sorted(
|
sorted(
|
||||||
[
|
[
|
||||||
i
|
x
|
||||||
for i in case_path.glob(pattern="*")
|
for x in case_path.glob(pattern="*")
|
||||||
if i.is_file() and i.suffix.lower() in [".jpg", ".jpeg", ".png"]
|
if x.is_file() and x.suffix.lower() in [".jpg", ".jpeg", ".png"]
|
||||||
],
|
],
|
||||||
key=lambda i: i.stat().st_birthtime, # 根据影像件创建时间顺序排序
|
key=lambda x: x.stat().st_birthtime, # 根据影像件创建时间顺序排序
|
||||||
),
|
),
|
||||||
1,
|
1,
|
||||||
):
|
):
|
||||||
|
|
@ -57,34 +59,47 @@ if __name__ == "__main__":
|
||||||
)
|
)
|
||||||
|
|
||||||
# 就影像件层按照影像件类型指定排序
|
# 就影像件层按照影像件类型指定排序
|
||||||
dossier["images_layer"].sort(
|
dossier["images_layer"] = dict(
|
||||||
key=lambda i: [
|
sorted(
|
||||||
|
dossier["images_layer"].items(),
|
||||||
|
key=lambda x: [
|
||||||
"居民户口簿",
|
"居民户口簿",
|
||||||
"居民身份证(国徽面)",
|
"居民身份证(国徽面)",
|
||||||
"居民身份证(头像面)",
|
"居民身份证(头像面)",
|
||||||
"居民身份证(国徽、头像面)",
|
"居民身份证(国徽、头像面)",
|
||||||
"中国港澳台地区及境外护照",
|
"中国港澳台地区及境外护照",
|
||||||
|
"银行卡",
|
||||||
"理赔申请书",
|
"理赔申请书",
|
||||||
|
"其它",
|
||||||
"增值税发票",
|
"增值税发票",
|
||||||
"医疗门诊收费票据",
|
"医疗门诊收费票据",
|
||||||
"医疗住院收费票据",
|
"医疗住院收费票据",
|
||||||
"医疗费用清单",
|
"医疗费用清单",
|
||||||
"银行卡",
|
].index(x[1]["image_type"]),
|
||||||
"其它",
|
),
|
||||||
].index(i["image_type"])
|
|
||||||
)
|
)
|
||||||
|
# 统计已分类影像件数
|
||||||
|
dossier["classified_images_counts"] = len(dossier["images_layer"])
|
||||||
|
|
||||||
# 遍历影像件层内影像件
|
# 遍历影像件层内所有影像件
|
||||||
for image in dossier["images_layer"]:
|
for image_index, image in dossier["images_layer"].items():
|
||||||
|
if re.match(pattern=r"^\d{2}$", string=image_index):
|
||||||
# 识别影像件并整合至赔案档案
|
# 识别影像件并整合至赔案档案
|
||||||
image_recognize(
|
image_recognize(
|
||||||
|
image_index=image_index,
|
||||||
image=image,
|
image=image,
|
||||||
insurer_company=insurer_company,
|
insurer_company=insurer_company,
|
||||||
dossier=dossier,
|
dossier=dossier,
|
||||||
)
|
)
|
||||||
|
|
||||||
# 就票据层按照开票日期和票据号顺序排序
|
# 统计已识别影像件数
|
||||||
dossier["receipts_layer"].sort(key=lambda x: (x["date"], x["number"]))
|
dossier["recognized_images_counts"] = len(
|
||||||
|
[
|
||||||
|
image
|
||||||
|
for image in dossier["images_layer"].values()
|
||||||
|
if image["image_recognized"] == "是"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
# 理算赔案并整合至赔案档案
|
# 理算赔案并整合至赔案档案
|
||||||
case_adjust(dossier=dossier)
|
case_adjust(dossier=dossier)
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ from decimal import Decimal, ROUND_HALF_UP
|
||||||
from hashlib import md5
|
from hashlib import md5
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import sys
|
import sys
|
||||||
from typing import Any, Dict, List
|
from typing import Any, Dict, List, Optional
|
||||||
|
|
||||||
sys.path.append(Path(__file__).parent.parent.as_posix())
|
sys.path.append(Path(__file__).parent.parent.as_posix())
|
||||||
from utils.sqlite import SQLite
|
from utils.sqlite import SQLite
|
||||||
|
|
@ -97,7 +97,7 @@ class MasterData(SQLite):
|
||||||
guid TEXT PRIMARY KEY,
|
guid TEXT PRIMARY KEY,
|
||||||
--理赔责任名称
|
--理赔责任名称
|
||||||
liability TEXT NOT NULL,
|
liability TEXT NOT NULL,
|
||||||
--出险事故
|
--理赔类型
|
||||||
accident TEXT NOT NULL,
|
accident TEXT NOT NULL,
|
||||||
--个人自费理算比例
|
--个人自费理算比例
|
||||||
personal_self_ratio TEXT NOT NULL,
|
personal_self_ratio TEXT NOT NULL,
|
||||||
|
|
@ -158,6 +158,20 @@ class MasterData(SQLite):
|
||||||
)
|
)
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
# 初始化票据理算表
|
||||||
|
self.execute(
|
||||||
|
sql="""
|
||||||
|
CREATE TABLE IF NOT EXISTS adjustments
|
||||||
|
(
|
||||||
|
--票据号
|
||||||
|
receipt_number TEXT PRIMARY KEY,
|
||||||
|
--剩余理算金额
|
||||||
|
remaining_adjustment_amount TEXT NOT NULL,
|
||||||
|
--理算时间
|
||||||
|
adjust_time TEXT NOT NULL
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
)
|
||||||
except Exception as exception:
|
except Exception as exception:
|
||||||
raise RuntimeError(f"初始化主数据发生异常:{str(exception)}") from exception
|
raise RuntimeError(f"初始化主数据发生异常:{str(exception)}") from exception
|
||||||
|
|
||||||
|
|
@ -309,7 +323,72 @@ class MasterData(SQLite):
|
||||||
except Exception as exception:
|
except Exception as exception:
|
||||||
raise RuntimeError(f"{str(exception)}") from exception
|
raise RuntimeError(f"{str(exception)}") from exception
|
||||||
|
|
||||||
def query_remaining_amount(
|
def query_remaining_adjustment_amount(
|
||||||
|
self,
|
||||||
|
receipt_number: str,
|
||||||
|
) -> Optional[Decimal]:
|
||||||
|
"""
|
||||||
|
根据票据号查询剩余理算金额
|
||||||
|
:param receipt_number: 票据号
|
||||||
|
:return: 剩余理算金额
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
with self:
|
||||||
|
result = self.query_one(
|
||||||
|
sql="""
|
||||||
|
SELECT remaining_adjustment_amount
|
||||||
|
FROM adjustments
|
||||||
|
WHERE receipt_number = ?
|
||||||
|
ORDER BY adjust_time DESC
|
||||||
|
LIMIT 1;
|
||||||
|
""",
|
||||||
|
parameters=(receipt_number,),
|
||||||
|
)
|
||||||
|
if not result:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return Decimal(result["remaining_adjustment_amount"]).quantize(
|
||||||
|
Decimal("0.00"),
|
||||||
|
rounding=ROUND_HALF_UP,
|
||||||
|
)
|
||||||
|
|
||||||
|
except Exception as exception:
|
||||||
|
raise RuntimeError(f"{str(exception)}") from exception
|
||||||
|
|
||||||
|
def add_ajustment(
|
||||||
|
self,
|
||||||
|
receipt_number: str,
|
||||||
|
remaining_adjustment_amount: Decimal,
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
新增理算记录
|
||||||
|
:param receipt_number: 票据号
|
||||||
|
:param remaining_adjustment_amount: 剩余理算金额
|
||||||
|
:return: 无
|
||||||
|
"""
|
||||||
|
if remaining_adjustment_amount < Decimal("0.00"):
|
||||||
|
raise ValueError("剩余理算金额小于0")
|
||||||
|
|
||||||
|
# 当前时间
|
||||||
|
current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")
|
||||||
|
|
||||||
|
with self:
|
||||||
|
if not self.execute(
|
||||||
|
sql="""
|
||||||
|
INSERT INTO adjustments
|
||||||
|
(receipt_number, remaining_adjustment_amount, adjust_time)
|
||||||
|
VALUES
|
||||||
|
(?, ?, ?)
|
||||||
|
""",
|
||||||
|
parameters=(
|
||||||
|
receipt_number,
|
||||||
|
f"{remaining_adjustment_amount:.2f}",
|
||||||
|
current_time,
|
||||||
|
),
|
||||||
|
):
|
||||||
|
raise RuntimeError("新增理算记录发生异常")
|
||||||
|
|
||||||
|
def query_after_change_amount(
|
||||||
self,
|
self,
|
||||||
person_policy_guid: str,
|
person_policy_guid: str,
|
||||||
) -> Decimal:
|
) -> Decimal:
|
||||||
|
|
@ -354,11 +433,6 @@ class MasterData(SQLite):
|
||||||
:param change_amount: 变动金额
|
:param change_amount: 变动金额
|
||||||
:return: 无
|
:return: 无
|
||||||
"""
|
"""
|
||||||
if before_change_amount != self.query_remaining_amount(
|
|
||||||
person_policy_guid=person_policy_guid,
|
|
||||||
):
|
|
||||||
raise ValueError("变动前金额不等于最新一条保额变动记录的变动后金额")
|
|
||||||
|
|
||||||
# 变动后金额
|
# 变动后金额
|
||||||
after_change_amount = (before_change_amount - change_amount).quantize(
|
after_change_amount = (before_change_amount - change_amount).quantize(
|
||||||
Decimal("0.00"),
|
Decimal("0.00"),
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="zh-CN">
|
<html lang="zh-CN">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>赔案档案</title>
|
<title>票据理赔自动化报告</title>
|
||||||
<style>
|
<style>
|
||||||
:root {
|
:root {
|
||||||
--arcoblue-1: #e8f3ff;
|
--arcoblue-1: #e8f3ff;
|
||||||
|
|
@ -176,7 +175,7 @@
|
||||||
|
|
||||||
.card-container {
|
.card-container {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
grid-template-columns: 1fr;
|
||||||
gap: var(--spacing-md);
|
gap: var(--spacing-md);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -206,8 +205,8 @@
|
||||||
/* 字段布局 */
|
/* 字段布局 */
|
||||||
.info-grid {
|
.info-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: repeat(3, 1fr);
|
||||||
gap: var(--spacing-sm);
|
gap: var(--spacing-md) var(--spacing-lg);
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -346,7 +345,7 @@
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
border-bottom: 1px solid var(--color-border);
|
border-bottom: 1px solid var(--color-border);
|
||||||
width: 20%;
|
width: 20%;
|
||||||
word-break: break-word;
|
word-break: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
tr:nth-child(even) {
|
tr:nth-child(even) {
|
||||||
|
|
@ -382,14 +381,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.card-container {
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
}
|
|
||||||
|
|
||||||
.invoice-details {
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-content {
|
.header-content {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
|
|
@ -398,6 +389,9 @@
|
||||||
.insurance-logo {
|
.insurance-logo {
|
||||||
margin-top: var(--spacing-md);
|
margin-top: var(--spacing-md);
|
||||||
}
|
}
|
||||||
|
.invoice-details {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
@ -407,159 +401,158 @@
|
||||||
<header>
|
<header>
|
||||||
<div class="header-content">
|
<div class="header-content">
|
||||||
<div>
|
<div>
|
||||||
<h1>赔案档案</h1>
|
<h1>{{ obj["report_layer"]["case_number"] }}理算报告</h1>
|
||||||
<div class="header-info">
|
<div class="header-info">
|
||||||
<p>
|
<p>
|
||||||
赔案编号: {{ dossier.report_layer.case_number }} | 保险分公司:
|
保险分公司: {{ obj["report_layer"]["insurer_company"] }} |
|
||||||
{{ dossier.report_layer.insurer_company }} | 报案时间: {{
|
报案时间: {{ obj["report_layer"]["report_time"] |
|
||||||
dossier.report_layer.report_time | DateTime }}
|
datetime_to_str }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div class="section">
|
<div class="section">
|
||||||
<h2>影像件层</h2>
|
<h2>影像件层</h2>
|
||||||
|
<div>
|
||||||
|
签收影像件{{ obj["report_layer"]["images_counts"] }}张,其中:已分类{{
|
||||||
|
obj["classified_images_counts"] }}张,已识别{{
|
||||||
|
obj["recognized_images_counts"] }}张
|
||||||
|
</div>
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>影像件唯一标识</th>
|
|
||||||
<th>影像件编号</th>
|
<th>影像件编号</th>
|
||||||
<th>影像件名称</th>
|
<th>影像件路径</th>
|
||||||
<th>影像件类型</th>
|
<th>影像件类型</th>
|
||||||
|
<th>已识别</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for image in dossier.images_layer %}
|
{% for image_index,image in obj["images_layer"].items() %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ image.image_guid }}</td>
|
<td>{{ image_index }}</td>
|
||||||
<td>{{ image.image_index }}</td>
|
<td>{{ image["image_relative_path"] }}</td>
|
||||||
<td>{{ image.image_name }}</td>
|
<td>{{ image["image_type"] }}</td>
|
||||||
<td>{{ image.image_type }}</td>
|
<td>{{ image["image_recognized"] }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="section">
|
<div class="section">
|
||||||
<h2>赔案层</h2>
|
<h2>赔案层</h2>
|
||||||
<div class="card-container">
|
<div class="card-container">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<h3>出险人(被保险人)信息</h3>
|
<h3>出险人(即被保险人)信息</h3>
|
||||||
<div class="info-grid">
|
<div class="info-grid">
|
||||||
<div class="info-item">
|
<div class="info-item">
|
||||||
<div class="info-label">姓名</div>
|
<div class="info-label">姓名</div>
|
||||||
<div class="info-value">
|
<div class="info-value">
|
||||||
{{ dossier.insured_person_layer.insured_person }}
|
{{ obj["insured_person_layer"]["insured_person"] }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-item">
|
<div class="info-item">
|
||||||
<div class="info-label">证件类型</div>
|
<div class="info-label">出生</div>
|
||||||
<div class="info-value">
|
<div class="info-value">
|
||||||
{{ dossier.insured_person_layer.identity_type }}
|
{{ obj["insured_person_layer"]["birth_date"] | datetime_to_str
|
||||||
</div>
|
}} | {{ obj["insured_person_layer"]["age"] }}岁
|
||||||
</div>
|
|
||||||
<div class="info-item">
|
|
||||||
<div class="info-label">证件号码</div>
|
|
||||||
<div class="info-value">
|
|
||||||
{{ dossier.insured_person_layer.identity_number }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="info-item">
|
|
||||||
<div class="info-label">证件有效期</div>
|
|
||||||
<div class="info-value">
|
|
||||||
{{ dossier.insured_person_layer.commencement_date | DateTime
|
|
||||||
}} 至 {{ dossier.insured_person_layer.termination_date |
|
|
||||||
DateTime }}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-item">
|
<div class="info-item">
|
||||||
<div class="info-label">性别</div>
|
<div class="info-label">性别</div>
|
||||||
<div class="info-value">
|
<div class="info-value">
|
||||||
{{ dossier.insured_person_layer.gender }}
|
{{ obj["insured_person_layer"]["gender"] }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="info-item">
|
<div class="info-item">
|
||||||
<div class="info-label">出生</div>
|
<div class="info-label">证件类型</div>
|
||||||
<div class="info-value">
|
<div class="info-value">
|
||||||
{{ dossier.insured_person_layer.birth_date | DateTime }} | {{
|
{{ obj["insured_person_layer"]["identity_type"] }}
|
||||||
dossier.insured_person_layer.age }}岁
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<div class="info-label">证件号码</div>
|
||||||
|
<div class="info-value">
|
||||||
|
{{ obj["insured_person_layer"]["identity_number"] }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="info-item">
|
||||||
|
<div class="info-label">证件有效期</div>
|
||||||
|
<div class="info-value">
|
||||||
|
{{ obj["insured_person_layer"]["commencement_date"] |
|
||||||
|
datetime_to_str }} 至 {{
|
||||||
|
obj["insured_person_layer"]["termination_date"] |
|
||||||
|
datetime_to_str }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-item">
|
<div class="info-item">
|
||||||
<div class="info-label">手机号</div>
|
<div class="info-label">手机号</div>
|
||||||
<div class="info-value">
|
<div class="info-value">
|
||||||
{{ dossier.insured_person_layer.phone_number }}
|
{{ obj["insured_person_layer"]["phone_number"] }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-item">
|
<div class="info-item">
|
||||||
<div class="info-label">住址</div>
|
<div class="info-label">住址</div>
|
||||||
<div class="info-value">
|
<div class="info-value">
|
||||||
{{ dossier.insured_person_layer.province }} {{
|
{{ obj["insured_person_layer"]["province"] }} {{
|
||||||
dossier.insured_person_layer.city }} {{
|
obj["insured_person_layer"]["city"] }} {{
|
||||||
dossier.insured_person_layer.district }}
|
obj["insured_person_layer"]["district"] }}
|
||||||
</div>
|
</div>
|
||||||
<div class="info-value">
|
<div class="info-value">
|
||||||
{{ dossier.insured_person_layer.detailed_address }}
|
{{ obj["insured_person_layer"]["detailed_address"] }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<h3>领款信息</h3>
|
<h3>领款信息</h3>
|
||||||
<div class="info-grid">
|
<div class="info-grid">
|
||||||
<div class="info-item">
|
<div class="info-item">
|
||||||
<div class="info-label">户名</div>
|
<div class="info-label">开户银行</div>
|
||||||
<div class="info-value">
|
<div class="info-value">
|
||||||
{{ dossier.insured_person_layer.account }}
|
{{ obj["insured_person_layer"]["account_bank"] }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-item">
|
<div class="info-item">
|
||||||
<div class="info-label">开户银行</div>
|
<div class="info-label">户名</div>
|
||||||
<div class="info-value">
|
<div class="info-value">
|
||||||
{{ dossier.insured_person_layer.account_bank }}
|
{{ obj["insured_person_layer"]["account"] }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="info-item">
|
<div class="info-item">
|
||||||
<div class="info-label">户号</div>
|
<div class="info-label">户号</div>
|
||||||
<div class="info-value">
|
<div class="info-value">
|
||||||
{{ dossier.insured_person_layer.account_number }}
|
{{ obj["insured_person_layer"]["account_number"] }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<h3>在保个单</h3>
|
<h3>在保个单</h3>
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>团单号</th>
|
<th>团单号</th>
|
||||||
<th>保险分公司名称</th>
|
|
||||||
<th>被保险人姓名</th>
|
|
||||||
<th>主被保险人</th>
|
<th>主被保险人</th>
|
||||||
|
<th>被保险人</th>
|
||||||
<th>与主被保险人关系</th>
|
<th>与主被保险人关系</th>
|
||||||
<th>保险期</th>
|
<th>保险期</th>
|
||||||
<th>理赔责任名称</th>
|
<th>理赔责任</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for liability in dossier.liabilities_layer %}
|
{% for liability in obj["liabilities_layer"] %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ liability.group_policy }}</td>
|
<td>{{ liability["group_policy"] }}</td>
|
||||||
<td>{{ liability.insurer_company }}</td>
|
<td>{{ liability["master_insured_person"] }}</td>
|
||||||
<td>{{ liability.insured_person }}</td>
|
<td>{{ liability["insured_person"] }}</td>
|
||||||
<td>{{ liability.master_insured_person }}</td>
|
<td>{{ liability["relationship"] }}</td>
|
||||||
<td>{{ liability.relationship }}</td>
|
|
||||||
<td>
|
<td>
|
||||||
{{ liability.commencement_date | DateTime }} 至 {{
|
{{ liability["commencement_date"] | datetime_to_str }} 至 {{
|
||||||
liability.termination_date | DateTime }}
|
liability["termination_date"] | datetime_to_str }}
|
||||||
</td>
|
</td>
|
||||||
<td>{{ liability.liability }}</td>
|
<td>{{ liability["liability"] }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
@ -567,126 +560,186 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="section">
|
||||||
|
<h2>结论层</h2>
|
||||||
|
<div class="invoice-card">
|
||||||
|
<div class="invoice-details">
|
||||||
|
<div class="detail-item">
|
||||||
|
<div class="detail-label">理赔结论</div>
|
||||||
|
<div class="detail-value">
|
||||||
|
{{ obj["adjustment_layer"]["conclusion"] }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="detail-item">
|
||||||
|
<div class="detail-label">理算金额</div>
|
||||||
|
<div class="detail-value">
|
||||||
|
{{ obj["adjustment_layer"]["adjustment_amount"] }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="detail-item">
|
||||||
|
<div class="detail-label">结论说明</div>
|
||||||
|
<div class="detail-value">
|
||||||
|
{{ obj["adjustment_layer"]["explanation"] }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="section">
|
<div class="section">
|
||||||
<h2>票据层</h2>
|
<h2>票据层</h2>
|
||||||
{% for receipt in dossier.receipts_layer %}
|
{% for receipt in obj["receipts_layer"] %}
|
||||||
<div class="invoice-card">
|
<div class="invoice-card">
|
||||||
<div class="invoice-header">
|
<div class="invoice-header">
|
||||||
<div class="invoice-number-container">
|
<div class="invoice-number-container">
|
||||||
<div class="invoice-number">{{ receipt.receipt_number }}</div>
|
<div class="invoice-number">{{ receipt["number"] }}</div>
|
||||||
<span class="invoice-reference">关联影像件序号: {{ receipt.image_index }}</span>
|
<span class="invoice-reference"
|
||||||
|
>关联影像件:
|
||||||
|
<a
|
||||||
|
href="file:///{{ receipt.image_path | replace('\\', '/') }}"
|
||||||
|
target="_blank"
|
||||||
|
>{{ receipt["image_index"] }}</a
|
||||||
|
></span
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="invoice-verification">
|
<div class="invoice-verification">
|
||||||
{% if receipt.verification == '真票' %}
|
{% if receipt["verification"] == '真票' %}
|
||||||
<span class="verification-tag valid">{{ receipt.verification }}</span>
|
<span class="verification-tag valid"
|
||||||
{% elif receipt.verification == '无法查验' %}
|
>{{ receipt["verification"] }}</span
|
||||||
<span class="verification-tag suspicious">{{ receipt.verification }}</span>
|
>
|
||||||
|
{% elif receipt["verification"] == '无法查验' %}
|
||||||
|
<span class="verification-tag suspicious"
|
||||||
|
>{{ receipt["verification"] }}</span
|
||||||
|
>
|
||||||
{% else %}
|
{% else %}
|
||||||
<span class="verification-tag invalid">{{ receipt.verification }}</span>
|
<span class="verification-tag invalid"
|
||||||
|
>{{ receipt["verification"] }}</span
|
||||||
|
>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="invoice-details">
|
<div class="invoice-details">
|
||||||
<div class="detail-item">
|
<div class="detail-item">
|
||||||
<div class="detail-label">出险人</div>
|
<div class="detail-label">出险人</div>
|
||||||
<div class="detail-value">{{ receipt.payer }}</div>
|
<!-- 出险人可为空值 -->
|
||||||
|
<div class="detail-value">{{ receipt["payer"] | str_to_str}}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="detail-item">
|
<div class="detail-item">
|
||||||
<div class="detail-label">票据代码</div>
|
<div class="detail-label">票据代码</div>
|
||||||
<div class="detail-value">{{ receipt.code }}</div>
|
<!-- 票据代码可为空值 -->
|
||||||
|
<div class="detail-value">{{ receipt["code"] | str_to_str}}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="detail-item">
|
<div class="detail-item">
|
||||||
<div class="detail-label">校验码后六位</div>
|
<div class="detail-label">校验码</div>
|
||||||
<div class="detail-value">{{ receipt.verification_code }}</div>
|
<div class="detail-value">{{ receipt["check_code"] }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="detail-item">
|
<div class="detail-item">
|
||||||
<div class="detail-label">开票日期</div>
|
<div class="detail-label">开票日期</div>
|
||||||
<div class="detail-value">{{ receipt.date | DateTime }}</div>
|
<div class="detail-value">
|
||||||
|
{{ receipt["date"] | datetime_to_str }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="detail-item">
|
<div class="detail-item">
|
||||||
<div class="detail-label">票据金额</div>
|
<div class="detail-label">开票金额</div>
|
||||||
<div class="detail-value">{{ receipt.amount }}元</div>
|
<div class="detail-value">{{ receipt["amount"] }}元</div>
|
||||||
</div>
|
|
||||||
<div class="detail-item">
|
|
||||||
<div class="detail-label">出险事故</div>
|
|
||||||
<div class="detail-value">{{ receipt.accident }}</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="detail-item">
|
<div class="detail-item">
|
||||||
<div class="detail-label">购药及就医机构</div>
|
<div class="detail-label">购药及就医机构</div>
|
||||||
<div class="detail-value">
|
<div class="detail-value">
|
||||||
{{ receipt.institution }} | {{ receipt.institution_type }}
|
{{ receipt["institution_type"] }} | {{ receipt["institution"] }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="detail-item">
|
<div class="detail-item">
|
||||||
<div class="detail-label">医疗诊断</div>
|
<div class="detail-label">医疗诊断</div>
|
||||||
<div class="detail-value">{{ receipt.diagnosis }}</div>
|
<div class="detail-value">{{ receipt["diagnosis"] }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>类别</th>
|
|
||||||
<th>药品/医疗服务</th>
|
<th>药品/医疗服务</th>
|
||||||
<th>数量</th>
|
<th>数量</th>
|
||||||
<th>金额</th>
|
<th>金额</th>
|
||||||
|
<th>个人自费项</th>
|
||||||
|
<th>个人自付项</th>
|
||||||
|
<th>合理项</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for item in receipt.items %}
|
{% for item in receipt["items"] %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ item.category }}</td>
|
<td>{{ item["category"] }} | {{ item["medicine"] }}</td>
|
||||||
<td>{{ item.medicine }}</td>
|
<td>{{ item["quantity"] }}</td>
|
||||||
<td>{{ item.quantity }}</td>
|
<td>{{ item["amount"] }}元</td>
|
||||||
<td>{{ item.amount }}元</td>
|
<td>{{ item["personal_self_payment"] }}元</td>
|
||||||
|
<td>{{ item["non_medical_payment"] }}元</td>
|
||||||
|
<td>{{ item["reasonable_amount"] }}元</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
<div class="invoice-details">
|
||||||
|
<div class="detail-item">
|
||||||
|
<div class="detail-label">理赔类型</div>
|
||||||
|
<div class="detail-value">{{ receipt["accident"] }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="detail-item">
|
||||||
|
<div class="detail-label">个人自费金额</div>
|
||||||
|
<div class="detail-value">
|
||||||
|
{{ receipt["personal_self_payment"] }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="detail-item">
|
||||||
|
<div class="detail-label">个人自付金额</div>
|
||||||
|
<div class="detail-value">
|
||||||
|
{{ receipt["non_medical_payment"] }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="detail-item">
|
||||||
|
<div class="detail-label">合理金额</div>
|
||||||
|
<div class="detail-value">{{ receipt["reasonable_amount"] }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
<th>剩余理算金额</th>
|
||||||
|
<th>团单号</th>
|
||||||
|
<th>个单号</th>
|
||||||
|
<th>个单剩余保额</th>
|
||||||
|
<th>理赔责任</th>
|
||||||
<th>个人自费可理算金额</th>
|
<th>个人自费可理算金额</th>
|
||||||
<th>个人自付可理算金额</th>
|
<th>个人自付可理算金额</th>
|
||||||
<th>合理可理算金额</th>
|
<th>合理可理算金额</th>
|
||||||
|
<th>可理算金额</th>
|
||||||
<th>理算金额</th>
|
<th>理算金额</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for adjustment in receipt.adjustments %}
|
{% for adjustment in receipt["adjustments"] %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>{{ adjustment["remaining_adjustment_amount"] }}</td>
|
||||||
{{ adjustment.personal_self_adjustable_amount }} = {{
|
<td>{{ adjustment["group_policy"] }}</td>
|
||||||
adjustment.personal_self_payment }} * {{
|
<td>{{ adjustment["person_policy"] }}</td>
|
||||||
adjustment.personal_self_ratio }} %
|
<td>{{ adjustment["remaining_coverage_amount"] }}</td>
|
||||||
</td>
|
<td>{{ adjustment["liability"] }}</td>
|
||||||
<td>
|
<td>{{ adjustment["personal_self_adjustable_amount"] }}</td>
|
||||||
{{ adjustment.non_medical_adjustable_amount }} = {{
|
<td>{{ adjustment["non_medical_adjustable_amount"] }}</td>
|
||||||
adjustment.non_medical_payment }} * {{
|
<td>{{ adjustment["reasonable_adjustable_amount"] }}</td>
|
||||||
adjustment.non_medical_ratio }} %
|
<td>{{ adjustment["adjustable_amount"] }}</td>
|
||||||
</td>
|
<td>{{ adjustment["adjustment_amount"] }}</td>
|
||||||
<td>
|
|
||||||
{{ adjustment.reasonable_adjustable_amount }} = {{
|
|
||||||
adjustment.reasonable_amount }} * {{
|
|
||||||
adjustment.reasonable_ratio }} %
|
|
||||||
</td>
|
|
||||||
<td>{{ adjustment.adjustment_amount }}元</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div class="amount-total">总金额: {{ receipt.amount }}元</div>
|
<div class="amount-total">
|
||||||
|
理算金额合计: {{ receipt["adjustment_amount"] }}元
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
<p>@liubiren.cloud</p>
|
<p>@liubiren.cloud</p>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
Loading…
Reference in New Issue