diff --git a/.isort.cfg b/.isort.cfg
deleted file mode 100644
index eb008e9..0000000
--- a/.isort.cfg
+++ /dev/null
@@ -1,4 +0,0 @@
-[settings]
-order_by_type = true
-multi_line_output = 3
-indent = " "
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..ff5300e
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+ "python.languageServer": "None"
+}
\ No newline at end of file
diff --git a/utils/authenticator.py b/utils/authenticator.py
index e376b38..a14934b 100644
--- a/utils/authenticator.py
+++ b/utils/authenticator.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""
-认证器
+认证器模块
"""
import hashlib
diff --git a/票据理赔自动化/case.py b/票据理赔自动化/case.py
index 04dfeae..b5345bc 100644
--- a/票据理赔自动化/case.py
+++ b/票据理赔自动化/case.py
@@ -1,10 +1,13 @@
# -*- coding: utf-8 -*-
"""通用模块"""
-from typing import Any, Dict, List
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
@@ -28,17 +31,75 @@ def case_adjust(dossier: Dict[str, Any]) -> None:
return
# 赔案理算记录
- adjustments = (
- pandas.DataFrame(data=dossier["receipts_layer"]).assing(
- adjustments=lambda dataframe: dataframe.apply(
- lambda row: receipt_adjust(
- row=row, liabilities=dossier["liabilities_layer"]
- ),
- axis="columns",
- ) # 票据理算
+ 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,
+ }
+ )
)
- ).explode("adjustments", ignore_index=True)
- print(adjustments)
def receipt_adjust(
@@ -46,72 +107,140 @@ def receipt_adjust(
) -> List[Dict[str, Any]]:
"""
理算票据
- :param row: 一张票据数据
+ :param row: 票据数据
:param liabilities: 理算责任
:return: 理算记录
"""
# 初始化票据理算记录
- adjustments = []
- # 初始化剩余个人自费金额
- remaining_personal_self_payment = row["personal_self_payment"]
- # 初始化剩余个人自付金额
- remaining_non_medical_payment = row["non_medical_payment"]
- # 初始化剩余合理金额
- remaining_reasonable_amount = row["reasonable_amount"]
+ receipt_adjustments = []
- # 出险事故
- accident = row["accident"]
- # 出险人
- accident_person = row["payer"]
- # 出险日期
- accident_date = row["date"]
+ # 初始化票据剩余可理算金额
+ 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,
+ )
- # 查验状态
- verification = row["verification"]
-
- # 初始化理赔责任理算金额
- adjustment_amount = Decimal("0.00")
- # 初始化理赔责任理赔金额
- claim_amount = Decimal("0.00")
-
- # 遍历所有理赔责任,根据出险事故、出险人、出险日期和查验状态匹配责任
+ # 遍历所有理赔责任,根据出险人、出险事故、查验状态和出险日期匹配理赔责任
for liability in liabilities:
if (
- accident == liability["accident"]
- and accident_person == liability["insured_person"]
+ row["payer"] in [liability["insured_person"] for liability in liabilities]
+ and row["accident"] == liability["accident"]
+ and row["verification"] in ["真票", "无法查验"]
and liability["commencement_date"]
- <= accident_date
+ <= row["date"]
<= liability["termination_date"]
- and verification == "真票"
):
- # 理赔责任理算金额
- adjustment_amount = (
- row["personal_self_payment"]
- * liability["personal_self_ratio"] # 个人自费金额
- + row["non_medical_payment"]
- * liability["non_medical_ratio"] # 个人自付金额
- + row["reasonable_amount"] * liability["reasonable_ratio"] # 合理金额
- ).quantize(
- Decimal("0.00"),
- rounding=ROUND_HALF_UP,
- )
-
- # 据变动保单唯一标识查询最新一条保额变动记录的变动后金额(理赔责任的理赔保单余额)
+ # 个单余额
remaining_amount = masterdata.query_remaining_amount(
- policy_guid=liability["policy_guid"],
- )
- # 理赔责任理赔金额
- claim_amount = min(
- remaining_amount,
- adjustment_amount,
+ person_policy_guid=liability["person_policy_guid"],
)
- # 初始化票据理算记录
- adjustment = {
- "liability": liability["liability"], # 理赔责任名称
- "type": row["就诊类型"],
- "amount": row["合理金额"],
- "payable": 0.0,
+ # 个人自费可理算金额
+ 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 adjustments
+ return receipt_adjustments
diff --git a/票据理赔自动化/common.py b/票据理赔自动化/common.py
index 552b8bc..db6d92a 100644
--- a/票据理赔自动化/common.py
+++ b/票据理赔自动化/common.py
@@ -1,14 +1,15 @@
# -*- coding: utf-8 -*-
"""通用模块"""
-import sys
from pathlib import Path
+import sys
from masterdata import MasterData
sys.path.append(Path(__file__).parent.parent.as_posix())
from utils.rules_engine import RulesEngine
+
# 实例化主数据
masterdata = MasterData(database=Path(__file__).parent / "database.db")
diff --git a/票据理赔自动化/database.db b/票据理赔自动化/database.db
index ec40379..822f3e1 100644
Binary files a/票据理赔自动化/database.db and b/票据理赔自动化/database.db differ
diff --git a/票据理赔自动化/dossiers/254728869001.html b/票据理赔自动化/dossiers/254728869001.html
index bb78b8a..e69de29 100644
--- a/票据理赔自动化/dossiers/254728869001.html
+++ b/票据理赔自动化/dossiers/254728869001.html
@@ -1,1509 +0,0 @@
-
-
-
-
-
- 赔案档案
-
-
-
-
-
-
-
-
影像件层
-
-
-
- | 影像件序号 |
- 影像件名称 |
- 已分类(含旋正) |
- 影像件类型 |
- 已识别 |
-
-
-
-
-
- | 01 |
- application_1.jpg |
- 是 |
- 理赔申请书 |
- 是 |
-
-
-
- | 02 |
- beneficiary_1.jpg |
- 是 |
- 银行卡 |
- 否,无需识别 |
-
-
-
- | 03 |
- insurant_1.jpg |
- 是 |
- 居民身份证(正背面) |
- 是 |
-
-
-
- | 04 |
- invoice_1.jpg |
- 是 |
- 增值税发票 |
- 是 |
-
-
-
- | 05 |
- invoice_10.jpg |
- 是 |
- 增值税发票 |
- 是 |
-
-
-
- | 06 |
- invoice_11.jpg |
- 是 |
- 增值税发票 |
- 是 |
-
-
-
- | 07 |
- invoice_12.jpg |
- 是 |
- 增值税发票 |
- 是 |
-
-
-
- | 08 |
- invoice_2.jpg |
- 是 |
- 增值税发票 |
- 是 |
-
-
-
- | 09 |
- invoice_3.jpg |
- 是 |
- 增值税发票 |
- 是 |
-
-
-
- | 10 |
- invoice_4.jpg |
- 是 |
- 增值税发票 |
- 是 |
-
-
-
- | 11 |
- invoice_5.jpg |
- 是 |
- 增值税发票 |
- 是 |
-
-
-
- | 12 |
- invoice_6.jpg |
- 是 |
- 增值税发票 |
- 是 |
-
-
-
- | 13 |
- invoice_7.jpg |
- 是 |
- 增值税发票 |
- 是 |
-
-
-
- | 14 |
- invoice_8.jpg |
- 是 |
- 增值税发票 |
- 是 |
-
-
-
- | 15 |
- invoice_9.jpg |
- 是 |
- 增值税发票 |
- 是 |
-
-
-
-
-
-
-
-
赔案层
-
-
-
申请人信息
-
-
-
-
-
-
证件号码
-
320602197401310029
-
-
-
证件有效期
-
2016-06-28 至 2036-06-28
-
-
-
-
-
出生
-
1974-01-31 | 51岁
-
-
-
-
-
住址
-
江苏省 苏州市 昆山市
-
-
玉山镇许文塘新村45幢301室
-
-
-
-
-
-
受益人信息
-
-
-
-
-
-
银行账号
-
6217566101001926753
-
-
-
-
-
-
-
-
发票层
-
-
-
-
-
-
-
-
-
-
-
-
-
医药机构
-
苏州雷允上国药连锁总店有限公司
-
-
-
-
-
-
-
- | 大项 |
- 小项 |
- 数量 |
- 金额 |
-
-
-
-
-
- | 化学药品制剂 |
- 塞来昔布胶囊 |
- 2.00 |
- 53.88元 |
-
-
-
-
-
总金额: 53.88元
-
-
-
-
-
-
-
-
-
-
-
-
-
-
医药机构
-
荥阳市药之佳缘大药房有限公司
-
-
-
-
-
-
-
- | 大项 |
- 小项 |
- 数量 |
- 金额 |
-
-
-
-
-
- | 中成药 |
- 香菊感冒颗粒 |
- 2.00 |
- 35.00元 |
-
-
-
- | 化学药品制剂 |
- 感冒灵颗粒 |
- 1.00 |
- 15.80元 |
-
-
-
-
-
总金额: 50.80元
-
-
-
-
-
-
-
-
-
-
- | 大项 |
- 小项 |
- 数量 |
- 金额 |
-
-
-
-
-
- | 化学药品制剂 |
- 塞来昔布胶囊0.2g*18粒/盒 |
- 1.00 |
- 69.00元 |
-
-
-
-
-
总金额: 69.00元
-
-
-
-
-
-
-
-
-
-
- | 大项 |
- 小项 |
- 数量 |
- 金额 |
-
-
-
-
-
- | 化学药品制剂 |
- 吗替麦考酚酯胶囊 |
- 10.00 |
- 607.00元 |
-
-
-
-
-
总金额: 607.00元
-
-
-
-
-
-
-
-
-
-
- | 大项 |
- 小项 |
- 数量 |
- 金额 |
-
-
-
-
-
- | 化学药品制剂 |
- 吗替麦考酚酯胶囊 |
- 10.00 |
- 607.00元 |
-
-
-
-
-
总金额: 607.00元
-
-
-
-
-
-
-
-
-
-
- | 大项 |
- 小项 |
- 数量 |
- 金额 |
-
-
-
-
-
- | 化学药品制剂 |
- 玻璃酸钠滴眼液0.1%*10ml/支/盒 |
- 2.00 |
- 119.56元 |
-
-
-
-
-
总金额: 119.56元
-
-
-
-
-
-
-
-
-
-
- | 大项 |
- 小项 |
- 数量 |
- 金额 |
-
-
-
-
-
- | 化学药品制剂 |
- 玻璃酸钠滴眼液0.1%*10ml/支/盒 |
- 2.00 |
- 123.48元 |
-
-
-
-
-
总金额: 123.48元
-
-
-
-
-
-
-
-
-
-
- | 大项 |
- 小项 |
- 数量 |
- 金额 |
-
-
-
-
-
- | 医疗仪器器械 |
- 口腔溃疡含漱液40ml |
- 1.00 |
- 13.00元 |
-
-
-
- | 营养保健食品 |
- Swisse钙维生素D片 |
- 6.00 |
- 467.80元 |
-
-
-
-
-
总金额: 480.80元
-
-
-
-
-
-
-
-
-
-
- | 大项 |
- 小项 |
- 数量 |
- 金额 |
-
-
-
-
-
- | 营养保健食品 |
- SwisseSwisse斯维诗鱼油软胶囊160g0.8g/粒*200粒 |
- 1.00 |
- 146.90元 |
-
-
-
- | 营养保健食品 |
- Swisse钙维生素D片 |
- 2.00 |
- 152.60元 |
-
-
-
-
-
总金额: 299.50元
-
-
-
-
-
-
-
-
-
-
- | 大项 |
- 小项 |
- 数量 |
- 金额 |
-
-
-
-
-
- | 化学药品制剂 |
- 海德威骨化三醇软胶囊 |
- 5.00 |
- 131.50元 |
-
-
-
-
-
总金额: 131.50元
-
-
-
-
-
-
-
-
-
-
- | 大项 |
- 小项 |
- 数量 |
- 金额 |
-
-
-
-
-
- | 中成药 |
- 麝香痔疮栓 |
- 10.00 |
- 156.20元 |
-
-
-
-
-
总金额: 156.20元
-
-
-
-
-
-
-
-
-
-
- | 大项 |
- 小项 |
- 数量 |
- 金额 |
-
-
-
-
-
- | 中成药 |
- 麝香痔疮栓 |
- 6.00 |
- 103.20元 |
-
-
-
-
-
总金额: 103.20元
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/票据理赔自动化/image.py b/票据理赔自动化/image.py
index c428b05..214f3c2 100644
--- a/票据理赔自动化/image.py
+++ b/票据理赔自动化/image.py
@@ -3,27 +3,27 @@
影像件处理模块
"""
-import json
-import re
-import sys
from base64 import b64encode
from datetime import datetime
-from decimal import ROUND_HALF_UP, Decimal
+from decimal import Decimal, ROUND_HALF_UP
from hashlib import md5
+import json
from pathlib import Path
+import re
+import sys
from typing import Any, Dict, List, Optional, Tuple
import cv2
-import numpy
-import pandas
-from common import masterdata, rules_engine
from fuzzywuzzy import fuzz
from jionlp import parse_location
+import numpy
+import pandas
-from utils.authenticator import Authenticator
-from utils.request import Request
+from common import masterdata, rules_engine
sys.path.append(Path(__file__).parent.parent.as_posix())
+from utils.authenticator import Authenticator
+from utils.request import Request
# 实例化认证器
@@ -139,12 +139,10 @@ def image_read(
:return: 影像件图像数组
"""
try:
- with open(image_path, "rb") as file:
- image_bytes = file.read() # 读取影像件字节流
-
- # 先将影像件字节流转为 numpy.ndarray 对象,再解码为单通道灰度图数组对象
+ # 先使用读取影像件,再解码为单通道灰度图数组对象
image_ndarray = cv2.imdecode(
- buf=numpy.frombuffer(image_bytes, numpy.uint8), flags=cv2.IMREAD_GRAYSCALE
+ buf=numpy.fromfile(file=image_path, dtype=numpy.uint8),
+ flags=cv2.IMREAD_GRAYSCALE,
)
if image_ndarray is None:
raise RuntimeError(f"影像件不存在")
@@ -600,7 +598,9 @@ def receipt_recognize(
"token": authenticator.get_token(servicer="szkt"), # 获取深圳快瞳访问令牌
"imgBase64": f"data:image/{image["image_format"].lstrip(".")};base64,{image["image_base64"]}", # 将影像件格式和BASE64编码嵌入数据统一资源标识符
},
- guid=md5(string=(url + image["image_guid"]).encode("utf-8")).hexdigest().upper(),
+ guid=md5(string=(url + image["image_guid"]).encode("utf-8"))
+ .hexdigest()
+ .upper(),
)
# 若查验状态为真票或红票则直接整合至赔案档案
if response.get("status") == 200 and response.get("code") == 10000:
diff --git a/票据理赔自动化/main.py b/票据理赔自动化/main.py
index 6694a62..fa80064 100644
--- a/票据理赔自动化/main.py
+++ b/票据理赔自动化/main.py
@@ -10,7 +10,6 @@ from pathlib import Path
from case import case_adjust
from image import image_classify, image_recognize
-from jinja2 import Environment, FileSystemLoader
if __name__ == "__main__":
# 初始化文件路径
@@ -20,27 +19,18 @@ if __name__ == "__main__":
folder_path = file_path / "directory"
folder_path.mkdir(parents=True, exist_ok=True) # 若文件夹路径不存在则创建
- # 实例化JINJA2环境
- environment = Environment(loader=FileSystemLoader(file_path))
- # 添加DATE过滤器
- environment.filters["date"] = lambda date: (
- date.strftime("%Y-%m-%d") if date else "长期"
- )
- # 加载赔案档案模版
- template = environment.get_template("template.html")
-
# 遍历文件夹中赔案文件夹并创建赔案档案
for case_path in [x for x in folder_path.iterdir() if x.is_dir()]:
# 初始化赔案档案(推送至TPA时,保险公司会提保险分公司名称、报案时间和影像件等,TPA签收后生成赔案号)
dossier = {
"report_layer": {
- "report_time": datetime(
- 2025, 7, 25, 12, 0, 0
- ), # 指定报案时间,默认为 datetime对象
"case_number": case_path.stem, # 默认为赔案文件夹名称
"insurer_company": (
insurer_company := "中银保险有限公司苏州分公司"
), # 默认为中银保险有限公司苏州分公司
+ "report_time": datetime(
+ 2025, 7, 25, 12, 0, 0
+ ), # 指定报案时间,默认为 datetime对象
}, # 报案层
"images_layer": [], # 影像件层
"insured_person_layer": {}, # 出险人层
diff --git a/票据理赔自动化/masterdata.py b/票据理赔自动化/masterdata.py
index 83675a0..9ea0247 100644
--- a/票据理赔自动化/masterdata.py
+++ b/票据理赔自动化/masterdata.py
@@ -3,11 +3,12 @@
主数据模块
"""
-import sys
from datetime import datetime
-from decimal import ROUND_HALF_UP, Decimal
+from decimal import Decimal, ROUND_HALF_UP
+from hashlib import md5
from pathlib import Path
-from typing import Any, Dict, List, Optional
+import sys
+from typing import Any, Dict, List
sys.path.append(Path(__file__).parent.parent.as_posix())
from utils.sqlite import SQLite
@@ -104,8 +105,6 @@ class MasterData(SQLite):
non_medical_ratio TEXT NOT NULL,
--合理理算比例
reasonable_ratio TEXT NOT NULL,
- --理赔保单唯一标识
- claim_policy_guid TEXT NOT NULL,
--个单唯一标识
person_policy_guid TEXT NOT NULL
)
@@ -128,8 +127,8 @@ class MasterData(SQLite):
after_change_amount TEXT NOT NULL,
--变动时间
change_time TEXT NOT NULL,
- --变动保单唯一标识
- change_policy_guid TEXT NOT NULL
+ --个单唯一标识
+ person_policy_guid TEXT NOT NULL
)
"""
)
@@ -200,7 +199,7 @@ class MasterData(SQLite):
liabilities.personal_self_ratio,
liabilities.non_medical_ratio,
liabilities.reasonable_ratio,
- liabilities.claim_policy_guid
+ liabilities.person_policy_guid
FROM insured_persons
INNER JOIN person_policies
ON insured_persons.person_policy_guid = person_policies.guid
@@ -212,10 +211,10 @@ class MasterData(SQLite):
INNER JOIN liabilities
ON person_policies.guid = liabilities.person_policy_guid
INNER JOIN coverage_changes
- ON liabilities.claim_policy_guid = coverage_changes.change_policy_guid
+ ON liabilities.person_policy_guid = coverage_changes.person_policy_guid
AND coverage_changes.change_time = (SELECT MAX(change_time)
FROM coverage_changes
- WHERE liabilities.claim_policy_guid = change_policy_guid)
+ WHERE liabilities.person_policy_guid = person_policy_guid)
WHERE group_policies.insurer_company = ?
AND insured_persons.insured_person = ?
AND insured_persons.identity_type = ?
@@ -312,11 +311,11 @@ class MasterData(SQLite):
def query_remaining_amount(
self,
- policy_guid: str,
+ person_policy_guid: str,
) -> Decimal:
"""
- 根据变动保单唯一标识查询最新一条保额变动记录的变动后金额
- :param policy_guid: 变动保单唯一标识
+ 根据个单唯一标识查询最新一条保额变动记录的变动后金额
+ :param person_policy_guid: 个单唯一标识
:return: 变动后金额
"""
try:
@@ -325,11 +324,11 @@ class MasterData(SQLite):
sql="""
SELECT after_change_amount
FROM coverage_changes
- WHERE change_policy_guid = ?
+ WHERE person_policy_guid = ?
ORDER BY change_time DESC
LIMIT 1;
""",
- parameters=(policy_guid,),
+ parameters=(person_policy_guid,),
)
if not result:
raise RuntimeError("查无数据")
@@ -341,3 +340,63 @@ class MasterData(SQLite):
except Exception as exception:
raise RuntimeError(f"{str(exception)}") from exception
+
+ def add_coverage_change(
+ self,
+ person_policy_guid: str,
+ before_change_amount: Decimal,
+ change_amount: Decimal,
+ ) -> None:
+ """
+ 新增保额扣减记录
+ :param person_policy_guid: 个单唯一标识
+ :param before_change_amount: 变动前金额
+ :param change_amount: 变动金额
+ :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(
+ Decimal("0.00"),
+ rounding=ROUND_HALF_UP,
+ )
+ if after_change_amount < Decimal("0.00"):
+ raise ValueError("变动后金额小于0")
+
+ # 当前时间
+ current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")
+
+ # 构建保额变动唯一标识
+ guid = (
+ md5(
+ string=f"{person_policy_guid} 保额扣减 {before_change_amount:.2f} {change_amount:.2f} {after_change_amount:.2f} {current_time}".encode(
+ "utf-8"
+ )
+ )
+ .hexdigest()
+ .upper()
+ )
+
+ with self:
+ if not self.execute(
+ sql="""
+ INSERT INTO coverage_changes
+ (guid, change_type, before_change_amount, change_amount, after_change_amount, change_time, person_policy_guid)
+ VALUES
+ (?, ?, ?, ?, ?, ?, ?)
+ """,
+ parameters=(
+ guid,
+ "保额扣减",
+ f"{before_change_amount:.2f}",
+ f"{change_amount:.2f}",
+ f"{after_change_amount:.2f}",
+ current_time,
+ person_policy_guid,
+ ),
+ ):
+ raise RuntimeError("新增保额扣减记录发生异常")
diff --git a/票据理赔自动化/template.html b/票据理赔自动化/template.html
index e0355b9..5a6d11e 100644
--- a/票据理赔自动化/template.html
+++ b/票据理赔自动化/template.html
@@ -1,602 +1,697 @@
-
-
-
+
+
+
赔案档案
-
-
-