104 lines
3.4 KiB
Python
104 lines
3.4 KiB
Python
|
|
"""指导成本解析只认已登记事实:多条候选不猜、单位与封存状态必须显式匹配。"""
|
|||
|
|
|
|||
|
|
from decimal import Decimal
|
|||
|
|
|
|||
|
|
import pytest
|
|||
|
|
|
|||
|
|
from app.core.exceptions import BadRequestError
|
|||
|
|
from app.services.tplus.guidance import check_unit_guidance_conflicts, resolve_guidance
|
|||
|
|
|
|||
|
|
|
|||
|
|
class Fact:
|
|||
|
|
"""最小事实替身:resolve_guidance 只读取 kind 与 data_json。"""
|
|||
|
|
|
|||
|
|
def __init__(self, data_json):
|
|||
|
|
self.kind = "GUIDANCE"
|
|||
|
|
self.data_json = data_json
|
|||
|
|
|
|||
|
|
|
|||
|
|
def facts_of(*rows):
|
|||
|
|
"""按 (kind, fact_key) 组装成 latest_guidance_facts 的返回结构。"""
|
|||
|
|
return {("GUIDANCE", str(index)): Fact(row) for index, row in enumerate(rows)}
|
|||
|
|
|
|||
|
|
|
|||
|
|
def final_row(**overrides):
|
|||
|
|
row = {
|
|||
|
|
"product_id": 9,
|
|||
|
|
"month": "2026-03",
|
|||
|
|
"basis": "UNIT",
|
|||
|
|
"unit": "根",
|
|||
|
|
"is_finalized": True,
|
|||
|
|
}
|
|||
|
|
row.update(overrides)
|
|||
|
|
return row
|
|||
|
|
|
|||
|
|
|
|||
|
|
def test_没有匹配事实时返回缺失():
|
|||
|
|
state, guidance = resolve_guidance(facts_of(), 9, "line-1", "2026-03", "根")
|
|||
|
|
assert state == "MISSING"
|
|||
|
|
assert guidance is None
|
|||
|
|
|
|||
|
|
|
|||
|
|
def test_多条候选时不选择其中一条():
|
|||
|
|
rows = facts_of(final_row(), final_row(shipment_line_id="line-1"))
|
|||
|
|
state, guidance = resolve_guidance(rows, 9, "line-1", "2026-03", "根")
|
|||
|
|
assert state == "AMBIGUOUS"
|
|||
|
|
assert guidance is None
|
|||
|
|
|
|||
|
|
|
|||
|
|
def test_唯一候选且单位一致时返回已封存事实():
|
|||
|
|
row = final_row()
|
|||
|
|
state, guidance = resolve_guidance(facts_of(row), 9, "line-1", "2026-03", "根")
|
|||
|
|
assert state == "FINALIZED"
|
|||
|
|
assert guidance is row
|
|||
|
|
|
|||
|
|
|
|||
|
|
def test_单位不一致时不采用该事实():
|
|||
|
|
state, guidance = resolve_guidance(facts_of(final_row(unit="吨")), 9, "line-1", "2026-03", "根")
|
|||
|
|
assert state == "UNIT_MISMATCH"
|
|||
|
|
assert guidance is None
|
|||
|
|
|
|||
|
|
|
|||
|
|
def test_未封存事实不能作为指导价依据():
|
|||
|
|
state, guidance = resolve_guidance(facts_of(final_row(is_finalized=False)), 9, "line-1", "2026-03", "根")
|
|||
|
|
assert state == "UNFINALIZED"
|
|||
|
|
assert guidance is None
|
|||
|
|
|
|||
|
|
|
|||
|
|
def test_按明细行登记的指导价只对该行生效():
|
|||
|
|
rows = facts_of(final_row(basis="LINE", shipment_line_id="line-1"))
|
|||
|
|
assert resolve_guidance(rows, 9, "line-1", "2026-03", "根")[0] == "FINALIZED"
|
|||
|
|
assert resolve_guidance(rows, 9, "line-2", "2026-03", "根")[0] == "MISSING"
|
|||
|
|
|
|||
|
|
|
|||
|
|
def test_同一产品同月已有事实时拒绝重复登记():
|
|||
|
|
with pytest.raises(BadRequestError, match="重叠"):
|
|||
|
|
check_unit_guidance_conflicts(facts_of(final_row()), 9, "2026-03", "根", "10", [])
|
|||
|
|
|
|||
|
|
|
|||
|
|
def test_历史封存明细的成本或单位不一致时拒绝():
|
|||
|
|
with pytest.raises(BadRequestError, match="不一致"):
|
|||
|
|
check_unit_guidance_conflicts(
|
|||
|
|
facts_of(), 9, "2026-03", "根", "10",
|
|||
|
|
[{"unit_cost": "11", "unit": "根"}],
|
|||
|
|
)
|
|||
|
|
with pytest.raises(BadRequestError, match="不一致"):
|
|||
|
|
check_unit_guidance_conflicts(
|
|||
|
|
facts_of(), 9, "2026-03", "根", "10",
|
|||
|
|
[{"unit_cost": "10", "unit": "吨"}],
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
|
|||
|
|
def test_事实为空且历史一致时放行():
|
|||
|
|
check_unit_guidance_conflicts(
|
|||
|
|
facts_of(), 9, "2026-03", "根", Decimal("10"),
|
|||
|
|
[{"unit_cost": "10", "unit": "根"}, {"unit_cost": "10.00", "unit": "根"}],
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
|
|||
|
|
def test_金额非法时拒绝():
|
|||
|
|
for bad in ("abc", "-1", "nan"):
|
|||
|
|
with pytest.raises(BadRequestError, match="不一致"):
|
|||
|
|
check_unit_guidance_conflicts(facts_of(), 9, "2026-03", "根", bad, [])
|
|||
|
|
|