Files
zkxw/backend/app/services/sample_payment_progress.py
T
zlwuandCursor 47f873a3e6 feat(backend): add sample payment progress aggregation helper
Pure function for per-sample list progressStatus, paid/due totals, and canConfirm.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-05 12:54:48 +08:00

83 lines
2.4 KiB
Python

"""样本缴费方案进度汇总(列表「按样本」维度)"""
from __future__ import annotations
from decimal import Decimal
from typing import Any, Dict, Iterable, List, Mapping
def _dec(v: Any) -> Decimal:
if v is None or v == "":
return Decimal("0")
return Decimal(str(v))
def _status(row: Mapping[str, Any], *keys: str) -> str:
for k in keys:
if k in row and row[k] is not None:
return str(row[k]).strip()
return ""
def compute_sample_payment_progress(
*,
schedules: Iterable[Mapping[str, Any]],
transfers: Iterable[Mapping[str, Any]],
) -> Dict[str, Any]:
"""
progressStatus 优先级:jfqrz > yjq > bfyj > dfj
已缴仅计 pay_status=yjf;有效期排除 schedule_status=void
canConfirm:无 jfqrz,且存在有效未清期(欠缴>0)或可编辑的 dfj 草稿
"""
sch_list: List[Mapping[str, Any]] = list(schedules or [])
tr_list: List[Mapping[str, Any]] = list(transfers or [])
active = [
s
for s in sch_list
if _status(s, "scheduleStatus", "schedule_status") != "void"
]
active_schedule_count = len(active)
cleared_schedule_count = sum(
1
for s in active
if _status(s, "scheduleStatus", "schedule_status") == "cleared"
)
paid = sum(
(
_dec(t.get("transferAmount", t.get("transfer_amount")))
for t in tr_list
if _status(t, "payStatus", "pay_status") == "yjf"
),
Decimal("0"),
)
due_total = sum((_dec(s.get("dueAmount", s.get("due_amount"))) for s in active), Decimal("0"))
due_remain = due_total - paid
if due_remain < 0:
due_remain = Decimal("0")
has_jfqrz = any(_status(t, "payStatus", "pay_status") == "jfqrz" for t in tr_list)
if has_jfqrz:
progress = "jfqrz"
elif active_schedule_count > 0 and cleared_schedule_count == active_schedule_count:
progress = "yjq"
elif paid > 0:
progress = "bfyj"
else:
progress = "dfj"
can_confirm = (not has_jfqrz) and any(
_status(s, "scheduleStatus", "schedule_status") != "cleared" for s in active
)
return {
"progressStatus": progress,
"paidAmount": paid,
"dueRemainAmount": due_remain,
"clearedScheduleCount": cleared_schedule_count,
"activeScheduleCount": active_schedule_count,
"canConfirm": can_confirm,
}