完善空运锁舱与海运报价单单选回填,并增强模板预览与协同字段盖写。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -21,10 +21,28 @@ from agent.schema.field_validate import (
|
||||
air_general_cargo_collab_defaults,
|
||||
validate_required_fields,
|
||||
)
|
||||
from agent.schema.tms_air_query import assemble_air_query
|
||||
from agent.schema.tms_air_query import assemble_air_query, volume_cbm_number
|
||||
|
||||
|
||||
class AirFieldContractTests(unittest.TestCase):
|
||||
def test_volume_dimension_product(self) -> None:
|
||||
self.assertEqual(volume_cbm_number("1.1*1.2*3"), "3.96")
|
||||
self.assertEqual(volume_cbm_number("1.1x1.2x3"), "3.96")
|
||||
self.assertEqual(volume_cbm_number("2CBM"), "2")
|
||||
out = assemble_air_query(
|
||||
{
|
||||
"目的港": "吉隆坡",
|
||||
"品名": "普货",
|
||||
"件数": "3件",
|
||||
"毛重": "5",
|
||||
"体积": "1.1*1.2*3",
|
||||
"包装方式": "托盘",
|
||||
"报价日期": "2026-10-08",
|
||||
}
|
||||
)
|
||||
self.assertTrue(out["ok"], out)
|
||||
self.assertEqual(out["facts"]["体积"], "1.1*1.2*3")
|
||||
|
||||
def test_origin_optional_still_ok(self) -> None:
|
||||
result = validate_required_fields(
|
||||
facts={
|
||||
|
||||
@@ -38,6 +38,64 @@ class FakeBotGroup:
|
||||
return {"ok": True}
|
||||
|
||||
|
||||
class AirSheetQuoteTotalTests(unittest.TestCase):
|
||||
"""群填表报价包必须带 TMS 总销售价,不能把附加费之和当预估费用。"""
|
||||
|
||||
def test_sheet_quote_replaces_fee_sum_total_with_tms_sale_price(self) -> None:
|
||||
from agent.policy.air_group_ops import _sheet_quote_with_tms_sale_total
|
||||
|
||||
thin = {
|
||||
"total": "200",
|
||||
"currency": "CNY",
|
||||
"fee_lines": [
|
||||
{"name": "增点费", "amount": "100", "currency": "CNY"},
|
||||
{"name": "拆板费", "amount": "100", "currency": "USD"},
|
||||
],
|
||||
}
|
||||
tms = {
|
||||
"source": "TMS",
|
||||
"total": "7918.25",
|
||||
"currency": "CNY",
|
||||
"chargeableWeightKg": "833.5",
|
||||
"airOptions": [
|
||||
{
|
||||
"optionNo": "AIR-1",
|
||||
"salePrice": 7918.25,
|
||||
"currency": "CNY",
|
||||
"departureCharges": [
|
||||
{"chargeName": "增点费", "unitPrice": 100, "currency": "CNY"},
|
||||
{"chargeName": "拆板费", "unitPrice": 100, "currency": "USD"},
|
||||
],
|
||||
}
|
||||
],
|
||||
}
|
||||
out = _sheet_quote_with_tms_sale_total(thin, tms=tms, facts={"起运港": "南京", "目的港": "吉隆坡"})
|
||||
self.assertIn("7918.25", str(out.get("total") or ""))
|
||||
self.assertNotEqual(str(out.get("total") or "").strip(), "200")
|
||||
self.assertTrue(out.get("airOptions") or out.get("selected_air_option"))
|
||||
|
||||
def test_sheet_quote_keeps_product_total_when_not_fee_sum(self) -> None:
|
||||
from agent.policy.air_group_ops import _sheet_quote_with_tms_sale_total
|
||||
|
||||
product = {
|
||||
"total": "9000",
|
||||
"currency": "CNY",
|
||||
"source_label": "航线报价",
|
||||
"fee_lines": [
|
||||
{"name": "增点费", "amount": "100", "currency": "CNY"},
|
||||
{"name": "拆板费", "amount": "100", "currency": "USD"},
|
||||
],
|
||||
}
|
||||
tms = {
|
||||
"source": "TMS",
|
||||
"total": "7918.25",
|
||||
"currency": "CNY",
|
||||
"airOptions": [{"optionNo": "AIR-1", "salePrice": 7918.25, "currency": "CNY"}],
|
||||
}
|
||||
out = _sheet_quote_with_tms_sale_total(product, tms=tms, facts={})
|
||||
self.assertEqual(str(out.get("total") or "").strip(), "9000")
|
||||
|
||||
|
||||
class AirGroupCollabTests(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
from agent.redis_coord.air_inbound import reset_air_inbound_for_tests
|
||||
|
||||
@@ -17,6 +17,7 @@ from agent.policy import inquiry_copy as copy
|
||||
from agent.schema.quote_adjust import (
|
||||
AdjustExtract,
|
||||
AdjustItem,
|
||||
adjustable_rows,
|
||||
apply_adjust,
|
||||
effective_quote,
|
||||
extract_adjust,
|
||||
@@ -196,6 +197,47 @@ class QuoteAdjustTests(unittest.TestCase):
|
||||
self.assertEqual(names["箱单费"], "210.00")
|
||||
self.assertEqual(got.quote["fee_total_polCostItems"], "1344.00")
|
||||
|
||||
def test_sea_pol_subtotal_and_total_up_ten_percent(self) -> None:
|
||||
"""协同话术:始发港费用小计 + 费用合计 各提升 10%。"""
|
||||
src = {
|
||||
"currency": "CNY",
|
||||
"total": "14300",
|
||||
"fee_lines": [
|
||||
{"name": "操作费", "amount": "1220", "currency": "CNY"},
|
||||
{"name": "海运费", "amount": "USD 1250", "currency": "USD"},
|
||||
],
|
||||
"polCostItems": [
|
||||
{"name": "操作费", "amount": "1220", "currency": "CNY"},
|
||||
{"name": "箱单费", "amount": "50", "currency": "CNY"},
|
||||
],
|
||||
"oceanCostItems": [
|
||||
{"name": "海运费", "amount": "1250", "currency": "USD"},
|
||||
],
|
||||
"podCostItems": [
|
||||
{"name": "操作费", "amount": "240", "currency": "USD"},
|
||||
],
|
||||
"fee_total_polCostItems": "2335",
|
||||
"fee_total_oceanCostItems": "8778.75",
|
||||
"fee_total_podCostItems": "2985",
|
||||
}
|
||||
names = [str(r.get("name") or "") for r in adjustable_rows(src)]
|
||||
text = "将始发港费用小计提升10%\n费用合计提升10%"
|
||||
ext = extract_adjust(text, fee_names=names, allow_model=False)
|
||||
self.assertTrue(ext.is_adjust)
|
||||
fee_names = [x.fee_name for x in ext.items]
|
||||
self.assertIn("始发港费用小计", fee_names)
|
||||
self.assertIn("费用合计", fee_names)
|
||||
# 即使模型误标 wants_total,有点名费目时也不能只改合计
|
||||
ext.wants_total = True
|
||||
got = apply_adjust(src, ext, business_line="SEA")
|
||||
self.assertTrue(got.ok)
|
||||
self.assertEqual(got.quote["fee_total_polCostItems"], "2568.50")
|
||||
self.assertEqual(got.quote["total"], "15730.00")
|
||||
body = copy.adjust_group_body(got.quote, business_line="SEA")
|
||||
self.assertIn("始发港费用小计", body)
|
||||
self.assertIn("海运费小计", body)
|
||||
self.assertIn("目的港费用小计", body)
|
||||
|
||||
def test_same_fee_name_keeps_its_own_amount(self) -> None:
|
||||
src = {
|
||||
"currency": "USD",
|
||||
|
||||
@@ -60,11 +60,23 @@ class SeaOptionsTest(unittest.TestCase):
|
||||
kept = drop_invented_door_trade_terms("贸易条款:门到门", {"贸易条款": "门到门"})
|
||||
self.assertEqual(kept["贸易条款"], "门到门")
|
||||
|
||||
def test_prompt_lists_two_options_only(self):
|
||||
text = sea_class_prompt()
|
||||
self.assertIn("Port to Port", text)
|
||||
self.assertIn("其他", text)
|
||||
self.assertNotIn("门到门", text)
|
||||
def test_wo012_trade_port_class_other(self):
|
||||
"""条款写 Port to Port、分类写其它:二者各归各栏,禁止互盖。"""
|
||||
text = (
|
||||
"运输方式:海运\n起运港:纽约港\n目的港:巴生西港\n品名:机器设备\n"
|
||||
"货物数量:15件\n整柜或拼柜:整柜\n箱型箱量:40HQ*1\n"
|
||||
"贸易条款:Port to Port\n运输分类:其它\n报价日期:2026-09-29"
|
||||
)
|
||||
got = harvest_sea_class(text, {"贸易条款": "门到门", "运输分类": "Port to Port"})
|
||||
self.assertEqual(got.get("运输分类"), "其他")
|
||||
got = drop_invented_door_trade_terms(text, {"贸易条款": "门到门", "运输分类": "其他"})
|
||||
self.assertEqual(got.get("贸易条款"), "Port to Port")
|
||||
self.assertEqual(got.get("运输分类"), "其他")
|
||||
|
||||
def test_trade_line_port_does_not_set_class_when_class_labeled_other(self):
|
||||
text = "贸易条款:Port to Port\n运输分类:其它"
|
||||
self.assertEqual(harvest_sea_class(text, {}).get("运输分类"), "其他")
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in New Issue
Block a user