| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108 |
- """样本缴费方案进度汇总(列表「按样本」维度)"""
- 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 _schedule_id(row: Mapping[str, Any]) -> str:
- return str(row.get("id") or row.get("scheduleId") or row.get("schedule_id") or "").strip()
- def _transfer_schedule_id(row: Mapping[str, Any]) -> str:
- return str(row.get("scheduleId") or row.get("schedule_id") or "").strip()
- def is_schedule_effectively_cleared(
- schedule: Mapping[str, Any],
- *,
- confirmed_paid: Decimal,
- ) -> bool:
- """
- 批次已缴清:显式 cleared,或财务确认实缴(yjf)已覆盖应缴。
- 兼容历史数据未回写 schedule_status 的情况。
- """
- if _status(schedule, "scheduleStatus", "schedule_status") == "cleared":
- return True
- due = _dec(schedule.get("dueAmount", schedule.get("due_amount")))
- return confirmed_paid >= due
- def compute_sample_payment_progress(
- *,
- schedules: Iterable[Mapping[str, Any]],
- transfers: Iterable[Mapping[str, Any]],
- ) -> Dict[str, Any]:
- """
- progressStatus 优先级:yjq > bfyj > dfj(不含付款确认中)
- 已缴仅计 pay_status=yjf;有效期排除 schedule_status=void
- 已缴清期:schedule_status=cleared,或该期 yjf 合计 >= 应缴
- canConfirm:无 jfqrz,且存在有效未清期
- """
- sch_list: List[Mapping[str, Any]] = list(schedules or [])
- tr_list: List[Mapping[str, Any]] = list(transfers or [])
- paid_by_schedule: Dict[str, Decimal] = {}
- paid = Decimal("0")
- for t in tr_list:
- if _status(t, "payStatus", "pay_status") != "yjf":
- continue
- amount = _dec(t.get("transferAmount", t.get("transfer_amount")))
- paid += amount
- sid = _transfer_schedule_id(t)
- if sid:
- paid_by_schedule[sid] = paid_by_schedule.get(sid, Decimal("0")) + amount
- active = [
- s
- for s in sch_list
- if _status(s, "scheduleStatus", "schedule_status") != "void"
- ]
- active_schedule_count = len(active)
- cleared_flags = [
- is_schedule_effectively_cleared(
- s,
- confirmed_paid=paid_by_schedule.get(_schedule_id(s), Decimal("0")),
- )
- for s in active
- ]
- cleared_schedule_count = sum(1 for cleared in cleared_flags if cleared)
- 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 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(not cleared for cleared in cleared_flags)
- return {
- "progressStatus": progress,
- "paidAmount": paid,
- "dueRemainAmount": due_remain,
- "clearedScheduleCount": cleared_schedule_count,
- "activeScheduleCount": active_schedule_count,
- "canConfirm": can_confirm,
- }
|