后台按查看权限收紧菜单与「只看自己」数据范围;跨境整车报价填表更稳,协商中顺延不再误报销售超时。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -123,6 +123,35 @@ def notify_app_flag(override: Optional[bool]) -> bool:
|
||||
return notify_app_ready()
|
||||
|
||||
|
||||
def _skip_sales_timeout(
|
||||
*,
|
||||
status: str,
|
||||
wait_kind: str,
|
||||
next_followup_at: str,
|
||||
lost_reason: str,
|
||||
now: datetime,
|
||||
) -> str:
|
||||
"""
|
||||
成交跟进相关等待在终态/顺延窗内不应再告警。
|
||||
|
||||
返回跳过原因;空串表示继续按小时判定。
|
||||
协商中:只要还排着再问时刻(next_followup_at > now),就不发销售超时;
|
||||
再问认领后会清空该时刻并重新 start deal,那时才重新计时。
|
||||
"""
|
||||
st = (status or "").strip()
|
||||
_ = wait_kind
|
||||
if st == "未成交" and (lost_reason or "").strip():
|
||||
return "lost_reason_recorded"
|
||||
if st == "协商中":
|
||||
raw = (next_followup_at or "").strip()
|
||||
if not raw:
|
||||
return ""
|
||||
due = _parse_started(raw)
|
||||
if due is None or due > now:
|
||||
return "negotiate_followup_pending"
|
||||
return ""
|
||||
|
||||
|
||||
def _handle_one(
|
||||
row: dict[str, Any],
|
||||
*,
|
||||
@@ -139,6 +168,14 @@ def _handle_one(
|
||||
kind = str(row.get("event_wait_kind") or row.get("eventWaitKind") or "").strip()
|
||||
if not no or not kind:
|
||||
return False
|
||||
status = str(row.get("status") or "").strip()
|
||||
followup = str(row.get("next_followup_at") or row.get("nextFollowupAt") or "").strip()
|
||||
lost_reason = str(row.get("lost_reason") or row.get("lostReason") or "").strip()
|
||||
# 产品口径:协商中顺延再问期间不算「销售无响应」;未成交已写原因不再催。
|
||||
# 即使 clear_wait 失败留下陈旧 deal 等待,这里也要挡住(WO202610060024 / WO202610070034)。
|
||||
skip_reason = _skip_sales_timeout(
|
||||
status=status, wait_kind=kind, next_followup_at=followup, lost_reason=lost_reason, now=now
|
||||
)
|
||||
line = str(row.get("business_line") or row.get("businessLine") or "").strip()
|
||||
facts = dict(row.get("facts") or {})
|
||||
klass = str(facts.get("运输分类") or facts.get("transportType") or "").strip()
|
||||
@@ -154,6 +191,9 @@ def _handle_one(
|
||||
started = _parse_started(row.get("event_wait_started_at") or row.get("eventWaitStartedAt"))
|
||||
if started is None:
|
||||
return False
|
||||
if skip_reason:
|
||||
logger.info("事件异常跳过 wo=%s kind=%s reason=%s", no, kind, skip_reason)
|
||||
return False
|
||||
if workday_elapsed_hours(started, now) < hours:
|
||||
return False
|
||||
watchers = [dict(x) for x in (row.get("watchers") or []) if isinstance(x, dict)]
|
||||
|
||||
@@ -344,12 +344,23 @@ class MemoryLedger:
|
||||
ticket.next_followup_at = ""
|
||||
if lost_reason:
|
||||
ticket.lost_reason = lost_reason
|
||||
# 成交等待结束;若仍缺原因由智能体再 start lost_reason
|
||||
ticket.event_wait_kind = ""
|
||||
ticket.event_wait_started_at = ""
|
||||
ticket.event_wait_alerted = 0
|
||||
elif to_status == "已成交":
|
||||
ticket.next_followup_at = ""
|
||||
if deal_notify_summary:
|
||||
ticket.deal_notify_summary = deal_notify_summary
|
||||
ticket.event_wait_kind = ""
|
||||
ticket.event_wait_started_at = ""
|
||||
ticket.event_wait_alerted = 0
|
||||
elif to_status == "协商中":
|
||||
ticket.next_followup_at = next_followup_at
|
||||
# 顺延再问期间不盯成交超时;到期再问会重新 start deal
|
||||
ticket.event_wait_kind = ""
|
||||
ticket.event_wait_started_at = ""
|
||||
ticket.event_wait_alerted = 0
|
||||
return {"ok": True, "status": ticket.status}
|
||||
|
||||
def patch_lost_reason(self, *, work_order_no: str, lost_reason: str) -> dict[str, Any]:
|
||||
@@ -363,6 +374,9 @@ class MemoryLedger:
|
||||
if not reason:
|
||||
return {"ok": False, "error": "reason_blank"}
|
||||
ticket.lost_reason = reason
|
||||
ticket.event_wait_kind = ""
|
||||
ticket.event_wait_started_at = ""
|
||||
ticket.event_wait_alerted = 0
|
||||
return {"ok": True, "work_order_no": work_order_no, "lost_reason": reason}
|
||||
|
||||
def claim_due_negotiate(self) -> list[dict[str, Any]]:
|
||||
@@ -940,6 +954,11 @@ class MemoryLedger:
|
||||
continue
|
||||
if ticket.status in {"已成交", "已关闭", "转人工"}:
|
||||
continue
|
||||
# 未成交已写原因、协商中仍排着再问:不进巡检列表
|
||||
if ticket.status == "未成交" and (ticket.lost_reason or "").strip():
|
||||
continue
|
||||
if ticket.status == "协商中" and (ticket.next_followup_at or "").strip():
|
||||
continue
|
||||
watchers: list[dict[str, Any]] = []
|
||||
seen: set[str] = set()
|
||||
sales_id = (ticket.sales_wecom_id or "").strip()
|
||||
@@ -989,6 +1008,10 @@ class MemoryLedger:
|
||||
"collab_member_ids": list(ticket.collab_member_ids),
|
||||
"facts": dict(ticket.facts),
|
||||
"eventException": ticket.event_exception,
|
||||
"lost_reason": ticket.lost_reason,
|
||||
"lostReason": ticket.lost_reason,
|
||||
"next_followup_at": ticket.next_followup_at,
|
||||
"nextFollowupAt": ticket.next_followup_at,
|
||||
"watchers": watchers,
|
||||
}
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user