陆运多条线路按段展示 TMS 费用名和时效,选定后再写回该条成本。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-22 18:28:29 +08:00
co-authored by Cursor
parent e769eb9c1d
commit 49faa35f47
23 changed files with 2323 additions and 55 deletions
+95 -21
View File
@@ -901,6 +901,38 @@ def sea_bucket_fee_lines(quote: dict[str, Any] | None) -> list[str]:
return sections
def _land_grouped_fee_lines(quote: dict[str, Any] | None) -> list[str]:
"""陆运:每段先写始发→目的,再写该段 TMS 费用名。"""
from agent.schema.land_route_options import land_leg_fee_blocks
src = dict(quote or {})
header_ccy = str(src.get("currency") or src.get("currencyCode") or "").strip()
lines: list[str] = []
for title, fees, eta in land_leg_fee_blocks(src):
lines.append(title)
for fee in fees:
name = str(fee.get("name") or "").strip()
amount = str(fee.get("amount") or "").strip()
line_ccy = str(fee.get("currency") or fee.get("currencyCode") or header_ccy).strip()
if name and amount and amount != "-":
lines.append(f"{name}:{money_text(amount, line_ccy)}")
if eta:
lines.append(f"时效:{eta}")
return lines
def _is_land_surface(*, quote: dict[str, Any] | None = None, business_line: str = "", transport_mode: str = "") -> bool:
"""陆运销售面:有 landSegments / 陆运业务线就按段出明细。"""
src = dict(quote or {})
if src.get("landSegments") or src.get("land_segments") or src.get("selected_land_route"):
return True
raw_line = (business_line or "").strip()
if raw_line and scheme_transport_mode(raw_line, fallback="") == "陆运":
return True
mode = (transport_mode or "").strip()
return bool(mode) and scheme_transport_mode(mode, fallback="") == "陆运"
def quote_area_fee_text(
quote: dict[str, Any] | None,
business_line: str = "",
@@ -915,8 +947,13 @@ def quote_area_fee_text(
header_ccy = str(src.get("currency") or src.get("currencyCode") or "").strip()
total = money_text(str(src.get("total") or "-").strip() or "-", header_ccy)
bucket_lines = sea_bucket_fee_lines(src)
land_lines = _land_grouped_fee_lines(src) if _is_land_surface(
quote=src, business_line=business_line, transport_mode=transport_mode
) else []
if bucket_lines:
fee_lines = list(bucket_lines)
elif land_lines:
fee_lines = list(land_lines)
else:
fee_lines = [f"{name}:{amount}" for name, amount in quote_fee_lines(src)]
fee_lines.append(f"{_total_label(src, business_line, transport_mode)}:{total}")
@@ -968,20 +1005,28 @@ def sea_quote_detail_text(
"""
完整价格明细文本:来源、工单号、费用行、总价。海运/陆运/空运共用。
时效、报价有效期只在卡片上出现,这里不写,避免和卡重复。
陆运分段时效跟在各段费用后面,用 TMS 原文。海运时效、报价有效期仍只在卡片上。
空运:业务确认没有时效,总销售价下面只留报价有效期。
不受卡片引用条字数限制。first_or_same 只兼容调用方。
"""
_ = first_or_same, history_work_order_no, status
src = dict(quote or {})
source = str(src.get("source_label") or "TMS标准报价").replace(" ", "")
lines = [source, f"{work_order_no}价格明细"]
if (business_line or "").upper() == "AIR":
line = (business_line or "").upper()
if line == "AIR":
from agent.schema.air_route_options import route_codes
route = route_codes(None, {}, src)
if route:
lines.append(route)
if line == "LAND":
land_line = str(src.get("route_line") or "").strip()
if land_line:
lines.append(land_line)
lines.append(quote_area_fee_text(src, business_line=business_line or "SEA"))
if line == "AIR":
lines.append(f"报价有效期:{tms_valid_until_display(src)}")
return "\n".join(lines)
@@ -1048,10 +1093,10 @@ def quote_detail_lines(
transport_mode: str = "",
) -> list[str]:
"""
价格明细正文:TMS 费用、总销售价、时效、有效期。
价格明细正文:TMS 费用、总销售价一行、有效期;海运/陆运再加时效。
给文本兜底和企微引用条共用,禁止在这里编时效/有效期。
价格卡不展示工单状态,也不展示相同询价内容检索。
空运业务确认没有时效,这里不写。禁止编时效/有效期。
总销售价只写一次。价格卡不展示工单状态,也不展示相同询价内容检索。
"""
_ = status, first_or_same, history_work_order_no
src = dict(quote or {})
@@ -1060,13 +1105,18 @@ def quote_detail_lines(
bucket_lines = sea_bucket_fee_lines(src) if (business_line or "").upper() == "SEA" or (
transport_mode or ""
).strip() == "海运" else []
land_lines = _land_grouped_fee_lines(src) if _is_land_surface(
quote=src, business_line=business_line, transport_mode=transport_mode
) else []
if bucket_lines:
lines = list(bucket_lines)
elif land_lines:
lines = list(land_lines)
else:
lines = [f"{name}:{amount}" for name, amount in quote_fee_lines(src)]
lines.append(f"{_total_label(src, business_line, transport_mode)}:{total}")
lines.append(f"{_total_label(src, business_line, transport_mode)}:{total}")
lines.append(f"时效:{tms_eta_display(src)}")
if not _is_air_surface(business_line=business_line, transport_mode=transport_mode):
lines.append(f"时效:{tms_eta_display(src)}")
lines.append(f"报价有效期:{tms_valid_until_display(src)}")
return lines
@@ -1146,7 +1196,7 @@ def tms_hit_wecom_payload(
价格明细
┌ 引用条:空运费 / 其它费用 / 总销售价
时效、报价有效期
报价有效期(空运不写时效)
协同说明(普通文字,贴按钮上方)
按钮
@@ -1159,7 +1209,6 @@ def tms_hit_wecom_payload(
source = str(src.get("source_label") or "TMS标准报价").replace(" ", "")
meta = "\n".join(
[
f"时效:{tms_eta_display(src)}",
f"报价有效期:{tms_valid_until_display(src)}",
"",
collab_hint(work_order_no),
@@ -1770,13 +1819,18 @@ def scheme_card(
body,
"价格明细",
quote_area_fee_text(src, transport_mode=transport_mode),
f"时效:{eta or '-'}",
f"报价有效期:{valid}",
"请确认是否采用该报价方案:",
f"【{BTN_ADOPT_EXCEL_CARD}】",
f"【{BTN_ADOPT_PDF_CARD}】",
f"【{BTN_REJECT}】",
]
if not _is_air_surface(transport_mode=transport_mode):
lines.append(f"时效:{eta or '-'}")
lines.extend(
[
f"报价有效期:{valid}",
"请确认是否采用该报价方案:",
f"【{BTN_ADOPT_EXCEL_CARD}】",
f"【{BTN_ADOPT_PDF_CARD}】",
f"【{BTN_REJECT}】",
]
)
return "\n".join(lines)
@@ -1806,6 +1860,19 @@ def scheme_transport_mode(business_line: str = "", fallback: str = "空运") ->
return (fallback or "空运").strip() or "空运"
def _is_air_surface(*, business_line: str = "", transport_mode: str = "") -> bool:
"""
空运销售面:业务确认没有时效,展示层一律不写。
海运/陆运仍保留时效。两边都空时不算空运,避免误藏海运/陆运卡。
"""
raw_line = (business_line or "").strip()
if raw_line and scheme_transport_mode(raw_line, fallback="") == "空运":
return True
mode = (transport_mode or "").strip()
return bool(mode) and scheme_transport_mode(mode, fallback="") == "空运"
def scheme_horizontal_rows(
*,
work_order_no: str,
@@ -1857,7 +1924,6 @@ def scheme_horizontal_rows(
{"keyname": "航线", "value": _clip(_air_scheme_route(q, src), 26)},
{"keyname": "货物", "value": _clip(f"{cargo};{pieces};{weight};{volume}", 26)},
{"keyname": "包装", "value": _clip(pack, 26)},
{"keyname": "时效", "value": _clip(tms_eta_display(q), 26)},
{"keyname": "报价有效期", "value": _clip(tms_valid_until_display(q), 26)},
]
@@ -2044,19 +2110,22 @@ def deal_horizontal_rows(
transport_mode: str = "",
) -> list[dict[str, str]]:
"""
成交跟进卡字段:总销售价、时效、报价有效期。
成交跟进卡字段:总销售价、报价有效期;海运/陆运再加时效。
空运业务确认没有时效,这里不写。
不要历史报价 / 工单状态 / 来源。来源只留卡片顶部红字。
"""
_ = facts
src = dict(quote or {})
header_ccy = str(src.get("currency") or src.get("currencyCode") or "").strip()
total = money_text(str(src.get("total") or "-").strip() or "-", header_ccy)
return [
rows = [
{"keyname": _total_label(src, business_line, transport_mode), "value": _clip(total, 26)},
{"keyname": "时效", "value": _clip(tms_eta_display(src), 26)},
{"keyname": "报价有效期", "value": _clip(tms_valid_until_display(src), 26)},
]
if not _is_air_surface(business_line=business_line, transport_mode=transport_mode):
rows.append({"keyname": "时效", "value": _clip(tms_eta_display(src), 26)})
rows.append({"keyname": "报价有效期", "value": _clip(tms_valid_until_display(src), 26)})
return rows
def deal_card(
@@ -2098,7 +2167,7 @@ def deal_wecom_payload(
成交跟进卡:对齐现网 button_interaction。
顶部来源红字;标题询价工单号;副标题跟进成交;三个按钮。
字段只要总销售价、时效、有效期。
字段:总销售价、报价有效期;海运/陆运再加时效。空运不写时效。
"""
src = dict(quote or {})
source = str(src.get("source_label") or "TMS标准报价").replace(" ", "")
@@ -2406,6 +2475,7 @@ def air_group_brief(
空运群激活摘要:与海运进群同一套结构,字段用空运。
先 @ 销售和航线,再工单号 + 询价字段 + 协同补充(含电/含磁)+ TMS报价。
TMS 段跟单聊同一套线路分项,总销售价只一行;禁止用「空运费=总价」冒充。
不写「当前工单/询价要点/现在等到哪一步」。
"""
no = (work_order_no or "").strip()
@@ -2433,6 +2503,10 @@ def air_group_brief(
product_quote=product_quote,
has_price=has_price,
)
if tms:
from agent.schema.air_route_options import project_group_air_quote
tms = project_group_air_quote(tms, facts)
lines.append("")
lines.append("TMS报价:")
if tms and (tms.get("total") or quote_fee_lines(tms)):