test_pl_io_order_site_feedback_status.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. from datetime import datetime
  2. from decimal import Decimal
  3. from types import SimpleNamespace
  4. from unittest.mock import MagicMock
  5. import pytest
  6. from app.services.pl_io_order_service import apply_site_feedback
  7. def _header(**kwargs):
  8. defaults = dict(
  9. id="order-1",
  10. tenant_no="t1",
  11. status="in_progress",
  12. site_feedback_remark=None,
  13. site_feedback_images=None,
  14. site_feedback_time=None,
  15. site_first_submitted_at=None,
  16. update_by=None,
  17. )
  18. defaults.update(kwargs)
  19. return SimpleNamespace(**defaults)
  20. def _item(item_id="item-1"):
  21. return SimpleNamespace(
  22. id=item_id,
  23. plan_product_id="pp-1",
  24. actual_qty=None,
  25. qty_source=None,
  26. update_by=None,
  27. )
  28. def test_apply_site_feedback_sets_status_completed():
  29. header = _header()
  30. item = _item()
  31. db = MagicMock()
  32. apply_site_feedback(
  33. db,
  34. kind="inbound",
  35. header=header,
  36. items=[item],
  37. item_qty={"item-1": Decimal("12.5")},
  38. remark="ok",
  39. images=["a.jpg"],
  40. status="completed",
  41. )
  42. assert header.status == "completed"
  43. assert item.actual_qty == Decimal("12.5")
  44. assert header.site_first_submitted_at is not None
  45. assert header.site_feedback_remark == "ok"
  46. db.flush.assert_called_once()
  47. def test_apply_site_feedback_sets_status_voided():
  48. header = _header()
  49. apply_site_feedback(
  50. MagicMock(),
  51. kind="inbound",
  52. header=header,
  53. items=[_item()],
  54. item_qty={"item-1": Decimal("1")},
  55. remark=None,
  56. images=[],
  57. status="voided",
  58. )
  59. assert header.status == "voided"
  60. def test_apply_site_feedback_keeps_in_progress_when_requested():
  61. """首次提交选进行中时,不得隐式改成已完成。"""
  62. header = _header(status="in_progress")
  63. apply_site_feedback(
  64. MagicMock(),
  65. kind="inbound",
  66. header=header,
  67. items=[_item()],
  68. item_qty={"item-1": Decimal("1")},
  69. remark=None,
  70. images=[],
  71. status="in_progress",
  72. )
  73. assert header.status == "in_progress"
  74. assert header.site_first_submitted_at is not None
  75. def test_apply_site_feedback_rejects_invalid_status():
  76. header = _header()
  77. with pytest.raises(ValueError, match="无效状态"):
  78. apply_site_feedback(
  79. MagicMock(),
  80. kind="inbound",
  81. header=header,
  82. items=[_item()],
  83. item_qty={},
  84. remark=None,
  85. images=[],
  86. status="done",
  87. )
  88. def test_apply_site_feedback_rejects_already_voided():
  89. header = _header(status="voided")
  90. with pytest.raises(ValueError, match="单据已作废"):
  91. apply_site_feedback(
  92. MagicMock(),
  93. kind="inbound",
  94. header=header,
  95. items=[_item()],
  96. item_qty={},
  97. remark=None,
  98. images=[],
  99. status="completed",
  100. )
  101. def test_apply_site_feedback_does_not_reset_first_submitted_at():
  102. first = datetime(2026, 1, 1, 12, 0, 0)
  103. header = _header(
  104. status="completed",
  105. site_first_submitted_at=first,
  106. )
  107. apply_site_feedback(
  108. MagicMock(),
  109. kind="inbound",
  110. header=header,
  111. items=[_item()],
  112. item_qty={"item-1": Decimal("2")},
  113. remark="二改",
  114. images=[],
  115. status="in_progress",
  116. )
  117. assert header.status == "in_progress"
  118. assert header.site_first_submitted_at == first