销售侧汇总统一叫费用合计,并认准空运费的金额。
企微跳转改为账号密码登录,报价卡上的费用按原金额改回;航线按单价数量金额报价时不再记成海运费。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -210,8 +210,8 @@ class AirGroupCollabTests(unittest.TestCase):
|
||||
tms_quote=tms,
|
||||
)
|
||||
tms_block = text.split("TMS报价:", 1)[1]
|
||||
self.assertEqual(tms_block.count("总销售价:"), 1)
|
||||
self.assertIn("总销售价:CNY 183370.0", tms_block)
|
||||
self.assertEqual(tms_block.count("费用合计:"), 1)
|
||||
self.assertIn("费用合计:CNY 183370.0", tms_block)
|
||||
self.assertNotIn("时效", tms_block)
|
||||
self.assertIn("报价有效期:2026-09-30", tms_block)
|
||||
self.assertIn("请航线人员回复“确认报价”确定采用TMS报价。", text)
|
||||
@@ -266,8 +266,8 @@ class AirGroupCollabTests(unittest.TestCase):
|
||||
self.assertIn("运输费:CNY 11.0", tms_block)
|
||||
self.assertIn("提送货费:CNY 6.0", tms_block)
|
||||
self.assertIn("人工费:CNY 123.0", tms_block)
|
||||
self.assertIn("总销售价:CNY 183370", tms_block)
|
||||
self.assertEqual(tms_block.count("总销售价:"), 1)
|
||||
self.assertIn("费用合计:CNY 183370", tms_block)
|
||||
self.assertEqual(tms_block.count("费用合计:"), 1)
|
||||
self.assertNotIn("空运费", tms_block)
|
||||
|
||||
def test_air_group_brief_multi_route_does_not_borrow_other_air_freight(self) -> None:
|
||||
@@ -312,8 +312,45 @@ class AirGroupCollabTests(unittest.TestCase):
|
||||
tms_block = text.split("TMS报价:", 1)[1]
|
||||
self.assertNotIn("空运费", tms_block)
|
||||
self.assertNotIn("运输费", tms_block)
|
||||
self.assertIn("总销售价:CNY 200040", tms_block)
|
||||
self.assertEqual(tms_block.count("总销售价:"), 1)
|
||||
self.assertIn("费用合计:CNY 200040", tms_block)
|
||||
self.assertEqual(tms_block.count("费用合计:"), 1)
|
||||
|
||||
def test_work_order_plus_lost_reason_is_deal_not_activate(self) -> None:
|
||||
"""WO@助手 未成交,原因… 必须当关单,不能只激活。"""
|
||||
from agent.routing.air_group_intent import INTENT_ACTIVATE, INTENT_LOST
|
||||
|
||||
text = "WO202609220014@询价小助手 未成交,原因客户不想送货了"
|
||||
self.assertEqual(classify_air_group_text(text, allow_network=False), INTENT_LOST)
|
||||
from unittest.mock import patch
|
||||
|
||||
with patch(
|
||||
"agent.routing.air_group_intent._classify_with_model",
|
||||
return_value=INTENT_ACTIVATE,
|
||||
):
|
||||
self.assertEqual(
|
||||
classify_air_group_text(text, allow_network=True),
|
||||
INTENT_LOST,
|
||||
)
|
||||
|
||||
def test_activate_lost_and_reason_same_message(self) -> None:
|
||||
wo = self._create_air()
|
||||
phase = handle_air_group(
|
||||
self._msg(
|
||||
sender="sales1",
|
||||
text=f"{wo}@询价小助手 未成交,原因客户不想送货了",
|
||||
chat="wr_air_1",
|
||||
mid="lost-inline-wo",
|
||||
),
|
||||
flow=self.flow,
|
||||
ledger=self.ledger,
|
||||
)
|
||||
self.assertEqual(phase, "done")
|
||||
ticket = self.ledger.get_ticket(work_order_no=wo)
|
||||
self.assertEqual(ticket.status, "未成交")
|
||||
self.assertEqual(ticket.lost_reason, "客户不想送货了")
|
||||
joined = "\n".join(self._texts())
|
||||
self.assertIn("客户不想送货了", joined)
|
||||
self.assertNotIn("请补充未成交原因", joined)
|
||||
|
||||
def test_work_order_only_is_activate_not_quote(self) -> None:
|
||||
from agent.routing.air_group_intent import INTENT_ACTIVATE
|
||||
|
||||
@@ -218,7 +218,7 @@ class AirRouteOptionsTests(unittest.TestCase):
|
||||
business_line="AIR",
|
||||
)
|
||||
self.assertNotIn("空运费", detail)
|
||||
self.assertIn("总销售价", detail)
|
||||
self.assertIn("费用合计", detail)
|
||||
self.assertIn("200040", detail)
|
||||
|
||||
def test_group_air_quote_projects_route_fees_and_drops_total_alias(self) -> None:
|
||||
@@ -281,7 +281,7 @@ class AirRouteOptionsTests(unittest.TestCase):
|
||||
"NKG-KUL",
|
||||
"增点费:CNY 100.0",
|
||||
"拆板费:USD 100.0",
|
||||
"总销售价:CNY 5001.0",
|
||||
"费用合计:CNY 5001.0",
|
||||
"报价有效期:2026-09-30",
|
||||
]
|
||||
),
|
||||
@@ -323,6 +323,16 @@ class AirRouteOptionsTests(unittest.TestCase):
|
||||
self.assertIn("/air-routes/tok-abc", url)
|
||||
self.assertNotIn("userid", url)
|
||||
|
||||
def test_ok_page_auto_closes_wecom_h5(self) -> None:
|
||||
from agent.channel.h5.air_route_page import expired_air_route_page, render_air_route_ok
|
||||
|
||||
ok = render_air_route_ok()
|
||||
dead = expired_air_route_page()
|
||||
self.assertIn("closeWindow", ok)
|
||||
self.assertIn("wecom-jssdk", ok)
|
||||
self.assertIn("jsapi-signature", ok)
|
||||
self.assertNotIn("closeWindow", dead)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
@@ -342,7 +342,7 @@ class AirTextInquiryTests(unittest.TestCase):
|
||||
self.assertIn("已报价", self.flow.session_of("u1").status)
|
||||
self.assertIn(copy.BTN_SKIP_COLLAB, joined)
|
||||
self.assertIn("价格明细", joined)
|
||||
self.assertIn("总销售价", joined)
|
||||
self.assertIn("费用合计", joined)
|
||||
self.assertNotIn("时效:", joined)
|
||||
self.assertIn("报价有效期:", joined)
|
||||
self.assertNotIn("相同询价内容检索", joined)
|
||||
@@ -378,7 +378,7 @@ class AirTextInquiryTests(unittest.TestCase):
|
||||
card = scheme_cards[0]["template_card"]
|
||||
self.assertEqual(
|
||||
card["quote_area"]["quote_text"],
|
||||
"总销售价:USD 1,280\n共2项费用,详见上一条明细",
|
||||
"费用合计:USD 1,280\n共2项费用,详见上一条明细",
|
||||
)
|
||||
self.assertEqual(len(card["button_list"]), 3)
|
||||
self.assertEqual(
|
||||
@@ -408,7 +408,7 @@ class AirTextInquiryTests(unittest.TestCase):
|
||||
follow = file_extras[-1].get("followup_card") or {}
|
||||
self.assertTrue(str(follow.get("task_id") or "").startswith("deal-"))
|
||||
deal_rows = {r["keyname"]: r["value"] for r in follow.get("horizontal_content_list") or []}
|
||||
self.assertIn("总销售价", deal_rows)
|
||||
self.assertIn("费用合计", deal_rows)
|
||||
self.assertNotIn("时效", deal_rows)
|
||||
self.assertNotIn("历史报价", deal_rows)
|
||||
self.assertNotIn("工单状态", deal_rows)
|
||||
@@ -873,7 +873,7 @@ class AirTextInquiryTests(unittest.TestCase):
|
||||
self.assertNotIn("title", card["quote_area"])
|
||||
self.assertEqual(
|
||||
quote_text,
|
||||
"总销售价:USD 1,280\n共2项费用,详见上一条明细",
|
||||
"费用合计:USD 1,280\n共2项费用,详见上一条明细",
|
||||
)
|
||||
self.assertNotIn("时效:", quote_text)
|
||||
numeric = copy.tms_hit_wecom_payload(
|
||||
@@ -888,7 +888,7 @@ class AirTextInquiryTests(unittest.TestCase):
|
||||
)["template_card"]["quote_area"]["quote_text"]
|
||||
self.assertEqual(
|
||||
numeric,
|
||||
"总销售价:CNY 14669.60\n共1项费用,详见上一条明细",
|
||||
"费用合计:CNY 14669.60\n共1项费用,详见上一条明细",
|
||||
)
|
||||
self.assertEqual(copy.money_text("14669.60", ""), "14669.60")
|
||||
self.assertNotIn(copy.collab_hint("WO202609140001"), quote_text)
|
||||
@@ -928,11 +928,11 @@ class AirTextInquiryTests(unittest.TestCase):
|
||||
self.assertIn("WO202609220005价格明细", text)
|
||||
self.assertIn("操作费:CNY 1220.00", text)
|
||||
self.assertIn("空运费:USD 1250.00", text)
|
||||
self.assertIn("总销售价:CNY 142998.53", text)
|
||||
self.assertIn("费用合计:CNY 142998.53", text)
|
||||
self.assertNotIn("时效", text)
|
||||
self.assertIn("报价有效期:2026-09-30", text)
|
||||
card_text = copy.quote_area_safe_text(long_quote, business_line="AIR")
|
||||
self.assertIn("总销售价:CNY 142998.53", card_text)
|
||||
self.assertIn("费用合计:CNY 142998.53", card_text)
|
||||
self.assertIn("详见上一条明细", card_text)
|
||||
self.assertNotIn("操作费:CNY 1220.00", card_text)
|
||||
payload = copy.tms_hit_wecom_payload(
|
||||
@@ -965,7 +965,7 @@ class AirTextInquiryTests(unittest.TestCase):
|
||||
card_text = copy.quote_area_safe_text(quote, business_line="AIR")
|
||||
self.assertEqual(
|
||||
card_text,
|
||||
"总销售价:CNY 183370\n共3项费用,详见上一条明细",
|
||||
"费用合计:CNY 183370\n共3项费用,详见上一条明细",
|
||||
)
|
||||
payload = copy.tms_hit_wecom_payload(
|
||||
work_order_no="WO202609220006", quote=quote
|
||||
@@ -979,7 +979,7 @@ class AirTextInquiryTests(unittest.TestCase):
|
||||
)
|
||||
self.assertIn("ZUH-CAN-CRK", detail)
|
||||
self.assertIn("运输费:CNY 11.0", detail)
|
||||
self.assertIn("总销售价:CNY 183370", detail)
|
||||
self.assertIn("费用合计:CNY 183370", detail)
|
||||
rows = {
|
||||
x["keyname"]: x["value"]
|
||||
for x in copy.scheme_horizontal_rows(
|
||||
@@ -998,7 +998,7 @@ class AirTextInquiryTests(unittest.TestCase):
|
||||
)
|
||||
self.assertEqual(
|
||||
scheme["template_card"]["quote_area"]["quote_text"],
|
||||
"总销售价:CNY 183370\n共3项费用,详见上一条明细",
|
||||
"费用合计:CNY 183370\n共3项费用,详见上一条明细",
|
||||
)
|
||||
|
||||
def test_air_hit_sends_detail_text_before_card(self) -> None:
|
||||
@@ -1072,7 +1072,7 @@ class AirTextInquiryTests(unittest.TestCase):
|
||||
self.assertNotIn("title", card["quote_area"])
|
||||
self.assertEqual(
|
||||
quote_text,
|
||||
"总销售价:USD 1,280\n共2项费用,详见上一条明细",
|
||||
"费用合计:USD 1,280\n共2项费用,详见上一条明细",
|
||||
)
|
||||
self.assertNotIn("sub_title_text", card)
|
||||
rows = {x["keyname"]: x["value"] for x in card["horizontal_content_list"]}
|
||||
@@ -1124,7 +1124,7 @@ class AirTextInquiryTests(unittest.TestCase):
|
||||
self.assertIn("询价工单 WO202608210017", text)
|
||||
self.assertIn("报价单已发送,请跟进成交状态", text)
|
||||
self.assertIn("咨询成交状态:请输入成交 / 未成交 (未成交需补充原因)/ 协商中。", text)
|
||||
self.assertIn("总销售价:CNY 722.5", text)
|
||||
self.assertIn("费用合计:CNY 722.5", text)
|
||||
self.assertNotIn("时效", text)
|
||||
self.assertNotIn("- / -", text)
|
||||
self.assertIn("报价有效期:2026-08-24", text)
|
||||
@@ -1144,7 +1144,7 @@ class AirTextInquiryTests(unittest.TestCase):
|
||||
self.assertEqual(
|
||||
rows,
|
||||
{
|
||||
"总销售价": "CNY 722.5",
|
||||
"费用合计": "CNY 722.5",
|
||||
"报价有效期": "2026-08-24",
|
||||
},
|
||||
)
|
||||
|
||||
@@ -105,6 +105,11 @@ class AttachmentUploadH5Tests(unittest.TestCase):
|
||||
self.assertIn('class="card"', ok)
|
||||
self.assertIn('class="card"', dead)
|
||||
self.assertIn("过期", dead)
|
||||
self.assertIn("closeWindow", ok)
|
||||
self.assertIn("wecom-jssdk", ok)
|
||||
self.assertIn("jsapi-signature", ok)
|
||||
self.assertNotIn("closeWindow", page)
|
||||
self.assertNotIn("closeWindow", dead)
|
||||
|
||||
def test_post_png_rejected(self) -> None:
|
||||
token = self._open_card()
|
||||
|
||||
@@ -90,6 +90,22 @@ class AttachWaveTests(unittest.TestCase):
|
||||
self.assertIn("纽约港", oral)
|
||||
self.assertEqual(classify_multi_text(oral), "multi")
|
||||
|
||||
def test_one_sea_quote_filename_is_single(self) -> None:
|
||||
"""只传一份海运报价单:文件名带海运,表里再写运输方式,不能报同一方式两段。"""
|
||||
from agent.policy.multi_segments import classify_multi_text
|
||||
|
||||
self.book.open_wait("s1", precede_text="", token="tok1", now=1.0)
|
||||
self.book.add_files("s1", ["WO202608250012_海运报价单.xlsx"], now=2.0)
|
||||
self.book.complete_material(
|
||||
"s1",
|
||||
0,
|
||||
text="运输方式:海运\n起运港:纽约港\n目的港:巴生西港\n海运询价",
|
||||
)
|
||||
oral = self.book.compose_oral("s1")
|
||||
self.assertIn("段1:海运", oral)
|
||||
self.assertIn("运输方式:海运", oral)
|
||||
self.assertEqual(classify_multi_text(oral), "single")
|
||||
|
||||
def test_ltl_filename_counts_as_land_segment(self) -> None:
|
||||
"""零担询价表文件名没有「陆运」二字,也要和海运表切成两段。"""
|
||||
from agent.policy.multi_segments import classify_multi_text
|
||||
|
||||
@@ -150,6 +150,10 @@ class TestDealOutcome(unittest.TestCase):
|
||||
|
||||
def test_harvest_inline_lost_reason(self) -> None:
|
||||
self.assertEqual(harvest_inline_lost_reason("未成交,原因客户不想要了"), "客户不想要了")
|
||||
self.assertEqual(
|
||||
harvest_inline_lost_reason("WO202609220014@询价小助手 未成交,原因客户不想送货了"),
|
||||
"客户不想送货了",
|
||||
)
|
||||
self.assertEqual(harvest_inline_lost_reason("未成交,客户不想要了"), "客户不想要了")
|
||||
self.assertEqual(harvest_inline_lost_reason("未成交:价格太高"), "价格太高")
|
||||
self.assertEqual(harvest_inline_lost_reason("未成交原因客户不想要了"), "客户不想要了")
|
||||
|
||||
@@ -239,9 +239,9 @@ class LandRouteOptionsTests(unittest.TestCase):
|
||||
self.assertGreater(eta1, fee1)
|
||||
self.assertLess(eta1, od2)
|
||||
self.assertGreater(eta2, fee2)
|
||||
self.assertIn("总成本价:CNY 17100", text)
|
||||
valid = text.find("报价有效期:", text.find("总成本价:"))
|
||||
self.assertGreater(valid, text.find("总成本价:"))
|
||||
self.assertIn("费用合计:CNY 17100", text)
|
||||
valid = text.find("报价有效期:", text.find("费用合计:"))
|
||||
self.assertGreater(valid, text.find("费用合计:"))
|
||||
|
||||
def test_land_detail_and_group_use_selected_route_valid_until(self) -> None:
|
||||
"""选定广西这条后:私聊明细和群 TMS 段同一套正文,不摊开没选的线路。"""
|
||||
@@ -312,9 +312,9 @@ class LandRouteOptionsTests(unittest.TestCase):
|
||||
self.assertIn("运输费:CNY 7500.00", detail)
|
||||
self.assertIn("广西壮族自治区 → 胡志明", detail)
|
||||
self.assertIn("运输费:CNY 12500.00", detail)
|
||||
self.assertIn("总成本价:CNY 20000", detail)
|
||||
self.assertIn("费用合计:CNY 20000", detail)
|
||||
self.assertIn("报价有效期:2026-10-31", detail)
|
||||
self.assertGreater(detail.find("报价有效期:"), detail.find("总成本价:"))
|
||||
self.assertGreater(detail.find("报价有效期:"), detail.find("费用合计:"))
|
||||
self.assertNotIn("9600", detail)
|
||||
self.assertNotIn("时效:N+3 / N+3", detail)
|
||||
brief = copy.sea_group_brief(
|
||||
@@ -342,12 +342,12 @@ class LandRouteOptionsTests(unittest.TestCase):
|
||||
self.assertIn("上海-广西壮族自治区-胡志明", tms_block)
|
||||
self.assertIn("上海 → 广西壮族自治区", tms_block)
|
||||
self.assertIn("运输费:CNY 12500.00", tms_block)
|
||||
self.assertIn("总成本价:CNY 20000", tms_block)
|
||||
self.assertIn("费用合计:CNY 20000", tms_block)
|
||||
self.assertIn("报价有效期:2026-10-31", tms_block)
|
||||
self.assertNotIn("TMS标准报价", tms_block)
|
||||
self.assertNotIn("价格明细", tms_block)
|
||||
self.assertNotIn("9600", tms_block)
|
||||
self.assertNotIn("总成本价:-", tms_block)
|
||||
self.assertNotIn("费用合计:-", tms_block)
|
||||
self.assertNotIn("时效:-", tms_block)
|
||||
self.assertNotIn("时效:N+3 / N+3", tms_block)
|
||||
self.assertIn("请产品人员回复“确认报价”确定采用TMS报价。", brief)
|
||||
@@ -653,6 +653,16 @@ class LandRouteOptionsTests(unittest.TestCase):
|
||||
self.assertEqual(seqs, {1})
|
||||
self.assertTrue(any("报价方案" in x or "生成Excel" in x for x in replies[-3:]))
|
||||
|
||||
def test_ok_page_auto_closes_wecom_h5(self) -> None:
|
||||
from agent.channel.h5.land_route_page import expired_land_route_page, render_land_route_ok
|
||||
|
||||
ok = render_land_route_ok()
|
||||
dead = expired_land_route_page()
|
||||
self.assertIn("closeWindow", ok)
|
||||
self.assertIn("wecom-jssdk", ok)
|
||||
self.assertIn("jsapi-signature", ok)
|
||||
self.assertNotIn("closeWindow", dead)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
@@ -71,6 +71,25 @@ class SplitTests(unittest.TestCase):
|
||||
def test_same_mode_twice_is_rejected(self) -> None:
|
||||
self.assertEqual(classify_multi_text("段1:海运\n起运港:A\n段2:海运\n目的港:B"), "same_mode")
|
||||
|
||||
def test_one_sea_quote_sheet_is_single(self) -> None:
|
||||
"""测服 WO202608250012 海运报价单:文件名标段1,表里再写运输方式,仍是一段。"""
|
||||
oral = (
|
||||
"段1:海运\n"
|
||||
"运输方式:海运\n"
|
||||
"起运港:纽约港\n"
|
||||
"目的港:巴生西港\n"
|
||||
"品名:机器设备\n"
|
||||
"海运询价"
|
||||
)
|
||||
self.assertEqual(classify_multi_text(oral), "single")
|
||||
|
||||
def test_unlabeled_repeated_sea_is_single(self) -> None:
|
||||
"""报价单原文没有段标、海运出现两次,不能拆开再发。"""
|
||||
self.assertEqual(
|
||||
classify_multi_text("运输方式:海运\n起运港:A\n备注:海运询价"),
|
||||
"single",
|
||||
)
|
||||
|
||||
def test_explicit_markers_ignore_mode_words_in_body(self) -> None:
|
||||
"""文件名先标了段,表里再写「运输方式:海运」不能当成又一段海运。"""
|
||||
oral = (
|
||||
|
||||
@@ -15,6 +15,8 @@ if ROOT not in sys.path:
|
||||
|
||||
from agent.policy import inquiry_copy as copy
|
||||
from agent.schema.quote_adjust import (
|
||||
AdjustExtract,
|
||||
AdjustItem,
|
||||
apply_adjust,
|
||||
effective_quote,
|
||||
extract_adjust,
|
||||
@@ -164,6 +166,85 @@ class QuoteAdjustTests(unittest.TestCase):
|
||||
self.assertFalse(got.ok)
|
||||
self.assertEqual(got.code, "none_matched")
|
||||
|
||||
def test_sea_subtotal_and_bucket_lines(self) -> None:
|
||||
src = {
|
||||
"currency": "CNY",
|
||||
"total": "2000",
|
||||
"fee_lines": [
|
||||
{"name": "操作费", "amount": "1000", "currency": "CNY"},
|
||||
{"name": "箱单费", "amount": "200", "currency": "CNY"},
|
||||
],
|
||||
"polCostItems": [
|
||||
{"name": "操作费", "amount": "1000", "currency": "CNY"},
|
||||
{"name": "箱单费", "amount": "200", "currency": "CNY"},
|
||||
],
|
||||
"fee_total_polCostItems": "1200",
|
||||
}
|
||||
ext = AdjustExtract(
|
||||
is_adjust=True,
|
||||
items=[
|
||||
AdjustItem("操作费", "percent", "up", 10),
|
||||
AdjustItem("箱单费", "percent", "up", 5),
|
||||
AdjustItem("始发港费用小计", "percent", "up", 12),
|
||||
],
|
||||
)
|
||||
got = apply_adjust(src, ext, business_line="SEA")
|
||||
self.assertTrue(got.ok)
|
||||
self.assertEqual(got.unmatched, [])
|
||||
names = {row["name"]: row["amount"] for row in got.quote["polCostItems"]}
|
||||
self.assertEqual(names["操作费"], "1100.00")
|
||||
self.assertEqual(names["箱单费"], "210.00")
|
||||
self.assertEqual(got.quote["fee_total_polCostItems"], "1344.00")
|
||||
|
||||
def test_same_fee_name_keeps_its_own_amount(self) -> None:
|
||||
src = {
|
||||
"currency": "USD",
|
||||
"total": "500",
|
||||
"fee_lines": [
|
||||
{"name": "文件费", "amount": "450", "currency": "USD"},
|
||||
{"name": "文件费", "amount": "50", "currency": "USD"},
|
||||
],
|
||||
"polCostItems": [
|
||||
{"name": "文件费", "amount": "450", "currency": "USD"},
|
||||
{"name": "文件费", "amount": "50", "currency": "USD"},
|
||||
],
|
||||
}
|
||||
ext = AdjustExtract(is_adjust=True, items=[AdjustItem("文件费", "percent", "up", 10)])
|
||||
got = apply_adjust(src, ext, business_line="SEA")
|
||||
self.assertTrue(got.ok)
|
||||
amounts = [row["amount"] for row in got.quote["polCostItems"]]
|
||||
self.assertEqual(amounts, ["495.00", "55.00"])
|
||||
|
||||
def test_land_same_fee_name_each_leg_and_total(self) -> None:
|
||||
src = {
|
||||
"business_line": "LAND",
|
||||
"currency": "CNY",
|
||||
"total": "10000",
|
||||
"fee_lines": [
|
||||
{"name": "运输费", "amount": "7000", "currency": "CNY"},
|
||||
{"name": "运输费", "amount": "3000", "currency": "CNY"},
|
||||
],
|
||||
"landSegments": [
|
||||
{"origin": "上海", "destination": "谅山", "costPrice": "7000", "segmentSequence": 1, "routeLegSequence": 1},
|
||||
{"origin": "谅山", "destination": "河内", "costPrice": "3000", "segmentSequence": 1, "routeLegSequence": 2},
|
||||
],
|
||||
}
|
||||
ext = AdjustExtract(
|
||||
is_adjust=True,
|
||||
items=[
|
||||
AdjustItem("运输费", "percent", "up", 10),
|
||||
AdjustItem("总成本价", "percent", "up", 10),
|
||||
],
|
||||
)
|
||||
got = apply_adjust(src, ext, business_line="LAND")
|
||||
self.assertTrue(got.ok)
|
||||
self.assertEqual(got.unmatched, [])
|
||||
amounts = sorted(row["amount"] for row in got.quote["fee_lines"])
|
||||
self.assertEqual(amounts, ["3300.00", "7700.00"])
|
||||
costs = [leg["costPrice"] for leg in got.quote["landSegments"]]
|
||||
self.assertEqual(costs, ["7700.00", "3300.00"])
|
||||
self.assertEqual(got.quote["total"], "11000.00")
|
||||
|
||||
def test_overlay_manual_wins(self) -> None:
|
||||
tms = _quote(("运输费", "1000", "CNY"), ("卸货费", "200", "CNY"), total="1200")
|
||||
manual = _quote(("卸货费", "300", "CNY"), ("配送费", "20", "CNY"), total="320")
|
||||
@@ -219,17 +300,18 @@ class QuoteAdjustTests(unittest.TestCase):
|
||||
)
|
||||
self.assertAlmostEqual(second.profit_point or 0, 30.0)
|
||||
|
||||
def test_total_label_cost_until_sales_adjust(self) -> None:
|
||||
self.assertEqual(total_price_label("AIR"), "总销售价")
|
||||
self.assertEqual(total_price_label("SEA"), "总成本价")
|
||||
self.assertEqual(total_price_label("LAND"), "总成本价")
|
||||
self.assertEqual(total_price_label("", transport_mode="空运"), "总销售价")
|
||||
self.assertEqual(total_price_label("", transport_mode="陆运"), "总成本价")
|
||||
def test_total_label_is_fee_total_for_every_line(self) -> None:
|
||||
"""销售侧汇总一律叫费用合计,空运/海运/陆运、TMS/调价都一样。"""
|
||||
self.assertEqual(total_price_label("AIR"), "费用合计")
|
||||
self.assertEqual(total_price_label("SEA"), "费用合计")
|
||||
self.assertEqual(total_price_label("LAND"), "费用合计")
|
||||
self.assertEqual(total_price_label("", transport_mode="空运"), "费用合计")
|
||||
self.assertEqual(total_price_label("", transport_mode="陆运"), "费用合计")
|
||||
tms = {"source": "TMS", "source_label": "TMS标准报价"}
|
||||
self.assertEqual(total_price_label("LAND", quote=tms), "总成本价")
|
||||
self.assertEqual(total_price_label("SEA", quote={"source": "PRODUCT"}), "总成本价")
|
||||
self.assertEqual(total_price_label("LAND", quote={"source": "SALES_ADJUST"}), "总销售价")
|
||||
self.assertEqual(total_price_label("AIR", quote=tms), "总销售价")
|
||||
self.assertEqual(total_price_label("LAND", quote=tms), "费用合计")
|
||||
self.assertEqual(total_price_label("SEA", quote={"source": "PRODUCT"}), "费用合计")
|
||||
self.assertEqual(total_price_label("LAND", quote={"source": "SALES_ADJUST"}), "费用合计")
|
||||
self.assertEqual(total_price_label("AIR", quote=tms), "费用合计")
|
||||
|
||||
def test_tms_land_card_uses_cost_not_sale(self) -> None:
|
||||
q = _quote(("运输费", "121.00", "CNY"), total="121.00")
|
||||
@@ -239,7 +321,7 @@ class QuoteAdjustTests(unittest.TestCase):
|
||||
work_order_no="WO202609200005", quote=q, business_line="LAND"
|
||||
)
|
||||
self.assertEqual(detail["template_card"]["source"]["desc"], "TMS标准报价")
|
||||
self.assertIn("总成本价:CNY 121.00", detail["template_card"]["quote_area"]["quote_text"])
|
||||
self.assertIn("费用合计:CNY 121.00", detail["template_card"]["quote_area"]["quote_text"])
|
||||
self.assertNotIn("总销售价", detail["template_card"]["quote_area"]["quote_text"])
|
||||
scheme = copy.scheme_wecom_payload(
|
||||
work_order_no="WO202609200005",
|
||||
@@ -248,9 +330,9 @@ class QuoteAdjustTests(unittest.TestCase):
|
||||
transport_mode="陆运",
|
||||
)
|
||||
self.assertEqual(scheme["template_card"]["source"]["desc"], "TMS标准报价")
|
||||
self.assertIn("总成本价:CNY 121.00", scheme["template_card"]["quote_area"]["quote_text"])
|
||||
self.assertIn("费用合计:CNY 121.00", scheme["template_card"]["quote_area"]["quote_text"])
|
||||
deal = copy.deal_horizontal_rows(quote=q, business_line="LAND")
|
||||
self.assertEqual(deal[0]["keyname"], "总成本价")
|
||||
self.assertEqual(deal[0]["keyname"], "费用合计")
|
||||
|
||||
def test_adjust_confirm_uses_sale_total_name(self) -> None:
|
||||
src = _quote(("运输费", "110", "CNY"), total="110")
|
||||
@@ -261,24 +343,22 @@ class QuoteAdjustTests(unittest.TestCase):
|
||||
)
|
||||
self.assertTrue(land.ok)
|
||||
names = [x.get("name") for x in land.applied]
|
||||
self.assertIn("总销售价", names)
|
||||
self.assertNotIn("合计", names)
|
||||
self.assertIn("费用合计", names)
|
||||
self.assertNotIn("总销售价", names)
|
||||
self.assertNotIn("总成本价", names)
|
||||
text = copy.adjust_confirm(land.applied)
|
||||
self.assertIn("总销售价在当前报价基础上", text)
|
||||
self.assertNotIn("合计在当前报价基础上", text)
|
||||
self.assertIn("费用合计在当前报价基础上", text)
|
||||
body = copy.adjust_group_body(land.quote, business_line="LAND")
|
||||
self.assertIn("总销售价:", body)
|
||||
self.assertNotIn("合计:", body)
|
||||
self.assertIn("费用合计:", body)
|
||||
air = apply_adjust(
|
||||
src,
|
||||
extract_adjust("总销售价改为100CNY", fee_names=["运输费"], allow_model=False),
|
||||
business_line="AIR",
|
||||
)
|
||||
self.assertIn("总销售价", [x.get("name") for x in air.applied])
|
||||
self.assertNotIn("合计", [x.get("name") for x in air.applied])
|
||||
self.assertIn("费用合计", [x.get("name") for x in air.applied])
|
||||
note = copy.adjust_ledger_remark(land.applied)
|
||||
self.assertTrue(note.startswith("销售调价:"))
|
||||
self.assertIn("总销售价", note)
|
||||
self.assertIn("费用合计", note)
|
||||
|
||||
def test_quote_copy_uses_line_and_source_total_name(self) -> None:
|
||||
sea = {
|
||||
@@ -293,16 +373,17 @@ class QuoteAdjustTests(unittest.TestCase):
|
||||
"source": "TMS",
|
||||
"fee_rows": [{"name": "空运费", "amount": "700", "currency": "CNY"}],
|
||||
}
|
||||
self.assertIn("总成本价:CNY 2287.65", copy.quote_area_fee_text(sea, business_line="SEA"))
|
||||
self.assertIn("总销售价:CNY 722.5", copy.quote_area_fee_text(air, business_line="AIR"))
|
||||
self.assertIn("费用合计:CNY 2287.65", copy.quote_area_fee_text(sea, business_line="SEA"))
|
||||
self.assertIn("费用合计:CNY 722.5", copy.quote_area_fee_text(air, business_line="AIR"))
|
||||
self.assertNotIn("总销售价合计", copy.quote_area_fee_text(air, business_line="AIR"))
|
||||
self.assertNotIn("总销售价:CNY 2287.65", copy.quote_area_fee_text(sea, business_line="SEA"))
|
||||
self.assertIn("总成本价:CNY 2287.65", "\n".join(copy.quote_detail_lines(sea, business_line="SEA")))
|
||||
self.assertNotIn("总销售价", copy.quote_area_fee_text(sea, business_line="SEA"))
|
||||
self.assertNotIn("总成本价", copy.quote_area_fee_text(sea, business_line="SEA"))
|
||||
self.assertIn("费用合计:CNY 2287.65", "\n".join(copy.quote_detail_lines(sea, business_line="SEA")))
|
||||
rows = copy.deal_horizontal_rows(quote=air, business_line="AIR")
|
||||
self.assertEqual(rows[0]["keyname"], "总销售价")
|
||||
self.assertEqual(rows[0]["keyname"], "费用合计")
|
||||
self.assertNotIn("时效", [x["keyname"] for x in rows])
|
||||
land_rows = copy.deal_horizontal_rows(quote=sea, business_line="LAND")
|
||||
self.assertEqual(land_rows[0]["keyname"], "总成本价")
|
||||
self.assertEqual(land_rows[0]["keyname"], "费用合计")
|
||||
self.assertIn("时效", [x["keyname"] for x in land_rows])
|
||||
|
||||
def test_adjust_cards_use_sales_adjust_not_tms(self) -> None:
|
||||
@@ -314,7 +395,7 @@ class QuoteAdjustTests(unittest.TestCase):
|
||||
work_order_no="WO202609200004", quote=q, business_line="LAND"
|
||||
)
|
||||
self.assertEqual(detail["template_card"]["source"]["desc"], "销售调价")
|
||||
self.assertIn("总销售价:CNY 121.00", detail["template_card"]["quote_area"]["quote_text"])
|
||||
self.assertIn("费用合计:CNY 121.00", detail["template_card"]["quote_area"]["quote_text"])
|
||||
self.assertNotIn("总成本价", detail["template_card"]["quote_area"]["quote_text"])
|
||||
scheme = copy.scheme_wecom_payload(
|
||||
work_order_no="WO202609200004",
|
||||
@@ -323,7 +404,7 @@ class QuoteAdjustTests(unittest.TestCase):
|
||||
transport_mode="陆运",
|
||||
)
|
||||
self.assertEqual(scheme["template_card"]["source"]["desc"], "销售调价")
|
||||
self.assertIn("总销售价:CNY 121.00", scheme["template_card"]["quote_area"]["quote_text"])
|
||||
self.assertIn("费用合计:CNY 121.00", scheme["template_card"]["quote_area"]["quote_text"])
|
||||
|
||||
def test_private_eligibility(self) -> None:
|
||||
self.assertEqual(private_session_can_adjust("已报价", True, ""), "")
|
||||
@@ -469,10 +550,10 @@ class QuoteAdjustTests(unittest.TestCase):
|
||||
transport_mode="陆运",
|
||||
)
|
||||
tms_block = text.split("TMS报价:", 1)[1]
|
||||
self.assertIn("总成本价:CNY 110.00", tms_block.split("销售调价:", 1)[0])
|
||||
self.assertIn("费用合计:CNY 110.00", tms_block.split("销售调价:", 1)[0])
|
||||
self.assertIn("销售调价:", text)
|
||||
self.assertIn("总销售价:CNY 100.00", text.split("销售调价:", 1)[1])
|
||||
self.assertNotIn("总销售价:CNY 100.00", tms_block.split("销售调价:", 1)[0])
|
||||
self.assertIn("费用合计:CNY 100.00", text.split("销售调价:", 1)[1])
|
||||
self.assertNotIn("费用合计:CNY 100.00", tms_block.split("销售调价:", 1)[0])
|
||||
|
||||
def test_group_brief_adjust_alone_does_not_become_tms(self) -> None:
|
||||
"""没单独传入 TMS 时,调后价也不能写进 TMS 段。"""
|
||||
@@ -492,7 +573,7 @@ class QuoteAdjustTests(unittest.TestCase):
|
||||
self.assertIn("TMS报价:\nTMS 暂无匹配报价", text)
|
||||
self.assertIn("产品人员可通过“纯文字”告知新报价,或直接将新报价excel表发给智能体。", text)
|
||||
self.assertIn("销售调价:", text)
|
||||
self.assertIn("总销售价:CNY 100.00", text.split("销售调价:", 1)[1])
|
||||
self.assertIn("费用合计:CNY 100.00", text.split("销售调价:", 1)[1])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -478,7 +478,7 @@ class SeaTmsCardTests(unittest.TestCase):
|
||||
self.assertIn("海运费:USD 575", text)
|
||||
self.assertIn("海运费小计:CNY 4038", text)
|
||||
self.assertIn("目的港费用小计:CNY 470", text)
|
||||
self.assertIn("总成本价:CNY 6952.77", text)
|
||||
self.assertIn("费用合计:CNY 6952.77", text)
|
||||
self.assertNotIn("时效", text)
|
||||
|
||||
def test_long_fee_lines_go_to_text_not_clipped_card(self) -> None:
|
||||
@@ -515,14 +515,14 @@ class SeaTmsCardTests(unittest.TestCase):
|
||||
"封条费(SEAI):CNY 50.00",
|
||||
"设备费:CNY 50.00",
|
||||
"报关费:CNY 250.00",
|
||||
"总成本价:CNY 2287.65",
|
||||
"费用合计:CNY 2287.65",
|
||||
]
|
||||
),
|
||||
)
|
||||
self.assertNotIn("时效", text)
|
||||
self.assertNotIn("报价有效期", text)
|
||||
card_text = copy.quote_area_safe_text(long_quote, business_line="SEA")
|
||||
self.assertIn("总成本价:CNY 2287.65", card_text)
|
||||
self.assertIn("费用合计:CNY 2287.65", card_text)
|
||||
self.assertNotIn("海运费:USD 100.00\n燃油附加费", card_text)
|
||||
self.assertLessEqual(len(card_text), 80)
|
||||
payload = copy.sea_tms_hit_wecom_payload(
|
||||
@@ -1120,6 +1120,21 @@ class SeaGroupCollabFieldTests(unittest.TestCase):
|
||||
self.assertEqual(str(rows["海运费"].get("amount") or ""), "100.00")
|
||||
self.assertEqual(str(rows["燃油附加费"].get("amount") or ""), "15.00")
|
||||
|
||||
def test_air_unit_qty_amount_keeps_air_freight_name(self) -> None:
|
||||
"""空运费 单价/数量/金额:费用名是空运费,金额用「金额」不是单价。"""
|
||||
from agent.llm.extract_text import extract_product_quote
|
||||
|
||||
quote = extract_product_quote(
|
||||
"空运段,空运费 单价CNY99.00,数量100 ,金额CNY199.00@询价小助手"
|
||||
)
|
||||
rows = {str(x.get("name") or ""): x for x in (quote.get("fee_rows") or [])}
|
||||
self.assertIn("空运费", rows)
|
||||
self.assertNotIn("海运费", rows)
|
||||
self.assertEqual(str(rows["空运费"].get("amount") or ""), "199.00")
|
||||
self.assertEqual(str(rows["空运费"].get("currency") or ""), "CNY")
|
||||
self.assertEqual(str(rows["空运费"].get("unit_price") or ""), "99.00")
|
||||
self.assertEqual(str(quote.get("total") or ""), "199.0")
|
||||
|
||||
def test_fee_line_text_is_a_product_quote(self) -> None:
|
||||
"""产品按行改价:码头操作费100CNY / 报关费改为300CNY,必须抽出费用名,不能只当海运费合计。"""
|
||||
from agent.llm.extract_text import extract_product_quote
|
||||
|
||||
Reference in New Issue
Block a user