Files
shanxia2.0/backend/tests/test_pl_plan_execution_lock.py
T
wuzilongandCursor bde19c0644 feat: 锁定执行中计划并按装卸明细差量归档库存,补齐派车日历数量与酸碱类型。
执行中的运输计划不再随意改单;库存流水记到装卸产品明细;手工派车日历区分酸碱并记录数量。同时收紧合同附件与子表的差量保存,并清理已废弃的 bak 备份目录。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-29 07:59:53 +08:00

128 lines
4.2 KiB
Python

from types import SimpleNamespace
from unittest.mock import MagicMock, patch
import pytest
from fastapi import HTTPException
from app.api.pl.plan import cancel_plan, force_void_plan
from app.services.pl_plan_execution_lock import (
execution_lock_message,
inventory_status_is_executed,
pick_execution_lock_reason,
)
def test_pick_execution_lock_reason_prefers_weighed():
assert (
pick_execution_lock_reason(
weighed=True,
inventory_executed=True,
site_feedback=True,
manual_qty=True,
)
== "已过磅"
)
def test_pick_execution_lock_reason_order():
assert (
pick_execution_lock_reason(
weighed=False, inventory_executed=True, site_feedback=True, manual_qty=True
)
== "地磅出入库已执行"
)
assert (
pick_execution_lock_reason(
weighed=False, inventory_executed=False, site_feedback=True, manual_qty=True
)
== "现场已回传"
)
assert (
pick_execution_lock_reason(
weighed=False, inventory_executed=False, site_feedback=False, manual_qty=True
)
== "入库或出库实际数量已修改"
)
assert (
pick_execution_lock_reason(
weighed=False, inventory_executed=False, site_feedback=False, manual_qty=False
)
is None
)
def test_inventory_status_is_executed_only_outside_revokable():
assert inventory_status_is_executed("已完成") is True
assert inventory_status_is_executed("待入库") is False
assert inventory_status_is_executed("") is False
assert inventory_status_is_executed(None) is False
def test_execution_lock_messages():
assert (
execution_lock_message("已过磅", "arrange")
== "该计划已过磅,不能再调整,请联系管理员处理"
)
assert (
execution_lock_message("现场已回传", "void")
== "该计划现场已回传,不能作废,请联系管理员处理"
)
def test_cancel_plan_rejects_started_plan():
plan = SimpleNamespace(
id="p1",
follow_status="arranged",
archive_status="unarchived",
transport_status="arranged",
transport_id="t1",
)
user = MagicMock(id="u1")
with patch("app.api.pl.plan.get_tenant_no", return_value="t1"), patch(
"app.api.pl.plan._get_plan", return_value=plan
), patch(
"app.api.pl.plan.plan_execution_lock_reason", return_value="已过磅"
), patch("app.api.pl.plan.void_io_orders_for_plan") as void_io:
with pytest.raises(HTTPException) as exc:
cancel_plan("p1", body=None, db=MagicMock(), user=user)
assert exc.value.status_code == 400
assert exc.value.detail == "该计划已过磅,不能作废,请联系管理员处理"
void_io.assert_not_called()
def test_force_void_rejects_non_super_admin_without_permission():
user = MagicMock(id="u1", role="admin")
with patch("app.api.pl.plan.get_user_menu_codes", return_value=set()):
with pytest.raises(HTTPException) as exc:
force_void_plan("p1", body=None, db=MagicMock(), user=user)
assert exc.value.status_code == 403
def test_force_void_super_admin_cancels_started_plan():
plan = SimpleNamespace(
id="p1",
follow_status="arranged",
archive_status="unarchived",
transport_status="arranged",
transport_id="t1",
cancel_reason=None,
cancel_time=None,
cancel_by=None,
update_by=None,
)
user = MagicMock(id="u1", role="super_admin")
body = SimpleNamespace(cancel_reason="过磅作废,这趟不算")
db = MagicMock()
with patch("app.api.pl.plan.get_tenant_no", return_value="t1"), patch(
"app.api.pl.plan._get_plan", return_value=plan
), patch(
"app.api.pl.plan.plan_execution_lock_reason", return_value="已过磅"
), patch("app.api.pl.plan.apply_force_void_downstream") as apply:
result = force_void_plan("p1", body=body, db=db, user=user)
apply.assert_called_once()
assert result.follow_status == "cancelled"
assert result.archive_status == "cancelled"
assert result.transport_status == "pending"
assert result.cancel_reason == "过磅作废,这趟不算"
db.commit.assert_called_once()