落地空运单聊询价字段与线路选择。
必填核对后再建单;多条线路走 H5 点选;费用只展示该线路 TMS 回包,不套用整单空运费。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -79,7 +79,7 @@ class AirGroupCollabTests(unittest.TestCase):
|
||||
]
|
||||
)
|
||||
|
||||
def _create_air(self, *, sender="sales1", quote=None, status="已报价") -> str:
|
||||
def _create_air(self, *, sender="sales1", quote=None, status="已报价", quote_date="2026-09-16") -> str:
|
||||
created = self.ledger.create_ticket(
|
||||
sender_id=sender,
|
||||
business_line="AIR",
|
||||
@@ -91,7 +91,7 @@ class AirGroupCollabTests(unittest.TestCase):
|
||||
"毛重": "100",
|
||||
"体积": "2",
|
||||
"包装类型": "纸箱",
|
||||
"报价日期": "2026-09-16",
|
||||
"报价日期": quote_date,
|
||||
},
|
||||
)
|
||||
wo = created["work_order_no"]
|
||||
@@ -122,6 +122,25 @@ class AirGroupCollabTests(unittest.TestCase):
|
||||
def _texts(self) -> list[str]:
|
||||
return [x.get("content") or "" for x in self.groups.sent]
|
||||
|
||||
def test_attach_bot_client_keeps_req_id_for_quote(self) -> None:
|
||||
"""这次 @ 的 req_id 要带给桥,群里才能引用原消息。"""
|
||||
from agent.channel.aibot.reply import AibotReplyClient
|
||||
from agent.handlers.air_group_collab import _attach_bot_client
|
||||
|
||||
engine = AirTextInquiryFlow(ledger=self.ledger, group_client=None)
|
||||
msg = InboundMessage(
|
||||
sender_id="air1",
|
||||
message_id="m_quote",
|
||||
content="锁舱",
|
||||
chat_id="wr_air_1",
|
||||
chat_type="group",
|
||||
raw={"source": "aibot", "aibot_req_id": "cb_lock_1"},
|
||||
)
|
||||
_attach_bot_client(engine, msg)
|
||||
client = engine.group_client()
|
||||
self.assertIsInstance(client, AibotReplyClient)
|
||||
self.assertEqual(client._callback_req_id, "cb_lock_1")
|
||||
|
||||
def test_intent_lock_is_not_hardcoded(self) -> None:
|
||||
self.assertEqual(classify_air_group_text("帮我锁舱 WO202609160001", allow_network=False), INTENT_LOCK)
|
||||
self.assertEqual(classify_air_group_text("WO202609160001释放舱位", allow_network=False), INTENT_RELEASE)
|
||||
@@ -495,6 +514,7 @@ class AirGroupCollabTests(unittest.TestCase):
|
||||
)
|
||||
self.assertEqual(phase, "lock_ok")
|
||||
self.assertTrue(any("执行成功" in t for t in self._texts()))
|
||||
self.assertTrue(any("舱位选项:AIR-OPT-01" in t for t in self._texts()))
|
||||
self.assertFalse(any("还没有可锁的 TMS 报价" in t for t in self._texts()))
|
||||
|
||||
def test_manual_quote_without_quote_id_can_lock(self) -> None:
|
||||
@@ -884,6 +904,113 @@ class AirGroupCollabTests(unittest.TestCase):
|
||||
)
|
||||
self.assertEqual(phase, "not_air")
|
||||
|
||||
def test_lock_fail_tells_operator_and_tms_reason(self) -> None:
|
||||
"""锁舱失败群里要回操作人和 TMS 原因,不能被系统异常兜底盖掉。"""
|
||||
wo = self._create_air()
|
||||
self.ledger._cabin_lock_result = {
|
||||
"ok": False,
|
||||
"error": "lock_fail",
|
||||
"failReason": "该航班日期不存在可锁定的配载记录",
|
||||
}
|
||||
phase = handle_air_group(
|
||||
self._msg(sender="air1", text=f"{wo}锁舱", chat="wr_air_lock_fail", mid="lf1"),
|
||||
flow=self.flow,
|
||||
ledger=self.ledger,
|
||||
injected_intent="air_lock",
|
||||
)
|
||||
self.assertEqual(phase, "system_exception")
|
||||
joined = "\n".join(self._texts())
|
||||
self.assertIn(f"工单{wo} 空运锁舱失败。", joined)
|
||||
self.assertIn("操作人:HangXian", joined)
|
||||
self.assertIn("锁舱失败的原因:该航班日期不存在可锁定的配载记录", joined)
|
||||
self.assertIn("已通知IT运维排查,请稍等。", joined)
|
||||
self.assertNotIn("TMS接口异常,IT运维排查中", joined)
|
||||
self.assertNotIn("报价选项不存在", joined)
|
||||
self.assertNotIn("与工单不匹配", joined)
|
||||
|
||||
def test_lock_without_quote_still_shows_tms_reason(self) -> None:
|
||||
"""没有报价也可以锁舱;群里只回 TMS 原因,不要说选项不匹配。"""
|
||||
wo = self._create_air(quote={}, status="询价中")
|
||||
self.ledger._cabin_lock_result = {
|
||||
"ok": False,
|
||||
"error": "lock_fail",
|
||||
"failReason": "该航班日期不存在可锁定的配载记录",
|
||||
}
|
||||
phase = handle_air_group(
|
||||
self._msg(sender="air1", text=f"{wo}锁舱", chat="wr_air_lock_no_quote", mid="lnq1"),
|
||||
flow=self.flow,
|
||||
ledger=self.ledger,
|
||||
injected_intent="air_lock",
|
||||
)
|
||||
self.assertEqual(phase, "system_exception")
|
||||
joined = "\n".join(self._texts())
|
||||
self.assertIn("锁舱失败的原因:该航班日期不存在可锁定的配载记录", joined)
|
||||
self.assertNotIn("报价选项不存在", joined)
|
||||
self.assertNotIn("与工单不匹配", joined)
|
||||
|
||||
def test_paused_lock_retries_and_shows_tms_reason(self) -> None:
|
||||
"""上次锁舱失败标了系统异常后,再锁仍要打 TMS,不能只回兜底。"""
|
||||
wo = self._create_air()
|
||||
self.ledger.mark_system_exception(
|
||||
work_order_no=wo,
|
||||
system_exception="TMS",
|
||||
system_exception_reason="空运锁舱接口调用失败",
|
||||
step="tms_lock",
|
||||
payload={},
|
||||
)
|
||||
hits = {"n": 0}
|
||||
orig = self.ledger.lock_cabin
|
||||
|
||||
def counted(**kwargs):
|
||||
hits["n"] += 1
|
||||
return orig(**kwargs)
|
||||
|
||||
self.ledger.lock_cabin = counted # type: ignore[method-assign]
|
||||
self.ledger._cabin_lock_result = {
|
||||
"ok": False,
|
||||
"error": "lock_fail",
|
||||
"failReason": "该航班日期不存在可锁定的配载记录",
|
||||
}
|
||||
phase = handle_air_group(
|
||||
self._msg(sender="air1", text=f"{wo}锁舱", chat="wr_air_lock_paused", mid="lp1"),
|
||||
flow=self.flow,
|
||||
ledger=self.ledger,
|
||||
injected_intent="air_lock",
|
||||
)
|
||||
self.assertNotEqual(phase, "system_exception_paused")
|
||||
self.assertGreaterEqual(hits["n"], 1)
|
||||
joined = "\n".join(self._texts())
|
||||
self.assertIn("锁舱失败的原因:该航班日期不存在可锁定的配载记录", joined)
|
||||
self.assertNotIn("TMS接口异常,IT运维排查中", joined)
|
||||
|
||||
def test_release_fail_tells_operator_and_tms_reason(self) -> None:
|
||||
"""释放失败同理:操作人、原因、已通知IT,不要系统异常兜底。"""
|
||||
wo = self._create_air()
|
||||
handle_air_group(
|
||||
self._msg(sender="air1", text=f"{wo}锁舱", chat="wr_air_rl_fail", mid="rf0"),
|
||||
flow=self.flow,
|
||||
ledger=self.ledger,
|
||||
injected_intent="air_lock",
|
||||
)
|
||||
self.ledger._cabin_release_result = {
|
||||
"ok": False,
|
||||
"error": "release_fail",
|
||||
"failReason": "舱位不存在",
|
||||
}
|
||||
phase = handle_air_group(
|
||||
self._msg(sender="air1", text=f"{wo}释放舱位", chat="wr_air_rl_fail", mid="rf1"),
|
||||
flow=self.flow,
|
||||
ledger=self.ledger,
|
||||
injected_intent="air_release",
|
||||
)
|
||||
self.assertEqual(phase, "system_exception")
|
||||
joined = "\n".join(self._texts())
|
||||
self.assertIn(f"工单{wo} 空运释放舱位失败。", joined)
|
||||
self.assertIn("操作人:HangXian", joined)
|
||||
self.assertIn("释放舱位失败的原因:舱位不存在", joined)
|
||||
self.assertIn("已通知IT运维排查,请稍等。", joined)
|
||||
self.assertNotIn("TMS接口异常,IT运维排查中", joined)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user