空运人工价锁舱现拉补全舱位并拦清缺参;成交/报价填表与工单陆运费率展示同步收紧。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
linmianxun
2026-10-07 23:35:24 +08:00
co-authored by Cursor
parent 419b4a1bf7
commit e197ecea10
31 changed files with 1607 additions and 150 deletions
+65
View File
@@ -144,10 +144,75 @@ class TestDealOutcome(unittest.TestCase):
view = self.ledger.get_for_agent(work_order_no=self.wo, sender_id="sales1")
self.assertEqual(view.get("wait_phase"), "done")
def test_lost_from_inquiring_promotes_quoted_first(self) -> None:
"""主账仍询价中时点未成交:先补已报价再入未成交,禁止直接失败。"""
class _StrictSix:
def __init__(self) -> None:
self.status = "询价中"
self.lost_reason = ""
self.event_wait_kind = ""
def transition(self, *, work_order_no, to_status, **kwargs):
_ = work_order_no
allowed = {
"询价中": {"已报价", "协商中", "已关闭"},
"已报价": {"协商中", "已成交", "未成交", "已关闭"},
}
if to_status not in allowed.get(self.status, set()):
# 模拟 http_ledger 在 500 时只回 http_500(丢失正文)
return {"ok": False, "error": "http_500"}
self.status = to_status
if to_status == "未成交" and kwargs.get("lost_reason"):
self.lost_reason = str(kwargs.get("lost_reason") or "")
return {"ok": True, "status": self.status}
def patch_lost_reason(self, *, work_order_no, lost_reason):
_ = work_order_no
self.lost_reason = lost_reason
return {"ok": True}
def get_for_agent(self, *, work_order_no, sender_id):
_ = (work_order_no, sender_id)
return {"status": self.status, "lost_reason": self.lost_reason}
ledger = _StrictSix()
out = finish_deal_outcome(
ledger=ledger,
work_order_no="WO202610070023",
outcome="未成交",
facts={},
quote={},
business_line="AIR",
)
self.assertTrue(out.get("ok"), out)
self.assertEqual(ledger.status, "未成交")
self.assertEqual(out.get("phase"), "wait_lost_reason")
def test_lost_reason_ack_repeats_user_text(self) -> None:
self.assertEqual(copy.lost_reason_ack("客户不要了"), "已记下未成交原因:客户不要了。")
self.assertEqual(copy.lost_reason_ack("价格高。"), "已记下未成交原因:价格高。")
def test_lost_reason_ack_fills_ticket_no_from_bot_reply(self) -> None:
"""后台文案带 {{ticket_no}} 时必须填实值,禁止原样打出占位符。"""
from agent.policy import bot_reply
bot_reply.override_catalog(
{
"lost.reason_ack": (
"已将未成交原因“{{lost_reason}}”记录在后台,"
"工单{{ticket_no}}已关闭,智能体不再跟进该工单,请悉知。"
)
}
)
try:
text = copy.lost_reason_ack("换时间", work_order_no="WO202610070018")
self.assertIn("WO202610070018", text)
self.assertNotIn("{{ticket_no}}", text)
self.assertIn("换时间", text)
finally:
bot_reply.override_catalog(None)
def test_harvest_inline_lost_reason(self) -> None:
self.assertEqual(harvest_inline_lost_reason("未成交,原因客户不想要了"), "客户不想要了")
self.assertEqual(