test_pl_plan_execution_lock.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. from types import SimpleNamespace
  2. from unittest.mock import MagicMock, patch
  3. import pytest
  4. from fastapi import HTTPException
  5. from app.api.pl.plan import cancel_plan, force_void_plan
  6. from app.services.pl_plan_execution_lock import (
  7. execution_lock_message,
  8. inventory_status_is_executed,
  9. pick_execution_lock_reason,
  10. )
  11. def test_pick_execution_lock_reason_prefers_weighed():
  12. assert (
  13. pick_execution_lock_reason(
  14. weighed=True,
  15. inventory_executed=True,
  16. site_feedback=True,
  17. manual_qty=True,
  18. )
  19. == "已过磅"
  20. )
  21. def test_pick_execution_lock_reason_order():
  22. assert (
  23. pick_execution_lock_reason(
  24. weighed=False, inventory_executed=True, site_feedback=True, manual_qty=True
  25. )
  26. == "地磅出入库已执行"
  27. )
  28. assert (
  29. pick_execution_lock_reason(
  30. weighed=False, inventory_executed=False, site_feedback=True, manual_qty=True
  31. )
  32. == "现场已回传"
  33. )
  34. assert (
  35. pick_execution_lock_reason(
  36. weighed=False, inventory_executed=False, site_feedback=False, manual_qty=True
  37. )
  38. == "入库或出库实际数量已修改"
  39. )
  40. assert (
  41. pick_execution_lock_reason(
  42. weighed=False, inventory_executed=False, site_feedback=False, manual_qty=False
  43. )
  44. is None
  45. )
  46. def test_inventory_status_is_executed_only_outside_revokable():
  47. assert inventory_status_is_executed("已完成") is True
  48. assert inventory_status_is_executed("待入库") is False
  49. assert inventory_status_is_executed("") is False
  50. assert inventory_status_is_executed(None) is False
  51. def test_execution_lock_messages():
  52. assert (
  53. execution_lock_message("已过磅", "arrange")
  54. == "该计划已过磅,不能再调整,请联系管理员处理"
  55. )
  56. assert (
  57. execution_lock_message("现场已回传", "void")
  58. == "该计划现场已回传,不能作废,请联系管理员处理"
  59. )
  60. def test_cancel_plan_rejects_started_plan():
  61. plan = SimpleNamespace(
  62. id="p1",
  63. follow_status="arranged",
  64. archive_status="unarchived",
  65. transport_status="arranged",
  66. transport_id="t1",
  67. )
  68. user = MagicMock(id="u1")
  69. with patch("app.api.pl.plan.get_tenant_no", return_value="t1"), patch(
  70. "app.api.pl.plan._get_plan", return_value=plan
  71. ), patch(
  72. "app.api.pl.plan.plan_execution_lock_reason", return_value="已过磅"
  73. ), patch("app.api.pl.plan.void_io_orders_for_plan") as void_io:
  74. with pytest.raises(HTTPException) as exc:
  75. cancel_plan("p1", body=None, db=MagicMock(), user=user)
  76. assert exc.value.status_code == 400
  77. assert exc.value.detail == "该计划已过磅,不能作废,请联系管理员处理"
  78. void_io.assert_not_called()
  79. def test_force_void_rejects_non_super_admin_without_permission():
  80. user = MagicMock(id="u1", role="admin")
  81. with patch("app.api.pl.plan.get_user_menu_codes", return_value=set()):
  82. with pytest.raises(HTTPException) as exc:
  83. force_void_plan("p1", body=None, db=MagicMock(), user=user)
  84. assert exc.value.status_code == 403
  85. def test_force_void_super_admin_cancels_started_plan():
  86. plan = SimpleNamespace(
  87. id="p1",
  88. follow_status="arranged",
  89. archive_status="unarchived",
  90. transport_status="arranged",
  91. transport_id="t1",
  92. cancel_reason=None,
  93. cancel_time=None,
  94. cancel_by=None,
  95. update_by=None,
  96. )
  97. user = MagicMock(id="u1", role="super_admin")
  98. body = SimpleNamespace(cancel_reason="过磅作废,这趟不算")
  99. db = MagicMock()
  100. with patch("app.api.pl.plan.get_tenant_no", return_value="t1"), patch(
  101. "app.api.pl.plan._get_plan", return_value=plan
  102. ), patch(
  103. "app.api.pl.plan.plan_execution_lock_reason", return_value="已过磅"
  104. ), patch("app.api.pl.plan.apply_force_void_downstream") as apply:
  105. result = force_void_plan("p1", body=body, db=db, user=user)
  106. apply.assert_called_once()
  107. assert result.follow_status == "cancelled"
  108. assert result.archive_status == "cancelled"
  109. assert result.transport_status == "pending"
  110. assert result.cancel_reason == "过磅作废,这趟不算"
  111. db.commit.assert_called_once()