Validate arranged+this plan qty against fixed contract product quantities on transport arrange; return post-success remaining/framework cumulative reminders. Co-authored-by: Cursor <cursoragent@cursor.com>
124 lines
3.2 KiB
Python
124 lines
3.2 KiB
Python
"""安排运输时合同数量硬拦 / 提醒纯函数单测。"""
|
|
|
|
from app.services.pl_contract_plan_qty import (
|
|
contract_product_has_fixed_qty,
|
|
effective_arranged_total,
|
|
evaluate_product_qty_for_arrange,
|
|
)
|
|
|
|
|
|
def test_effective_arranged_total_subtracts_old():
|
|
assert effective_arranged_total(arranged_total=100, old_plan_qty=30, new_plan_qty=40) == 110.0
|
|
|
|
|
|
def test_effective_arranged_total_rounds():
|
|
assert effective_arranged_total(arranged_total=10.1234, old_plan_qty=1.1, new_plan_qty=2.2) == 11.223
|
|
|
|
|
|
def test_framework_customer_demand_not_fixed():
|
|
assert (
|
|
contract_product_has_fixed_qty(
|
|
contract_type="framework_sale",
|
|
quantity=0,
|
|
quantity_mode="customer_demand",
|
|
)
|
|
is False
|
|
)
|
|
|
|
|
|
def test_framework_fixed_mode_with_qty_is_fixed():
|
|
assert (
|
|
contract_product_has_fixed_qty(
|
|
contract_type="framework_purchase",
|
|
quantity=50,
|
|
quantity_mode="fixed",
|
|
)
|
|
is True
|
|
)
|
|
|
|
|
|
def test_sale_positive_qty_is_fixed():
|
|
assert (
|
|
contract_product_has_fixed_qty(
|
|
contract_type="sale",
|
|
quantity=100,
|
|
quantity_mode="fixed",
|
|
)
|
|
is True
|
|
)
|
|
|
|
|
|
def test_zero_or_empty_qty_not_fixed():
|
|
assert contract_product_has_fixed_qty(contract_type="sale", quantity=0, quantity_mode="fixed") is False
|
|
assert contract_product_has_fixed_qty(contract_type="sale", quantity=None, quantity_mode="fixed") is False
|
|
|
|
|
|
def test_evaluate_skip_when_no_new_qty():
|
|
assert (
|
|
evaluate_product_qty_for_arrange(
|
|
has_fixed_qty=True,
|
|
contract_qty=100,
|
|
arranged_total=80,
|
|
old_plan_qty=0,
|
|
new_plan_qty=0,
|
|
)
|
|
is None
|
|
)
|
|
|
|
|
|
def test_evaluate_overflow_violation():
|
|
r = evaluate_product_qty_for_arrange(
|
|
has_fixed_qty=True,
|
|
contract_qty=100,
|
|
arranged_total=80,
|
|
old_plan_qty=0,
|
|
new_plan_qty=30,
|
|
)
|
|
assert r is not None
|
|
assert r["kind"] == "violation"
|
|
assert r["overflowQty"] == 10.0
|
|
assert r["effectiveTotal"] == 110.0
|
|
assert r["arrangedTotalQty"] == 110.0
|
|
|
|
|
|
def test_evaluate_remaining_reminder():
|
|
r = evaluate_product_qty_for_arrange(
|
|
has_fixed_qty=True,
|
|
contract_qty=200,
|
|
arranged_total=150,
|
|
old_plan_qty=0,
|
|
new_plan_qty=40,
|
|
)
|
|
assert r is not None
|
|
assert r["kind"] == "remaining"
|
|
assert r["remainingQty"] == 10.0
|
|
assert r["arrangedTotalQty"] == 190.0
|
|
|
|
|
|
def test_evaluate_rearrange_deducts_old():
|
|
# arranged 已含旧 30;改为 40 → effective 110
|
|
r = evaluate_product_qty_for_arrange(
|
|
has_fixed_qty=True,
|
|
contract_qty=100,
|
|
arranged_total=100,
|
|
old_plan_qty=30,
|
|
new_plan_qty=40,
|
|
)
|
|
assert r is not None
|
|
assert r["kind"] == "violation"
|
|
assert r["overflowQty"] == 10.0
|
|
|
|
|
|
def test_evaluate_framework_cumulative():
|
|
r = evaluate_product_qty_for_arrange(
|
|
has_fixed_qty=False,
|
|
contract_qty=None,
|
|
arranged_total=126.5,
|
|
old_plan_qty=0,
|
|
new_plan_qty=28,
|
|
)
|
|
assert r is not None
|
|
assert r["kind"] == "framework_cumulative"
|
|
assert r["arrangedTotalQty"] == 154.5
|
|
assert r["thisPlanQty"] == 28.0
|