|
|
@@ -0,0 +1,173 @@
|
|
|
+from datetime import datetime
|
|
|
+from decimal import Decimal
|
|
|
+
|
|
|
+import pytest
|
|
|
+
|
|
|
+from app.services.sf_express_settlement import (
|
|
|
+ assert_freight_mutable,
|
|
|
+ assert_settle_request,
|
|
|
+ freight_amount_label,
|
|
|
+ plan_fill_amount,
|
|
|
+ plan_settle,
|
|
|
+ resolve_header_for_save,
|
|
|
+)
|
|
|
+
|
|
|
+
|
|
|
+def test_new_sf_express_nulls_amount_and_requires_waybill():
|
|
|
+ plan = resolve_header_for_save(
|
|
|
+ transport_mode="sfkd",
|
|
|
+ existing_waybill=None,
|
|
|
+ existing_amount=None,
|
|
|
+ existing_settle_status=None,
|
|
|
+ waybill_supplied=True,
|
|
|
+ waybill_no=" SF001 ",
|
|
|
+ amount_supplied=True,
|
|
|
+ freight_amount=Decimal("88"),
|
|
|
+ )
|
|
|
+ assert plan.waybill_no == "SF001"
|
|
|
+ assert plan.freight_amount is None
|
|
|
+ assert plan.settle_status == "wjs"
|
|
|
+ assert plan.clear_settle_time is True
|
|
|
+ assert freight_amount_label("sfkd", plan.freight_amount) == "月结"
|
|
|
+
|
|
|
+
|
|
|
+def test_sf_express_rejects_blank_and_long_waybill():
|
|
|
+ with pytest.raises(ValueError, match="请填写顺丰单号"):
|
|
|
+ resolve_header_for_save(
|
|
|
+ transport_mode="sfkd",
|
|
|
+ existing_waybill=None,
|
|
|
+ existing_amount=None,
|
|
|
+ existing_settle_status=None,
|
|
|
+ waybill_supplied=True,
|
|
|
+ waybill_no=" ",
|
|
|
+ amount_supplied=True,
|
|
|
+ freight_amount=None,
|
|
|
+ )
|
|
|
+ with pytest.raises(ValueError, match="顺丰单号不能超过 64 个字符"):
|
|
|
+ resolve_header_for_save(
|
|
|
+ transport_mode="sfkd",
|
|
|
+ existing_waybill=None,
|
|
|
+ existing_amount=None,
|
|
|
+ existing_settle_status=None,
|
|
|
+ waybill_supplied=True,
|
|
|
+ waybill_no="S" * 65,
|
|
|
+ amount_supplied=False,
|
|
|
+ freight_amount=None,
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
+def test_non_sf_requires_amount_and_clears_waybill_and_settlement():
|
|
|
+ plan = resolve_header_for_save(
|
|
|
+ transport_mode="zxc",
|
|
|
+ existing_waybill="SF001",
|
|
|
+ existing_amount=None,
|
|
|
+ existing_settle_status="wjs",
|
|
|
+ waybill_supplied=False,
|
|
|
+ waybill_no=None,
|
|
|
+ amount_supplied=True,
|
|
|
+ freight_amount="12.3",
|
|
|
+ )
|
|
|
+ assert plan.waybill_no is None
|
|
|
+ assert plan.freight_amount == Decimal("12.30")
|
|
|
+ assert plan.settle_status is None
|
|
|
+ assert plan.clear_settle_time is True
|
|
|
+
|
|
|
+
|
|
|
+def test_other_mode_named_sf_still_requires_amount():
|
|
|
+ plan = resolve_header_for_save(
|
|
|
+ transport_mode="sf",
|
|
|
+ existing_waybill=None,
|
|
|
+ existing_amount=None,
|
|
|
+ existing_settle_status=None,
|
|
|
+ waybill_supplied=True,
|
|
|
+ waybill_no="",
|
|
|
+ amount_supplied=True,
|
|
|
+ freight_amount="12.30",
|
|
|
+ )
|
|
|
+ assert plan.freight_amount == Decimal("12.30")
|
|
|
+ assert plan.waybill_no is None
|
|
|
+ assert plan.settle_status is None
|
|
|
+
|
|
|
+
|
|
|
+def test_non_sf_rejects_missing_amount_and_negative_amount():
|
|
|
+ with pytest.raises(ValueError, match="请填写运费"):
|
|
|
+ resolve_header_for_save(
|
|
|
+ transport_mode="zxc",
|
|
|
+ existing_waybill=None,
|
|
|
+ existing_amount=None,
|
|
|
+ existing_settle_status=None,
|
|
|
+ waybill_supplied=True,
|
|
|
+ waybill_no="",
|
|
|
+ amount_supplied=True,
|
|
|
+ freight_amount=None,
|
|
|
+ )
|
|
|
+ with pytest.raises(ValueError, match="运费不能为负数"):
|
|
|
+ resolve_header_for_save(
|
|
|
+ transport_mode="zxc",
|
|
|
+ existing_waybill=None,
|
|
|
+ existing_amount=None,
|
|
|
+ existing_settle_status=None,
|
|
|
+ waybill_supplied=True,
|
|
|
+ waybill_no="",
|
|
|
+ amount_supplied=True,
|
|
|
+ freight_amount="-1",
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
+def test_settled_header_cannot_change():
|
|
|
+ with pytest.raises(ValueError, match="已结算的运费不能修改运输方式或顺丰单号"):
|
|
|
+ resolve_header_for_save(
|
|
|
+ transport_mode="sfkd",
|
|
|
+ existing_waybill="SF001",
|
|
|
+ existing_amount=None,
|
|
|
+ existing_settle_status="yjs",
|
|
|
+ waybill_supplied=True,
|
|
|
+ waybill_no="SF002",
|
|
|
+ amount_supplied=False,
|
|
|
+ freight_amount=None,
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
+def test_blank_settle_keeps_null_and_historical_amount():
|
|
|
+ now = datetime(2026, 10, 21, 12, 0, 0)
|
|
|
+ assert plan_settle(current_amount=None, submitted_amount=" ", now=now) == (
|
|
|
+ None,
|
|
|
+ "yjs",
|
|
|
+ now,
|
|
|
+ )
|
|
|
+ assert plan_settle(current_amount=Decimal("10.00"), submitted_amount=None, now=now) == (
|
|
|
+ Decimal("10.00"),
|
|
|
+ "yjs",
|
|
|
+ now,
|
|
|
+ )
|
|
|
+ amount, status, settle_time = plan_settle(
|
|
|
+ current_amount=Decimal("10.00"), submitted_amount="20", now=now
|
|
|
+ )
|
|
|
+ assert amount == Decimal("20.00")
|
|
|
+ assert status == "yjs"
|
|
|
+ assert settle_time == now
|
|
|
+
|
|
|
+
|
|
|
+def test_batch_settle_rejects_amount_and_fill_keeps_only_monthly_rows():
|
|
|
+ assert_settle_request(["a", "b"], None)
|
|
|
+ with pytest.raises(ValueError, match="批量结算不能填写运费"):
|
|
|
+ assert_settle_request(["a", "b"], "10")
|
|
|
+ with pytest.raises(ValueError, match="请选择运费批次"):
|
|
|
+ assert_settle_request([], None)
|
|
|
+ assert plan_fill_amount(
|
|
|
+ current_amount=None, settle_status="yjs", submitted_amount="6.5"
|
|
|
+ ) == Decimal("6.50")
|
|
|
+ with pytest.raises(ValueError, match="只有已结算且仍为月结的运费可以补填金额"):
|
|
|
+ plan_fill_amount(
|
|
|
+ current_amount=Decimal("1.00"), settle_status="yjs", submitted_amount="2"
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
+def test_settled_batch_cannot_delete_or_change_samples():
|
|
|
+ with pytest.raises(ValueError, match="已结算的运费不能删除"):
|
|
|
+ assert_freight_mutable("yjs", "delete")
|
|
|
+ with pytest.raises(ValueError, match="已结算的运费不能调整样本"):
|
|
|
+ assert_freight_mutable("yjs", "items")
|
|
|
+ assert_freight_mutable("wjs", "delete")
|
|
|
+ assert freight_amount_label("sfkd", Decimal("8")) == "8.00"
|
|
|
+ assert freight_amount_label("zxc", None) == ""
|