员工岗位可重新启用;调价认空运航线;会话存档跳号补拉;陆运报价按车型列回填
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -186,6 +186,20 @@ _TIGHTEN_LABELS = tuple(
|
||||
)
|
||||
)
|
||||
_FEE_NAME = re.compile(r"^\*?[\u4e00-\u9fffA-Za-z0-9]{1,12}费$")
|
||||
# 识图/标签错位:报价编号、工单号、长数字进了起运地/目的地。三字码航线不动。
|
||||
_QUOTE_LIKE_PLACE = re.compile(
|
||||
r"^(?:"
|
||||
r"(?:WO|VT)\d{10,}"
|
||||
r"|QT[-_]?\d{6,}"
|
||||
r"|\d{8,}"
|
||||
r"|[A-Za-z]{1,6}\d{8,}"
|
||||
r")$",
|
||||
re.I,
|
||||
)
|
||||
_QUOTE_NO_IN_TEXT = re.compile(
|
||||
r"(?:报价编号|报价单编号|RFQ\s*No\.?|Quote\s*No\.?)\s*[::]?\s*([A-Za-z0-9\-_]{6,})",
|
||||
re.I,
|
||||
)
|
||||
_SHEET_STOP = (
|
||||
"报价明细",
|
||||
"报价条件",
|
||||
@@ -455,6 +469,64 @@ def _drop_fee_place_values(facts: dict[str, str]) -> dict[str, str]:
|
||||
return out
|
||||
|
||||
|
||||
def looks_like_quote_id_place(raw: str | None) -> bool:
|
||||
"""
|
||||
是否像报价编号/工单号,而不是起运地或目的地。
|
||||
|
||||
识图常把「报价编号:QT…」错贴到起运地。SZX、南京、SZX-CGK 不算。
|
||||
"""
|
||||
text = str(raw or "").strip()
|
||||
if not text:
|
||||
return False
|
||||
if "报价编号" in text or "报价单编号" in text:
|
||||
return True
|
||||
# 去掉常见装饰后再判
|
||||
cleaned = re.sub(r"[((]\s*(?:必填|非必填)\s*[))]", "", text).strip(" \t::,,。;;")
|
||||
if not cleaned:
|
||||
return False
|
||||
if _QUOTE_LIKE_PLACE.match(cleaned):
|
||||
return True
|
||||
# 纯数字超过 7 位多半是系统编号,不是港口名
|
||||
if re.fullmatch(r"\d{8,}", cleaned.replace(" ", "")):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def _quote_nos_mentioned(text: str) -> set[str]:
|
||||
"""原话/识图正文里出现的报价编号值。"""
|
||||
return {
|
||||
m.group(1).strip()
|
||||
for m in _QUOTE_NO_IN_TEXT.finditer(text or "")
|
||||
if (m.group(1) or "").strip()
|
||||
}
|
||||
|
||||
|
||||
def _drop_quote_like_place_values(
|
||||
facts: dict[str, str],
|
||||
*,
|
||||
source_text: str = "",
|
||||
) -> dict[str, str]:
|
||||
"""
|
||||
起运地/目的地若是报价编号、工单号或原话「报价编号:」同一值,丢掉。
|
||||
|
||||
副作用:无写库;只清洗本轮 facts。
|
||||
"""
|
||||
out = dict(facts or {})
|
||||
mentioned = _quote_nos_mentioned(source_text)
|
||||
for key in ("起运港", "起运地", "目的港", "目的地"):
|
||||
val = str(out.get(key) or "").strip()
|
||||
if not val:
|
||||
continue
|
||||
if not (looks_like_quote_id_place(val) or val in mentioned):
|
||||
continue
|
||||
out.pop(key, None)
|
||||
if key == "起运港":
|
||||
out.pop("originCode", None)
|
||||
if key == "目的港":
|
||||
out.pop("destinationCode", None)
|
||||
return out
|
||||
|
||||
|
||||
def extract_inquiry_snapshot(
|
||||
text: str,
|
||||
*,
|
||||
@@ -483,6 +555,7 @@ def extract_inquiry_snapshot(
|
||||
if str(facts.get(key) or "").strip():
|
||||
facts[key] = clip_inquiry_value(str(facts[key]))
|
||||
facts = _drop_fee_place_values(_clip_sheet_inquiry_facts(facts))
|
||||
facts = _drop_quote_like_place_values(facts, source_text=text)
|
||||
facts = _apply_air_added_services(text, facts, mode)
|
||||
subtype = str(facts.get("运输类型") or "").strip()
|
||||
return {
|
||||
@@ -539,6 +612,7 @@ def extract_inquiry_snapshot(
|
||||
if str(facts.get(key) or "").strip():
|
||||
facts[key] = clip_inquiry_value(str(facts[key]))
|
||||
facts = _drop_fee_place_values(_clip_sheet_inquiry_facts(facts))
|
||||
facts = _drop_quote_like_place_values(facts, source_text=text)
|
||||
facts = _apply_air_added_services(text, facts, mode)
|
||||
land_subtype = str((b_payload or {}).get("land_subtype") or facts.get("运输类型") or "").strip()
|
||||
if land_subtype and not str(facts.get("运输类型") or "").strip():
|
||||
@@ -1013,8 +1087,18 @@ _SKIP_FEE_NAMES = frozenset(
|
||||
# 航线常写「空运费 单价CNY99,数量100,金额CNY199」。单价和金额不是同一个数,必须认金额。
|
||||
# WO202610130003:协同句常见「空运费, KG, 单价CNY99.00, 数量100, 金额CNY9900」——费名与单价之间夹单位;
|
||||
# WO202610140008:航线写「增值费_板,单价CNY10…」——下划线单位;漏认会落到金额误抽,出单默认单位「票」。
|
||||
# WO202610190081:打板费,次,单价…——「次/人」等中文单位不在旧名单里时整句匹配失败,数量落成 1、金额=单价。
|
||||
# 数量/金额前也可能只有空格无逗号。漏匹配会落到 _RE_FEE_LINE,把单价 99 当成行金额、数量被补成 1。
|
||||
_FEE_UOM = r"KG|KGS|公斤|千克|TON|CBM|柜|票|板数|板|点|[A-Za-z]{1,8}"
|
||||
# 业务单位表(长词/带斜杠优先,避免「次/人」被拆成「次」):
|
||||
# 板数/点/人/天/货值/个/页数/柜/次/份/张/台/平方/件/计重/箱数/数量/千克/立方/卷/
|
||||
# 单数/票数/次/人/车/小时/人/小时/公里数/月/趟/周/批/项 + 英文重量体积码。
|
||||
# 末尾短中文通配:表外单位夹在费名与单价之间也不挡抽取。
|
||||
_FEE_UOM = (
|
||||
r"次/人|小时/人|板数|页数|箱数|单数|票数|公里数|货值|计重|平方|立方|千克|小时|"
|
||||
r"数量|KG|KGS|公斤|TON|CBM|柜|票|板|点|次|人|天|个|份|张|台|件|卷|车|月|趟|周|批|项|"
|
||||
r"托|套|袋|包|箱|"
|
||||
r"[A-Za-z]{1,8}|[\u4e00-\u9fff]{1,3}"
|
||||
)
|
||||
_RE_FEE_PRICED = re.compile(
|
||||
r"(?P<name>[\u4e00-\u9fffA-Za-z][\u4e00-\u9fffA-Za-z0-9/()()++\--]{0,24}费)"
|
||||
rf"(?:(?:\s*[,,、/]\s*|\s+|_)(?P<uom>{_FEE_UOM}))?"
|
||||
@@ -1251,7 +1335,9 @@ def _parse_fee_rows(text: str, baseline_currency: str = "") -> list[dict[str, st
|
||||
row[key] = val
|
||||
rows.append(row)
|
||||
|
||||
priced_hits = 0
|
||||
for hit in _RE_FEE_PRICED.finditer(raw):
|
||||
priced_hits += 1
|
||||
# 有「金额」用金额;只有单价时才用单价。禁止把单价误当成这一行的总金额。
|
||||
amount = (hit.group("amount") or "").strip() or (hit.group("unit") or "").strip()
|
||||
ccy = hit.group("amt_ccy") or hit.group("unit_ccy") or ""
|
||||
|
||||
Reference in New Issue
Block a user