From dda210c219d7ac7eca2812061da0290b81dceff1 Mon Sep 17 00:00:00 2001 From: linmianxun <634841751@qq.com> Date: Tue, 13 Oct 2026 14:56:09 +0800 Subject: [PATCH] =?UTF-8?q?=E7=A9=BA=E8=BF=90=E9=99=84=E4=BB=B6=E6=8A=A5?= =?UTF-8?q?=E4=BB=B7=E9=94=81=E8=88=B1=E5=89=8D=E5=85=88=E6=8F=90=E7=A4=BA?= =?UTF-8?q?=E8=88=AA=E7=BA=BF=E6=94=B9=E5=8F=91=E7=BA=AF=E6=96=87=E5=AD=97?= =?UTF-8?q?=E6=8A=A5=E4=BB=B7=EF=BC=8C=E9=81=BF=E5=85=8D=E9=99=84=E4=BB=B6?= =?UTF-8?q?=E9=87=91=E9=A2=9D=E6=8B=86=E4=B8=8D=E5=87=BA=E5=AF=BC=E8=87=B4?= =?UTF-8?q?=E9=94=81=E8=88=B1=E5=A4=B1=E8=B4=A5=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cursor --- inquiry-agent/agent/policy/air_group_ops.py | 45 ++++++++++++++- inquiry-agent/agent/policy/inquiry_copy.py | 61 +++++++++++++++++++- inquiry-agent/tests/test_air_group_collab.py | 2 + 3 files changed, 105 insertions(+), 3 deletions(-) diff --git a/inquiry-agent/agent/policy/air_group_ops.py b/inquiry-agent/agent/policy/air_group_ops.py index 99dda29..c4129c8 100644 --- a/inquiry-agent/agent/policy/air_group_ops.py +++ b/inquiry-agent/agent/policy/air_group_ops.py @@ -699,6 +699,9 @@ def apply_air_file( from agent.policy.event_exception import clear_wait clear_wait(flow.ledger, work_order_no=no) + # 附件不拆金额:锁舱常失败。产品口径改为提示航线改发纯文字报价后再锁。 + tip = copy.air_attachment_quote_lock_tip(work_order_no=no) + send_group_text(client, chat_id, tip) emit_group_deal(flow=flow, ticket=view, quote=quote) return "airline_file_quote" @@ -952,7 +955,8 @@ def apply_air_lock( return "no_quote" # 主账在出站前发现三项不齐:只请补,不当 TMS 故障,不通知 IT。 reason_text = str(out.get("failReason") or "") - if str(out.get("error") or "") == "lock_params_incomplete" or "锁舱参数不完整" in reason_text: + err_code = str(out.get("error") or "") + if err_code == "lock_params_incomplete" or "锁舱参数不完整" in reason_text: labels = missing or _labels_from_incomplete_reason(reason_text) _remember_partial_lock( flow, @@ -964,8 +968,45 @@ def apply_air_lock( ) send_group_text(group_client_of(flow), chat_id, copy.air_lock_need_params(labels)) return "wait_lock_params" + # 附件/手工报价、运价库无舱位:引导纯文字报价,不算系统异常、不通知 IT。 + src_label = str((quote or {}).get("source_label") or (quote or {}).get("source") or "") + need_text_quote = ( + err_code in {"lock_incomplete", "lock_fail"} + and ( + "航线附件报价" in src_label + or "附件" in src_label + or any( + x in reason_text + for x in ( + "手工报价", + "无匹配舱位", + "无匹配报价", + "运价库无匹配", + "标准运价库无匹配", + ) + ) + ) + ) + if need_text_quote: + send_group_text( + group_client_of(flow), + chat_id, + copy.air_lock_need_text_quote( + work_order_no=no, operator=name, reason=reason_text + ), + ) + return "need_text_quote" + if err_code == "lock_incomplete": + send_group_text( + group_client_of(flow), + chat_id, + copy.air_lock_incomplete( + work_order_no=no, operator=name, reason=reason_text + ), + ) + return "lock_incomplete" # 多段是产品限制,不是 TMS 故障。只回原因,不标系统异常、不暂停工单。 - if str(out.get("error") or "") == "multi_no_lock": + if err_code == "multi_no_lock": send_group_text( group_client_of(flow), chat_id, diff --git a/inquiry-agent/agent/policy/inquiry_copy.py b/inquiry-agent/agent/policy/inquiry_copy.py index 6b10a53..b6401cd 100644 --- a/inquiry-agent/agent/policy/inquiry_copy.py +++ b/inquiry-agent/agent/policy/inquiry_copy.py @@ -3273,13 +3273,72 @@ def air_lock_fail(*, work_order_no: str, operator: str, reason: str) -> str: ) +def air_text_quote_for_lock_example(work_order_no: str = "") -> str: + """ + 需要锁舱时,请航线用纯文字报价的示例句。 + + 附件报价不拆金额、常锁不成;产品口径改为引导航线改发文字价。 + """ + no = (work_order_no or "").strip() or "工单号" + return f"{no},空运段,空运费 单价CNY99.00,数量100 ,金额CNY9900.00@询价小助手" + + +def air_attachment_quote_lock_tip(*, work_order_no: str) -> str: + """ + 航线刚发完 Excel/PDF 附件报价后的提示。 + + 附件只存档、不拆费用;若还要锁舱,必须再发一版纯文字报价。 + """ + no = (work_order_no or "").strip() or "WO" + example = air_text_quote_for_lock_example(no) + return ( + f"工单{no}附件报价已收。\n" + "如需锁舱,请航线人员改用纯文字报价(不要只发附件),例如:\n" + f"{example}" + ) + + +def air_lock_need_text_quote(*, work_order_no: str, operator: str = "", reason: str = "") -> str: + """ + 附件/手工报价锁舱走不通时:引导航线先发文字报价,不算系统异常、不通知 IT。 + + 故意不回主账/TMS 原文「原因」(如 AIR-OPT / 运价库无匹配),只留文字报价指引。 + reason 参数保留兼容旧调用,出站话术不用。 + """ + _ = reason + no = (work_order_no or "").strip() or "WO" + who = (operator or "").strip() + lines = [f"工单{no} 还不能锁舱。"] + if who: + lines.append(f"操作人:{who}") + lines.append("如需锁舱,请航线人员先发纯文字报价,例如:") + lines.append(air_text_quote_for_lock_example(no)) + lines.append("文字报价写入后再发锁舱口令(可带舱位、航司、航班号)。") + return "\n".join(lines) + + def air_lock_incomplete(*, work_order_no: str, operator: str, reason: str = "") -> str: """ 锁舱参数不齐(纯手工价、运价库无舱位等):业务补问,不算系统异常,不惊动 IT。 - 航线按示例回舱位+航司+航班后即可再锁。 + 附件/无舱位场景改引导纯文字报价;有 TMS 舱位时仍可补 AIR-OPT 三项。 """ why = (reason or "").strip() + blob = why + if any( + x in blob + for x in ( + "手工报价", + "附件", + "无匹配舱位", + "无匹配报价", + "运价库无匹配", + "标准运价库无匹配", + ) + ): + return air_lock_need_text_quote( + work_order_no=work_order_no, operator=operator, reason=why + ) lines = [ f"工单{work_order_no} 还不能锁舱。", f"操作人:{operator}", diff --git a/inquiry-agent/tests/test_air_group_collab.py b/inquiry-agent/tests/test_air_group_collab.py index d3b508e..61391e8 100644 --- a/inquiry-agent/tests/test_air_group_collab.py +++ b/inquiry-agent/tests/test_air_group_collab.py @@ -1476,6 +1476,8 @@ class AirGroupCollabTests(unittest.TestCase): self.assertEqual(ticket.quote.get("quoteId"), "QT-AIR-1") self.assertEqual(ticket.status, "已报价") self.assertTrue(any(f"工单{wo}的1个附件已保存到后台工单" in t for t in self._texts())) + self.assertTrue(any("如需锁舱,请航线人员改用纯文字报价" in t for t in self._texts())) + self.assertTrue(any("空运费 单价CNY99.00" in t for t in self._texts())) self.assertTrue(any("报价单已发送,请确认成交状态" in t for t in self._texts())) def test_reactivate_after_airline_file_is_deal_not_blank_sheet(self) -> None: