汇总近期业务改动:催缴菜单与后端、样本信息快捷登记与进度、运费关联、客户档案扩展、协议优惠/期次与押金划转,并同步 SQL 迁移与管理端页面收敛。 Co-authored-by: Cursor <cursoragent@cursor.com>
104 lines
3.2 KiB
Python
104 lines
3.2 KiB
Python
"""fq → fgd:计算并期变更指令(纯函数,不写库)"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from decimal import Decimal
|
|
from typing import Any, Dict, Iterable, List, Mapping
|
|
|
|
from app.services.protocol_transfer_status import schedule_paid_and_arrears
|
|
|
|
|
|
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 plan_fq_to_fgd_merge(
|
|
*,
|
|
schedules: Iterable[Mapping[str, Any]],
|
|
transfers: Iterable[Mapping[str, Any]],
|
|
) -> Dict[str, Any]:
|
|
sch_list = [
|
|
s
|
|
for s in schedules
|
|
if _status(s, "scheduleStatus", "schedule_status") != "void"
|
|
and not s.get("_deleted")
|
|
]
|
|
sch_list.sort(key=lambda s: int(s.get("scheduleNo") or s.get("schedule_no") or 0))
|
|
tr_list = list(transfers or [])
|
|
|
|
by_sch: Dict[str, List[Mapping[str, Any]]] = {}
|
|
for t in tr_list:
|
|
sid = str(t.get("scheduleId") or t.get("schedule_id") or "")
|
|
by_sch.setdefault(sid, []).append(t)
|
|
|
|
def _arrears(s: Mapping[str, Any]) -> Decimal:
|
|
sid = str(s.get("id") or "")
|
|
confirmed = [
|
|
_dec(t.get("transferAmount", t.get("transfer_amount")))
|
|
for t in by_sch.get(sid, [])
|
|
if _status(t, "payStatus", "pay_status") == "yjf"
|
|
]
|
|
_paid, arr = schedule_paid_and_arrears(
|
|
due=s.get("dueAmount", s.get("due_amount")),
|
|
confirmed_transfers=confirmed,
|
|
)
|
|
arr_d = _dec(arr)
|
|
return arr_d if arr_d > 0 else Decimal("0")
|
|
|
|
target = None
|
|
for s in sch_list:
|
|
if _arrears(s) > 0:
|
|
target = s
|
|
break
|
|
if target is None:
|
|
for s in sch_list:
|
|
if _status(s, "scheduleStatus", "schedule_status") != "cleared":
|
|
target = s
|
|
break
|
|
if target is None:
|
|
raise ValueError("没有可合并的未清期次")
|
|
|
|
target_no = int(target.get("scheduleNo") or target.get("schedule_no") or 0)
|
|
later = [
|
|
s
|
|
for s in sch_list
|
|
if int(s.get("scheduleNo") or s.get("schedule_no") or 0) > target_no
|
|
]
|
|
for s in later:
|
|
sid = str(s.get("id") or "")
|
|
if any(
|
|
_status(t, "payStatus", "pay_status") == "yjf" for t in by_sch.get(sid, [])
|
|
):
|
|
raise ValueError("后续期已有已确认转账,无法合并为分改趸")
|
|
|
|
remain = sum((_arrears(s) for s in sch_list), Decimal("0"))
|
|
tid = str(target.get("id") or "")
|
|
target_confirmed = sum(
|
|
(
|
|
_dec(t.get("transferAmount", t.get("transfer_amount")))
|
|
for t in by_sch.get(tid, [])
|
|
if _status(t, "payStatus", "pay_status") == "yjf"
|
|
),
|
|
Decimal("0"),
|
|
)
|
|
new_due = target_confirmed + remain
|
|
soft_ids = [str(s.get("id") or "") for s in later if str(s.get("id") or "")]
|
|
soft_set = set(soft_ids)
|
|
kept = [s for s in sch_list if str(s.get("id") or "") not in soft_set]
|
|
|
|
return {
|
|
"targetScheduleId": tid,
|
|
"newDueAmount": new_due,
|
|
"softDeleteScheduleIds": soft_ids,
|
|
"newInstallmentCount": len(kept),
|
|
}
|