重启后仍能跳过协同出方案,未成交可同一句带原因;海陆有价卡改为直接生成报价单。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-23 18:45:28 +08:00
co-authored by Cursor
parent 378bb892b3
commit 84207bb4e5
18 changed files with 414 additions and 32 deletions
+57 -12
View File
@@ -71,6 +71,8 @@ _SEA_CLARIFY = tuple(
# 文字按钮(销售点卡或原样回复均可)
BTN_SKIP_COLLAB = "不需要协同,直接生成报价单"
BTN_SKIP_COLLAB_OLD = "不需要协同,直接生成基础报价单"
# 海运/陆运已有 TMS 报价时价格明细卡用短文案;空运仍用上面长文案。点卡 EventKey 不变。
BTN_SKIP_COLLAB_TMS = "直接生成报价单"
BTN_SKIP_COLLAB_KEY = "skip_collab"
BTN_SKIP_COLLAB_DONE = "已提交"
BTN_PULL_COLLAB = "拉产品进群协同"
@@ -695,7 +697,12 @@ def inquiry_card(
def canonical_button(raw: str) -> str:
"""把销售点卡 / 原文 / 企微 EventKey 收成标准按钮文案;对不上返回空。"""
text = (raw or "").strip().strip("【】")
if text in {BTN_SKIP_COLLAB, BTN_SKIP_COLLAB_OLD, BTN_SKIP_COLLAB_KEY}:
if text in {
BTN_SKIP_COLLAB,
BTN_SKIP_COLLAB_OLD,
BTN_SKIP_COLLAB_TMS,
BTN_SKIP_COLLAB_KEY,
}:
return BTN_SKIP_COLLAB
# 新卡 EventKey 带工单号:skip_collab:WO…,避免点旧卡落到最新单
if text.startswith(f"{BTN_SKIP_COLLAB_KEY}:"):
@@ -1441,7 +1448,7 @@ def sea_tms_hit_card(
lines.append("")
lines.append(sea_collab_ask())
lines.append(BTN_PULL_COLLAB)
lines.append(BTN_SKIP_COLLAB)
lines.append(BTN_SKIP_COLLAB_TMS)
return "\n".join(lines)
@@ -1485,7 +1492,7 @@ def sea_tms_hit_wecom_payload(
"key": f"{BTN_PULL_COLLAB_KEY}:{work_order_no}",
},
{
"text": BTN_SKIP_COLLAB,
"text": BTN_SKIP_COLLAB_TMS,
"style": 1,
"key": f"{BTN_SKIP_COLLAB_KEY}:{work_order_no}",
},
@@ -1860,7 +1867,7 @@ def sea_group_brief(
"""
群里自动摘要:询价 + 协同 + TMS 报价 + 人工报价。
TMS 无价只写「TMS 暂无匹配报价」,产品报的价单独放人工报价,销售调价另起一段。
TMS 无价写「TMS 暂无匹配报价」并提示用文字或 Excel 另报;产品报的价单独放人工报价,销售调价另起一段。
禁止把调后价写进 TMS 段。陆运传入 transport_mode=陆运,始发站/货物字段跟核对卡同一套。
"""
no = (work_order_no or "").strip()
@@ -1907,9 +1914,10 @@ def sea_group_brief(
lines.extend(ln for ln in detail if ln.strip() not in {source, title, "TMS标准报价"})
else:
lines.extend(quote_detail_lines(tms, transport_mode=mode))
lines.append(group_tms_confirm_hint())
lines.append(group_tms_confirm_hint(business_line="LAND" if mode == "陆运" else "SEA"))
else:
lines.append("TMS 暂无匹配报价")
lines.append(group_no_tms_quote_hint(business_line="LAND" if mode == "陆运" else "SEA"))
if manual and (quote_fee_lines(manual) or str(manual.get("total") or "").strip() not in {"", "-"}):
lines.append("")
lines.append("人工报价:")
@@ -2260,7 +2268,9 @@ def file_ready(*, kind: str, work_order_no: str, filename: str) -> str:
return f"已生成{kind}最终报价单:{filename}(工单 {work_order_no})"
DEAL_HINT = "咨询成交状态:请选择成交 / 未成交 / 协商中(未成交需补充原因)。"
# 未成交原因说明跟在未成交后,不跟在协商中后。验收脚本认这个开关。
DEAL_LOST_REASON_BEFORE_NEGOTIATE = True
DEAL_HINT = "咨询成交状态:请输入成交 / 未成交 (未成交需补充原因)/ 协商中。"
DEAL_SUBTITLE = "报价单已发送,请跟进成交状态"
@@ -2393,9 +2403,35 @@ def group_only_product_can_quote() -> str:
return "只有产品能报价格,请产品在群里发价格或 Excel/PDF"
def group_tms_confirm_hint() -> str:
"""报价有效期下方:有 TMS 价时告诉产品还可以确认沿用。"""
return "有TMS报价时可回复“确认报价”沿用该价;也可直接回复协同报价或上传报价附件。"
_GROUP_QUOTE_TAIL = (
"亦可通过“纯文字”告知新报价,或直接将新报价excel表发给智能体。\n"
"(注:附件报价单仅作为文件存储于后台,智能体不读取且不将报价内容写进系统)"
)
def group_tms_confirm_hint(*, business_line: str = "") -> str:
"""
有 TMS 价时写在报价有效期下方:谁可以确认沿用,以及怎么另报。
空运喊航线人员,海运/陆运喊产品人员。附件只当存档,不读进报价。
"""
line = (business_line or "").strip().upper()
who = "航线人员" if line == "AIR" else "产品人员"
return f"请{who}回复“确认报价”确定采用TMS报价。\n{_GROUP_QUOTE_TAIL}"
def group_no_tms_quote_hint(*, business_line: str = "") -> str:
"""
TMS 无价时写在「暂无匹配」下方:谁可以用文字或 Excel 另报。
空运喊航线人员,海运/陆运喊产品人员。附件只当存档,不读进报价。
"""
line = (business_line or "").strip().upper()
who = "航线人员" if line == "AIR" else "产品人员"
return (
f"{who}可通过“纯文字”告知新报价,或直接将新报价excel表发给智能体。\n"
"(注:附件报价单仅作为文件存储于后台,智能体不读取且不将报价内容写进系统)"
)
def quote_attachment_saved_app(work_order_no: str, count: int = 1) -> str:
@@ -2482,7 +2518,7 @@ def group_deal_followup(*, sales_name: str, work_order_no: str) -> str:
f"@{name}\n"
f"询价工单 {no}\n"
"报价单已发送,请确认成交状态\n"
"咨询成交状态:请输入成交 / 未成交 / 协商中(未成交需补充原因)"
"咨询成交状态:请输入成交 / 未成交 (未成交需补充原因)/ 协商中"
)
@@ -2490,11 +2526,19 @@ def group_deal_mention(*, sales_name: str, work_order_no: str) -> str:
return group_deal_followup(sales_name=sales_name, work_order_no=work_order_no)
def deal_ack(outcome: str, work_order_no: str, next_followup_at: str = "") -> str:
def deal_ack(
outcome: str,
work_order_no: str,
next_followup_at: str = "",
lost_reason: str = "",
) -> str:
"""成交三态回执。协商中写明顺延三个工作日和下次再问的月日 9:00。"""
if outcome == "已成交":
return f"已记录为「已成交」。工单 {work_order_no} 已关闭智能体跟进。"
if outcome == "未成交":
reason = (lost_reason or "").strip()
if reason:
return f"已记录为「未成交」。{lost_reason_ack(reason)}"
return "已记录为「未成交」。请补充未成交原因。"
when = _followup_month_day(next_followup_at)
if when:
@@ -2682,9 +2726,10 @@ def air_group_brief(
lines.append("TMS报价:")
if tms and (tms.get("total") or quote_fee_lines(tms)):
lines.extend(quote_detail_lines(tms, business_line="AIR"))
lines.append(group_tms_confirm_hint())
lines.append(group_tms_confirm_hint(business_line="AIR"))
else:
lines.append("TMS 暂无匹配报价")
lines.append(group_no_tms_quote_hint(business_line="AIR"))
if manual and (quote_fee_lines(manual) or str(manual.get("total") or "").strip() not in {"", "-"}):
lines.append("")
lines.append("人工报价:")