销售侧汇总统一叫费用合计,并认准空运费的金额。

企微跳转改为账号密码登录,报价卡上的费用按原金额改回;航线按单价数量金额报价时不再记成海运费。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-24 11:15:33 +08:00
co-authored by Cursor
parent 84207bb4e5
commit 4dfd756bcf
24 changed files with 778 additions and 150 deletions
+43 -6
View File
@@ -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
+12 -2
View File
@@ -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()
+13 -13
View File
@@ -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
+4
View File
@@ -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("未成交原因客户不想要了"), "客户不想要了")
+17 -7
View File
@@ -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 = (
+115 -34
View File
@@ -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__":
+18 -3
View File
@@ -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