陆运选项不全时改为复制填空清单,方便销售按编号回填。

停发填写样例图,同群只跟当前工单,重量体积统一为 KG/CBM。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-23 17:10:13 +08:00
co-authored by Cursor
parent 2334524bb8
commit 378bb892b3
33 changed files with 1204 additions and 265 deletions
+65 -20
View File
@@ -94,6 +94,13 @@ def operator_name(flow: Any, sender_id: str) -> str:
return (sender_id or "").strip() or "航线"
def is_air_ticket(ticket: object) -> bool:
"""按群找回的工单必须是空运。陆运旧单不能当空运当前单补含电含磁。"""
view = ticket_view(ticket)
line = str(view.get("business_line") or view.get("businessLine") or "").strip().upper()
return not line or line == "AIR"
def can_fill_fields(flow: Any, ticket: object, sender_id: str) -> bool:
return is_sales(ticket, sender_id) or is_airline(flow, sender_id)
@@ -147,6 +154,8 @@ def activate_ticket(
ticket = hydrate_ticket(flow, _ticket_from_activate(book, no, out))
if is_handoff_ticket(ticket):
return None, "handoff_ignore"
# 先落默认协同,再决定要不要发摘要。发文件切单不说话,工单上仍要有含电/含磁。
_seed_air_activate_collab(flow, ticket)
if speak:
send_air_brief_and_sheet(flow=flow, ticket=ticket, chat_id=chat_id)
_watch_archive_room(chat_id, no)
@@ -156,6 +165,54 @@ def activate_ticket(
return ticket, "activated"
def _collab_slot_empty(val: object) -> bool:
"""协同项没写,或只写了横杠。横杠是摘要占位,不能当成已经填过。"""
return str(val or "").strip() in {"", "-", "—", "-"}
def _seed_air_activate_collab(flow: Any, ticket: object) -> dict[str, str]:
"""
空运群激活时预填协同,并记到主账。
私聊已经写过的含电/含磁先带上。
货物品名正好是「普货」、该项还空着,默认否。
群里已经记下的值不覆盖。只补空项。
"""
from agent.schema.field_validate import (
AIR_COLLAB_FIELDS,
air_general_cargo_collab_defaults,
pick_collab_from_facts,
)
view = ticket_view(ticket)
no = ticket_no(ticket)
facts = dict(view.get("facts") or {})
# 主账有时品名在货物名称列,事实里没带「品名」。群摘要仍能看到普货。
if not str(facts.get("品名") or facts.get("货物品名") or "").strip():
cargo = str(view.get("goodsName") or view.get("goods_name") or "").strip()
if cargo:
facts["品名"] = cargo
seeded = pick_collab_from_facts(facts, AIR_COLLAB_FIELDS)
for key, val in air_general_cargo_collab_defaults(facts).items():
if _collab_slot_empty(seeded.get(key)):
seeded[key] = val
collab = dict(view.get("collab_facts") or {})
to_write: dict[str, str] = {}
for key, val in seeded.items():
if val and _collab_slot_empty(collab.get(key)):
collab[key] = val
to_write[key] = val
if to_write:
patcher = getattr(getattr(flow, "ledger", None), "patch_collab_facts", None)
if callable(patcher):
patcher(
work_order_no=no,
facts=to_write,
sender_id=str(view.get("sales_wecom_id") or view.get("salesWecomId") or ""),
)
return collab
def send_air_brief_and_sheet(
*,
flow: Any,
@@ -177,25 +234,7 @@ def send_air_brief_and_sheet(
if room and not view.get("collab_chat_id"):
view["collab_chat_id"] = room
facts = dict(view.get("facts") or {})
from agent.schema.field_validate import AIR_COLLAB_FIELDS, pick_collab_from_facts
seeded = pick_collab_from_facts(facts, AIR_COLLAB_FIELDS)
collab = dict(view.get("collab_facts") or {})
if seeded:
changed = False
for k, v in seeded.items():
if v and not str(collab.get(k) or "").strip():
collab[k] = v
changed = True
if changed:
view["collab_facts"] = collab
patcher = getattr(getattr(flow, "ledger", None), "patch_collab_facts", None)
if callable(patcher):
patcher(
work_order_no=no,
facts=seeded,
sender_id=str(view.get("sales_wecom_id") or view.get("salesWecomId") or ""),
)
collab = _seed_air_activate_collab(flow, view)
view["collab_facts"] = collab
view_q = dict(view.get("quote") or {})
override = dict(quote_override or {})
@@ -358,6 +397,9 @@ def apply_air_fields(*, flow: Any, ticket: object, text: str, sender_id: str, ch
ticket = hydrate_ticket(flow, ticket)
no = ticket_no(ticket)
client = group_client_of(flow)
if not is_air_ticket(ticket):
send_group_text(client, chat_id, copy.air_need_work_order())
return "need_work_order"
stopped = halt_closed_group(flow=flow, ticket=ticket, chat_id=chat_id)
if stopped:
return stopped
@@ -369,7 +411,10 @@ def apply_air_fields(*, flow: Any, ticket: object, text: str, sender_id: str, ch
send_group_text(client, chat_id, copy.air_fields_not_recognized())
return "fields_empty"
flow.ledger.patch_collab_facts(work_order_no=no, facts=fields, sender_id=sender_id)
ticket2 = current_ticket(flow, chat_id) or ticket
# 必须回读本单。按群再查会拿到同群更早的陆运/海运旧单(测服 WO202609230007)。
getter = getattr(flow.ledger, "get_ticket", None)
reloaded = getter(work_order_no=no) if callable(getter) else None
ticket2 = hydrate_ticket(flow, reloaded or ticket)
view = ticket_view(ticket2)
collab = dict(view.get("collab_facts") or {})
collab.update(fields)