| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384 |
- """缴费模式切换校验(与前端 protocol-pay-mode-switch.ts 规则一致)"""
- from __future__ import annotations
- from decimal import Decimal
- from typing import Any, Dict, Optional
- LOCKED_STATUSES = frozenset({"jfqrz", "yjf", "tfqrz", "ytf"})
- BLOCKING_STATUSES = frozenset({"jfqrz", "tfqrz", "ytf"})
- def _dec(v: Any) -> float:
- if v is None:
- return 0.0
- return float(Decimal(str(v)))
- def validate_payment_details_for_save(
- pay_mode: str,
- contract_amount: Optional[Any],
- installment_count: Optional[int],
- details: Optional[list],
- ) -> None:
- """保存缴费明细前的 P0 校验。"""
- rows = [dict(d) for d in (details or [])]
- contract = _dec(contract_amount)
- for r in rows:
- status = (r.get("pay_status") or r.get("payStatus") or "dfj").strip()
- if status in BLOCKING_STATUSES:
- raise ValueError("存在缴费确认中或退费处理中的明细,无法保存")
- paid_sum = sum(
- _dec(r.get("due_amount") if r.get("due_amount") is not None else r.get("dueAmount"))
- for r in rows
- if (r.get("pay_status") or r.get("payStatus")) == "yjf"
- )
- dfj_rows = [
- r
- for r in rows
- if (r.get("pay_status") or r.get("payStatus") or "dfj") == "dfj"
- ]
- yjf_rows = [r for r in rows if (r.get("pay_status") or r.get("payStatus")) == "yjf"]
- if pay_mode == "dj":
- if len(dfj_rows) > 1:
- raise ValueError("趸交模式仅允许一条待缴费明细")
- remaining = round(contract - paid_sum, 2)
- if dfj_rows:
- due = _dec(
- dfj_rows[0].get("due_amount")
- if dfj_rows[0].get("due_amount") is not None
- else dfj_rows[0].get("dueAmount")
- )
- expected = remaining if yjf_rows else contract
- if abs(due - expected) > 0.015:
- raise ValueError(
- f"趸交待缴金额应为 {expected:.2f},当前为 {due:.2f}"
- )
- elif pay_mode == "fq":
- count = int(installment_count or 0)
- if count < 1:
- raise ValueError("分期模式须填写分期期数")
- if len(rows) != count:
- raise ValueError(f"分期明细条数应等于分期期数({count})")
- fq_forbidden = frozenset({"jfqrz", "tfqrz", "ytf"})
- for r in rows:
- status = (r.get("pay_status") or r.get("payStatus") or "dfj").strip()
- if status in fq_forbidden:
- raise ValueError("分期模式下存在不可保存的缴费状态")
- total_due = sum(
- _dec(r.get("due_amount") if r.get("due_amount") is not None else r.get("dueAmount"))
- for r in rows
- )
- if contract and abs(total_due - contract) > 0.015:
- raise ValueError("分期各期应缴金额之和应等于合同金额")
- if yjf_rows:
- max_paid_no = max(
- int(r.get("installment_no") or r.get("installmentNo") or 0)
- for r in yjf_rows
- )
- if max_paid_no > count:
- raise ValueError("分期期数不能少于已缴费的最大期次")
|