目的地只留地名,货物数量不再用箱型,未成交原因去掉艾特。
口语里逗号后面的品名和包装不再粘在目的地上;件数被抽成箱型时改用件数;群里艾特机器人不记进未成交原因。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -516,16 +516,35 @@ def harvest_oral_collab(text: str) -> dict[str, str]:
|
||||
return out
|
||||
|
||||
|
||||
# 箱型原文:40HQ*1、1x40HQ、40尺高柜。货物数量不能收这种串。
|
||||
_CONTAINER_SPEC = re.compile(
|
||||
r"(?i)(20|40|45)\s*['\"′尺]?\s*(HQ|GP|NOR|RH|HC|高柜|平柜|柜)"
|
||||
)
|
||||
|
||||
|
||||
def looks_like_container_spec(text: str) -> bool:
|
||||
"""这串是箱型箱量,不是件数或吨数。20件、20吨不算。"""
|
||||
return bool(_CONTAINER_SPEC.search(text or ""))
|
||||
|
||||
|
||||
def project_sea_cargo_qty(facts: dict[str, str] | None) -> dict[str, str]:
|
||||
"""
|
||||
海运货物数量投影:原话「15件」常被抽成空运「件数」。
|
||||
海运货物数量投影。
|
||||
|
||||
货量已有则不覆盖。件数有、货量空时,货量用件数。不猜吨位、不改空运校验。
|
||||
抽字段常把「20件」放进件数,又把「40HQ*1」写进货量。
|
||||
货量只要长得像箱型,就丢掉,改用件数里的数量;件数也是箱型则不搬。
|
||||
箱型箱量空着时,把这串箱型补到箱型箱量。不限具体件数。
|
||||
"""
|
||||
merged = dict(facts or {})
|
||||
qty = (merged.get("货量") or merged.get("货物数量") or "").strip()
|
||||
pieces = (merged.get("件数") or "").strip()
|
||||
if not qty and pieces:
|
||||
if looks_like_container_spec(qty):
|
||||
if not (merged.get("箱型箱量") or "").strip():
|
||||
merged["箱型箱量"] = qty
|
||||
merged.pop("货量", None)
|
||||
merged.pop("货物数量", None)
|
||||
qty = ""
|
||||
if not qty and pieces and not looks_like_container_spec(pieces):
|
||||
merged["货量"] = pieces
|
||||
return merged
|
||||
|
||||
@@ -562,6 +581,38 @@ def _pricing_required_keys(
|
||||
return []
|
||||
|
||||
|
||||
_PLACE_KEYS = ("起运港", "目的港")
|
||||
_PACK_IN_PLACE = ("散货", "纸箱", "木箱", "托盘", "卡板", "木托")
|
||||
_PRICE_TAIL = re.compile(r"(的价格|的报价|价格|报价)$")
|
||||
|
||||
|
||||
def peel_trailing_place_clauses(facts: dict[str, str] | None) -> dict[str, str]:
|
||||
"""
|
||||
起运地/目的地只留逗号前的地名。
|
||||
|
||||
口语「目的地吉隆坡,普货,散货的价格」会被抽成一整串目的地。
|
||||
逗号后面的品名、包装、价格不是地名,拆回去;已有品名/包装不覆盖。
|
||||
"""
|
||||
merged = dict(facts or {})
|
||||
for key in _PLACE_KEYS:
|
||||
raw = (merged.get(key) or "").strip()
|
||||
parts = [p.strip() for p in re.split(r"[,,、]", raw) if p.strip()]
|
||||
if len(parts) <= 1:
|
||||
continue
|
||||
place = parts[0]
|
||||
for part in parts[1:]:
|
||||
part = _PRICE_TAIL.sub("", part).strip()
|
||||
if not part or part in {"价格", "报价", "查价"}:
|
||||
continue
|
||||
if part in _PACK_IN_PLACE and not (merged.get("包装方式") or "").strip():
|
||||
merged["包装方式"] = part
|
||||
continue
|
||||
if not (merged.get("品名") or "").strip() and not looks_like_container_spec(part):
|
||||
merged["品名"] = part
|
||||
merged[key] = place
|
||||
return merged
|
||||
|
||||
|
||||
def display_name(internal_key: str, business_line: str = "AIR") -> str:
|
||||
"""补问时给销售看的名字。"""
|
||||
line = (business_line or "").upper()
|
||||
@@ -589,7 +640,7 @@ def validate_required_fields(
|
||||
"""
|
||||
_ = require_formal
|
||||
contract = require_formal_contract("inquiry-required-fields-v1")
|
||||
normalized = apply_quote_date_default(normalize_facts(facts))
|
||||
normalized = peel_trailing_place_clauses(apply_quote_date_default(normalize_facts(facts)))
|
||||
line = (business_line or "").strip().upper()
|
||||
if line == "SEA":
|
||||
normalized = project_sea_cargo_qty(normalized)
|
||||
|
||||
Reference in New Issue
Block a user