落地海运协同群:测服靠会话存档听群话,报价单能带上协同字段。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -62,8 +62,29 @@ class FakeGroupClient:
|
||||
self.created.append({"name": "", "userids": list(userids), "chat_id": chat_id, "add": True})
|
||||
return {"ok": True}
|
||||
|
||||
def send_group(self, *, chat_id: str, content: str) -> dict:
|
||||
self.sent.append({"chat_id": chat_id, "content": content})
|
||||
def send_group(self, *, chat_id: str, content: str, mention_userids: list | None = None) -> dict:
|
||||
self.sent.append(
|
||||
{
|
||||
"chat_id": chat_id,
|
||||
"content": content,
|
||||
"kind": "text",
|
||||
"mention_userids": list(mention_userids or []),
|
||||
}
|
||||
)
|
||||
return {"ok": True}
|
||||
|
||||
def send_group_file(self, *, chat_id: str, filename: str, filepath: str = "", file_b64: str = "") -> dict:
|
||||
self.sent.append(
|
||||
{"chat_id": chat_id, "filename": filename, "kind": "file", "file_b64": file_b64}
|
||||
)
|
||||
return {"ok": True}
|
||||
|
||||
def send_group_card(self, *, chat_id: str, template_card: dict) -> dict:
|
||||
self.sent.append({"chat_id": chat_id, "kind": "card", "template_card": template_card})
|
||||
return {"ok": True}
|
||||
|
||||
def dismiss_group(self, *, chat_id: str, userids: list) -> dict:
|
||||
self.sent.append({"chat_id": chat_id, "kind": "dismiss", "userids": list(userids)})
|
||||
return {"ok": True}
|
||||
|
||||
|
||||
@@ -213,21 +234,40 @@ class SeaTmsCardTests(unittest.TestCase):
|
||||
],
|
||||
}
|
||||
text = copy.sea_quote_detail_text(
|
||||
work_order_no="WO202609150029", quote=long_quote
|
||||
work_order_no="WO202609150030", quote=long_quote
|
||||
)
|
||||
self.assertIn("海运费:USD 100.00", text)
|
||||
self.assertIn("报关费:CNY 250.00", text)
|
||||
self.assertIn("总销售价合计:CNY 2287.65", text)
|
||||
self.assertEqual(
|
||||
text,
|
||||
"\n".join(
|
||||
[
|
||||
"TMS标准报价",
|
||||
"WO202609150030价格明细",
|
||||
"海运费:USD 100.00",
|
||||
"燃油附加费:USD 15.00",
|
||||
"操作费:CNY 680.00",
|
||||
"文件费:CNY 450.00",
|
||||
"封条费(SEAI):CNY 50.00",
|
||||
"设备费:CNY 50.00",
|
||||
"报关费:CNY 250.00",
|
||||
"总销售价合计:CNY 2287.65",
|
||||
]
|
||||
),
|
||||
)
|
||||
self.assertNotIn("时效", text)
|
||||
self.assertNotIn("报价有效期", text)
|
||||
card_text = copy.quote_area_safe_text(long_quote)
|
||||
self.assertIn("总销售价合计:CNY 2287.65", card_text)
|
||||
self.assertNotIn("海运费:USD 100.00\n燃油附加费", card_text)
|
||||
self.assertLessEqual(len(card_text), 80)
|
||||
payload = copy.sea_tms_hit_wecom_payload(
|
||||
work_order_no="WO202609150029", quote=long_quote
|
||||
work_order_no="WO202609150030", quote=long_quote
|
||||
)
|
||||
self.assertEqual(
|
||||
payload["template_card"]["quote_area"]["quote_text"], card_text
|
||||
)
|
||||
sub = payload["template_card"]["sub_title_text"]
|
||||
self.assertIn("时效:1天", sub)
|
||||
self.assertIn("报价有效期:2026-09-30", sub)
|
||||
|
||||
def test_sea_hit_card_has_price_and_two_buttons(self) -> None:
|
||||
phase = self.flow.on_text(
|
||||
@@ -393,9 +433,12 @@ class SeaSkipAndPullTests(unittest.TestCase):
|
||||
self.assertNotIn("AI001", self.groups.created[0]["userids"])
|
||||
self.assertTrue(self.groups.sent)
|
||||
group_text = self.groups.sent[0]["content"]
|
||||
self.assertTrue(any(x.get("kind") == "file" for x in self.groups.sent))
|
||||
self.assertIn(wo, group_text)
|
||||
self.assertIn("起运港", group_text)
|
||||
self.assertIn("贸易条款", group_text)
|
||||
self.assertIn("商品海关编码", group_text)
|
||||
self.assertNotIn("HS编码", group_text)
|
||||
self.assertIn("TMS", group_text)
|
||||
sess = self.flow.session_of("s1")
|
||||
self.assertNotIn(copy.BTN_PULL_COLLAB, sess.allowed)
|
||||
@@ -405,6 +448,40 @@ class SeaSkipAndPullTests(unittest.TestCase):
|
||||
)
|
||||
self.assertEqual(again, "card_discard")
|
||||
|
||||
def test_pull_sends_sheet_when_client_not_injected(self) -> None:
|
||||
"""测服正式路径不注入 group_client,拉群仍要发出填好的模板。"""
|
||||
from unittest.mock import patch
|
||||
|
||||
live = FakeGroupClient()
|
||||
self.ledger.set_sea_staff(
|
||||
[
|
||||
{
|
||||
"name": "张产品",
|
||||
"wecomId": "prod1",
|
||||
"roleCode": "sea",
|
||||
"routes": "海运·全部线路",
|
||||
"status": "active",
|
||||
}
|
||||
]
|
||||
)
|
||||
with patch("agent.policy.sea_text_flow._default_group_client", return_value=live):
|
||||
flow = SeaTextInquiryFlow(self.ledger, group_client=None)
|
||||
flow.on_text(
|
||||
sender_id="s9",
|
||||
text="海运有价",
|
||||
reply=self._reply,
|
||||
injected_facts=COMPLETE_SEA,
|
||||
injected_mode="SEA",
|
||||
)
|
||||
phase = flow.on_button(
|
||||
sender_id="s9",
|
||||
action=copy.BTN_PULL_COLLAB,
|
||||
reply=self._reply,
|
||||
)
|
||||
self.assertEqual(phase, "collab_group")
|
||||
self.assertTrue(any(x.get("kind") == "text" for x in live.sent))
|
||||
self.assertTrue(any(x.get("kind") == "file" for x in live.sent))
|
||||
|
||||
def test_pull_group_includes_archive_bot(self) -> None:
|
||||
self.ledger.upsert_staff(
|
||||
{
|
||||
@@ -509,12 +586,27 @@ class SeaSkipAndPullTests(unittest.TestCase):
|
||||
reply=self._reply,
|
||||
)
|
||||
self.assertTrue(self.groups.sent)
|
||||
self.assertIn("暂无匹配报价", self.groups.sent[0]["content"])
|
||||
group_text = self.groups.sent[0]["content"]
|
||||
self.assertIn("TMS 暂无匹配报价", group_text)
|
||||
self.assertNotIn("未查到合适报价", group_text)
|
||||
self.assertIn("\n\n协同补充字段:\n", group_text)
|
||||
self.assertIn("\n\nTMS报价:\n", group_text)
|
||||
|
||||
|
||||
class SeaGroupCollabFieldTests(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
self.ledger = MemoryLedger()
|
||||
self.ledger.set_quote_templates(
|
||||
[
|
||||
{
|
||||
"templateId": "mem-default-sea",
|
||||
"name": "海运默认",
|
||||
"bizType": "海运",
|
||||
"keywords": [],
|
||||
"isDefault": True,
|
||||
}
|
||||
]
|
||||
)
|
||||
self.groups = FakeGroupClient()
|
||||
self.flow = SeaTextInquiryFlow(self.ledger, group_client=self.groups)
|
||||
self.replies: list[str] = []
|
||||
@@ -602,8 +694,10 @@ class SeaGroupCollabFieldTests(unittest.TestCase):
|
||||
flow=self.flow,
|
||||
ledger=self.ledger,
|
||||
)
|
||||
self.assertEqual(phase, "group_ignore")
|
||||
self.assertFalse(ticket.quote_version > 1 and "1200" in str(ticket.quote))
|
||||
self.assertEqual(phase, "product_quote_sent")
|
||||
self.assertEqual(str(ticket.quote.get("total") or ""), "1200")
|
||||
deal_texts = [x["content"] for x in self.groups.sent if x.get("kind") == "text" and "成交" in (x.get("content") or "")]
|
||||
self.assertTrue(deal_texts)
|
||||
|
||||
|
||||
class SeaContinueTests(unittest.TestCase):
|
||||
@@ -761,6 +855,17 @@ class SeaContinueTests(unittest.TestCase):
|
||||
card_meta={"event_key": f"adopt_excel:{wo}", "response_code": "rc-xl"},
|
||||
)
|
||||
self.assertEqual(self.flow.session_of("c3").phase, "wait_deal")
|
||||
file_extras = [x for x in self.extras if x.get("msgtype") == "file"]
|
||||
self.assertTrue(file_extras)
|
||||
follow = file_extras[-1].get("followup_card") or {}
|
||||
self.assertTrue(str(follow.get("task_id") or "").startswith("deal-"))
|
||||
separate_deal = [
|
||||
x
|
||||
for x in self.extras
|
||||
if x.get("msgtype") == "template_card"
|
||||
and str((x.get("template_card") or {}).get("task_id") or "").startswith("deal-")
|
||||
]
|
||||
self.assertEqual(separate_deal, [])
|
||||
empty = SeaTextInquiryFlow(self.ledger)
|
||||
store = MemoryMessageStore()
|
||||
msg = InboundMessage(
|
||||
@@ -777,11 +882,37 @@ class SeaContinueTests(unittest.TestCase):
|
||||
phase = handle_continue_thread(msg, thread_id=wo, store=store)
|
||||
self.assertEqual(phase, "wait_deal")
|
||||
joined = "\n".join(item.content for item in store._outbox.values())
|
||||
self.assertTrue(any(x in joined for x in (copy.BTN_DEAL, "成交")))
|
||||
self.assertFalse(any(x in joined for x in (copy.BTN_DEAL, "成交")))
|
||||
self.assertNotIn(copy.BTN_SKIP_COLLAB, joined)
|
||||
|
||||
|
||||
class SeaGroupCopyTests(unittest.TestCase):
|
||||
def test_group_brief_uses_customs_code_and_section_breaks(self) -> None:
|
||||
text = copy.sea_group_brief(
|
||||
work_order_no="WO202609150023",
|
||||
facts={
|
||||
"起运港": "纽约港",
|
||||
"目的港": "巴生西港",
|
||||
"品名": "机器设备",
|
||||
"货量": "15",
|
||||
"整柜或拼柜": "整柜",
|
||||
"箱型箱量": "40HQ*1",
|
||||
"报价日期": "2026-09-15",
|
||||
},
|
||||
quote=None,
|
||||
collab_facts={},
|
||||
sales_name="吴季林",
|
||||
product_names=["罗小华"],
|
||||
has_price=False,
|
||||
)
|
||||
self.assertIn("@吴季林 @罗小华", text)
|
||||
self.assertIn("询价字段:\n运输方式:海运", text)
|
||||
self.assertIn("\n\n协同补充字段:\n", text)
|
||||
self.assertIn("商品海关编码:-", text)
|
||||
self.assertNotIn("HS编码", text)
|
||||
self.assertIn("\n\nTMS报价:\nTMS 暂无匹配报价", text)
|
||||
self.assertNotIn("未查到合适报价", text)
|
||||
|
||||
def test_scheme_card_uses_sea_fields(self) -> None:
|
||||
facts = {
|
||||
"起运港": "上海",
|
||||
@@ -831,6 +962,312 @@ class SeaGroupCopyTests(unittest.TestCase):
|
||||
"已邀请:销售吴季林、产品罗小华。请在群聊中继续确认报价。",
|
||||
)
|
||||
|
||||
def test_group_only_excel_pdf_copy(self) -> None:
|
||||
self.assertEqual(
|
||||
copy.group_only_excel_pdf(),
|
||||
"群里只收 Excel 和 PDF,其它文件不会存入工单",
|
||||
)
|
||||
|
||||
|
||||
class SeaGroupOpsTests(unittest.TestCase):
|
||||
"""群协同:附件类型、产品报价、解散后再拉、终态提醒。"""
|
||||
|
||||
def setUp(self) -> None:
|
||||
self.ledger = MemoryLedger()
|
||||
self.ledger.set_quote_templates(
|
||||
[
|
||||
{
|
||||
"templateId": "mem-default-sea",
|
||||
"name": "海运默认",
|
||||
"bizType": "海运",
|
||||
"keywords": [],
|
||||
"isDefault": True,
|
||||
}
|
||||
]
|
||||
)
|
||||
self.ledger.set_sea_staff(
|
||||
[
|
||||
{
|
||||
"name": "赵产品",
|
||||
"wecomId": "prod4",
|
||||
"roleCode": "sea",
|
||||
"routes": "海运·全部线路",
|
||||
"status": "active",
|
||||
}
|
||||
]
|
||||
)
|
||||
self.ledger.upsert_staff({"name": "吴吉林", "wecomId": "sales1"})
|
||||
self.groups = FakeGroupClient()
|
||||
self.flow = SeaTextInquiryFlow(self.ledger, group_client=self.groups)
|
||||
self.replies: list[str] = []
|
||||
|
||||
def _reply(self, text: str, extra=None) -> None:
|
||||
self.replies.append(text)
|
||||
|
||||
def _open_group(self) -> tuple[str, str]:
|
||||
self.flow.on_text(
|
||||
sender_id="sales1",
|
||||
text="海运有价",
|
||||
reply=self._reply,
|
||||
injected_facts=COMPLETE_SEA,
|
||||
injected_mode="SEA",
|
||||
)
|
||||
self.flow.on_button(
|
||||
sender_id="sales1",
|
||||
action=copy.BTN_PULL_COLLAB,
|
||||
reply=self._reply,
|
||||
)
|
||||
sess = self.flow.session_of("sales1")
|
||||
return sess.work_order_no, sess.collab_chat_id
|
||||
|
||||
def test_group_rejects_non_excel_pdf(self) -> None:
|
||||
wo, chat = self._open_group()
|
||||
phase = handle_group_collab(
|
||||
InboundMessage(
|
||||
sender_id="sales1",
|
||||
message_id="img1",
|
||||
content="",
|
||||
msg_type="image",
|
||||
chat_id=chat,
|
||||
chat_type="group",
|
||||
media={"filename": "现场.jpg", "media_id": "mid-1"},
|
||||
),
|
||||
flow=self.flow,
|
||||
ledger=self.ledger,
|
||||
)
|
||||
self.assertEqual(phase, "file_type_rejected")
|
||||
texts = [x.get("content") or "" for x in self.groups.sent if x.get("kind") == "text"]
|
||||
self.assertIn("群里只收 Excel 和 PDF,其它文件不会存入工单", texts)
|
||||
ticket = self.ledger.get_ticket(work_order_no=wo)
|
||||
self.assertFalse(ticket.attachments)
|
||||
|
||||
def test_sales_excel_is_archived_not_quote(self) -> None:
|
||||
wo, chat = self._open_group()
|
||||
phase = handle_group_collab(
|
||||
InboundMessage(
|
||||
sender_id="sales1",
|
||||
message_id="xls1",
|
||||
content="",
|
||||
msg_type="file",
|
||||
chat_id=chat,
|
||||
chat_type="group",
|
||||
media={"filename": "对账单.xlsx", "media_id": "mid-2"},
|
||||
),
|
||||
flow=self.flow,
|
||||
ledger=self.ledger,
|
||||
)
|
||||
self.assertEqual(phase, "file_archived")
|
||||
ticket = self.ledger.get_ticket(work_order_no=wo)
|
||||
self.assertEqual(ticket.attachments[0]["file_name"], "对账单.xlsx")
|
||||
self.assertNotEqual(ticket.quote.get("source_label"), "产品附件报价")
|
||||
|
||||
def test_product_excel_quotes_and_deal(self) -> None:
|
||||
wo, chat = self._open_group()
|
||||
phase = handle_group_collab(
|
||||
InboundMessage(
|
||||
sender_id="prod4",
|
||||
message_id="xls2",
|
||||
content="",
|
||||
msg_type="file",
|
||||
chat_id=chat,
|
||||
chat_type="group",
|
||||
media={"filename": "产品报价.pdf", "media_id": "mid-3"},
|
||||
),
|
||||
flow=self.flow,
|
||||
ledger=self.ledger,
|
||||
)
|
||||
self.assertEqual(phase, "product_file_quote")
|
||||
ticket = self.ledger.get_ticket(work_order_no=wo)
|
||||
self.assertEqual(ticket.attachments[0]["file_name"], "产品报价.pdf")
|
||||
self.assertEqual(ticket.status, "已报价")
|
||||
self.assertTrue(any(x.get("kind") == "card" for x in self.groups.sent))
|
||||
|
||||
def test_terminal_file_speaks(self) -> None:
|
||||
wo, chat = self._open_group()
|
||||
self.ledger.transition(work_order_no=wo, to_status="已成交")
|
||||
sess = self.flow.session_of("sales1")
|
||||
sess.status = "已成交"
|
||||
phase = handle_group_collab(
|
||||
InboundMessage(
|
||||
sender_id="prod4",
|
||||
message_id="xls3",
|
||||
content="",
|
||||
msg_type="file",
|
||||
chat_id=chat,
|
||||
chat_type="group",
|
||||
media={"filename": "晚到.xlsx", "media_id": "mid-4"},
|
||||
),
|
||||
flow=self.flow,
|
||||
ledger=self.ledger,
|
||||
)
|
||||
self.assertEqual(phase, "ticket_locked_file")
|
||||
texts = [x.get("content") or "" for x in self.groups.sent if x.get("kind") == "text"]
|
||||
self.assertTrue(any("不能再上传附件" in t for t in texts))
|
||||
self.assertFalse(self.ledger.get_ticket(work_order_no=wo).attachments)
|
||||
|
||||
def test_dissolve_then_cannot_recreate(self) -> None:
|
||||
wo, chat = self._open_group()
|
||||
phase = handle_group_collab(
|
||||
InboundMessage(
|
||||
sender_id="sales1",
|
||||
message_id="dis1",
|
||||
content="这个群解散吧",
|
||||
chat_id=chat,
|
||||
chat_type="group",
|
||||
),
|
||||
flow=self.flow,
|
||||
ledger=self.ledger,
|
||||
injected_intent="dissolve",
|
||||
)
|
||||
self.assertEqual(phase, "group_dissolved")
|
||||
self.assertTrue(any(x.get("kind") == "dismiss" for x in self.groups.sent))
|
||||
self.assertTrue(self.ledger.get_ticket(work_order_no=wo).collab_ended)
|
||||
sess = self.flow.session_of("sales1")
|
||||
sess.allowed = (copy.BTN_PULL_COLLAB,)
|
||||
again = self.flow.on_button(
|
||||
sender_id="sales1",
|
||||
action=copy.BTN_PULL_COLLAB,
|
||||
reply=self._reply,
|
||||
)
|
||||
self.assertEqual(again, "collab_ended")
|
||||
self.assertIn("不能再拉群", "\n".join(self.replies))
|
||||
|
||||
def test_group_oral_keeps_inquiry_facts_when_snapshot_skinny(self) -> None:
|
||||
wo, chat = self._open_group()
|
||||
real_find = self.ledger.find_by_collab_chat
|
||||
|
||||
def skinny(*, chat_id: str):
|
||||
ticket = real_find(chat_id=chat_id)
|
||||
if ticket is None:
|
||||
return None
|
||||
return {
|
||||
"found": True,
|
||||
"work_order_no": ticket.work_order_no,
|
||||
"sales_wecom_id": ticket.sales_wecom_id,
|
||||
"collab_chat_id": ticket.collab_chat_id,
|
||||
"collab_member_ids": list(ticket.collab_member_ids),
|
||||
"collab_facts": dict(ticket.collab_facts),
|
||||
}
|
||||
|
||||
self.ledger.find_by_collab_chat = skinny # type: ignore[method-assign]
|
||||
self.flow._by_ticket.clear()
|
||||
self.flow._by_sender.clear()
|
||||
phase = handle_group_collab(
|
||||
InboundMessage(
|
||||
sender_id="sales1",
|
||||
message_id="oral-keep-facts-1",
|
||||
content="不含电、不含油、随时可提",
|
||||
chat_id=chat,
|
||||
chat_type="group",
|
||||
),
|
||||
flow=self.flow,
|
||||
ledger=self.ledger,
|
||||
injected_intent="collab_fields",
|
||||
)
|
||||
self.assertEqual(phase, "collab_field_saved")
|
||||
texts = "\n".join(x.get("content") or "" for x in self.groups.sent if x.get("kind") == "text")
|
||||
self.assertIn("上海", texts)
|
||||
self.assertIn("洛杉矶", texts)
|
||||
self.assertIn("随时可提", texts)
|
||||
self.assertNotRegex(texts, r"起运港:\s*-")
|
||||
self.assertIn("@吴吉林 @赵产品", texts)
|
||||
self.assertNotRegex(texts, r"^@销售(\s|$)")
|
||||
briefs = [
|
||||
x
|
||||
for x in self.groups.sent
|
||||
if x.get("kind") == "text" and "询价字段" in (x.get("content") or "")
|
||||
]
|
||||
self.assertTrue(briefs)
|
||||
self.assertIn("sales1", briefs[-1].get("mention_userids") or [])
|
||||
self.assertIn("prod4", briefs[-1].get("mention_userids") or [])
|
||||
self.assertNotIn("AI001", briefs[-1].get("mention_userids") or [])
|
||||
|
||||
def test_group_oral_oil_power_ready_is_saved(self) -> None:
|
||||
wo, chat = self._open_group()
|
||||
n = len([x for x in self.groups.sent if x.get("kind") == "text"])
|
||||
phase = handle_group_collab(
|
||||
InboundMessage(
|
||||
sender_id="sales1",
|
||||
message_id="oral-collab-1",
|
||||
content="不含电、不含油、随时可提",
|
||||
chat_id=chat,
|
||||
chat_type="group",
|
||||
),
|
||||
flow=self.flow,
|
||||
ledger=self.ledger,
|
||||
injected_intent="collab_fields",
|
||||
)
|
||||
self.assertEqual(phase, "collab_field_saved")
|
||||
ticket = self.ledger.get_ticket(work_order_no=wo)
|
||||
self.assertEqual(ticket.collab_facts.get("是否含电"), "否")
|
||||
self.assertEqual(ticket.collab_facts.get("是否含油"), "否")
|
||||
self.assertEqual(ticket.collab_facts.get("货好时间"), "随时可提")
|
||||
texts = [x.get("content") or "" for x in self.groups.sent if x.get("kind") == "text"]
|
||||
self.assertGreater(len(texts), n)
|
||||
|
||||
def test_collab_facts_are_projected_for_quote_sheet(self) -> None:
|
||||
from agent.policy.quote_overlay import overlay_labeled_facts
|
||||
from agent.policy.sea_group_ops import _facts_for_fill
|
||||
from openpyxl import Workbook, load_workbook
|
||||
|
||||
filled = _facts_for_fill(
|
||||
{"起运港": "DONGGUAN", "目的港": "海防"},
|
||||
{"货好时间": "随时可提", "是否含油": "否", "是否含电": "否", "HS编码": "8479"},
|
||||
)
|
||||
self.assertEqual(filled["cargo_ready_date"], "随时可提")
|
||||
self.assertEqual(filled["hs_code"], "8479")
|
||||
self.assertIn("油:否", filled["oil_elec_mag"])
|
||||
self.assertIn("电:否", filled["oil_elec_mag"])
|
||||
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
wb = Workbook()
|
||||
ws = wb.active
|
||||
ws["A1"] = "*货好时间/Cargo Ready Date:"
|
||||
ws["A2"] = "*商品海关编码/HS Code:"
|
||||
ws["A3"] = "*是否含油,电,磁/OIL,ELEC,MAG:"
|
||||
path = str(Path(tempfile.mkdtemp(prefix="collab-sheet-")) / "src.xlsx")
|
||||
wb.save(path)
|
||||
out = overlay_labeled_facts(filepath=path, facts=filled)
|
||||
painted = load_workbook(out["filepath"])
|
||||
sheet = painted.active
|
||||
self.assertEqual(sheet["B1"].value, "随时可提")
|
||||
self.assertEqual(sheet["B2"].value, "8479")
|
||||
self.assertIn("油:否", str(sheet["B3"].value or ""))
|
||||
|
||||
def test_stale_deal_card_speaks(self) -> None:
|
||||
from agent.policy.sea_group_ops import explain_deal_click
|
||||
|
||||
self._open_group()
|
||||
sess = self.flow.session_of("sales1")
|
||||
sess.deal_version = 2
|
||||
sess.collab_chat_id = sess.collab_chat_id or "wr_sea_1"
|
||||
reason = explain_deal_click(
|
||||
flow=self.flow,
|
||||
sender_id="sales1",
|
||||
action=copy.BTN_DEAL,
|
||||
card_meta={"event_key": f"deal:{sess.work_order_no}:v1"},
|
||||
sess=sess,
|
||||
)
|
||||
self.assertEqual(reason, "stale_deal")
|
||||
texts = [x.get("content") or "" for x in self.groups.sent if x.get("kind") == "text"]
|
||||
self.assertTrue(any("已过期" in t for t in texts))
|
||||
|
||||
def test_parse_group_file_fills_media(self) -> None:
|
||||
from agent.channel.wecom.parse import parse_plain_xml
|
||||
|
||||
xml = (
|
||||
"<xml><FromUserName>prod4</FromUserName><CreateTime>1</CreateTime>"
|
||||
"<MsgType>file</MsgType><MediaId>mid-x</MediaId>"
|
||||
"<FileName>报价.xlsx</FileName><ChatId>wr_sea_1</ChatId></xml>"
|
||||
)
|
||||
msg = parse_plain_xml(xml)
|
||||
self.assertEqual(msg.chat_type, "group")
|
||||
self.assertEqual(msg.chat_id, "wr_sea_1")
|
||||
self.assertEqual(msg.media.get("filename"), "报价.xlsx")
|
||||
self.assertEqual(msg.media.get("media_id"), "mid-x")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user