test_oa_auto_pass_initiator.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. """OA:审批人为发起人时自动通过"""
  2. import json
  3. import unittest
  4. from datetime import datetime
  5. from unittest.mock import patch
  6. from sqlalchemy import create_engine
  7. from sqlalchemy.orm import sessionmaker
  8. class TestAutoPassInitiator(unittest.TestCase):
  9. def setUp(self):
  10. from app.models.models import OaAppVersion, OaInstance, OaTask
  11. self.engine = create_engine("sqlite:///:memory:")
  12. OaAppVersion.__table__.create(self.engine)
  13. OaInstance.__table__.create(self.engine)
  14. OaTask.__table__.create(self.engine)
  15. Session = sessionmaker(bind=self.engine, autoflush=False, autocommit=False)
  16. self.db = Session()
  17. self.OaAppVersion = OaAppVersion
  18. self.OaInstance = OaInstance
  19. self.OaTask = OaTask
  20. self.now = datetime.now()
  21. def tearDown(self):
  22. self.db.close()
  23. self.engine.dispose()
  24. def _seed(self, *, flow, advanced, initiator="u0", tasks=None):
  25. self.db.add(
  26. self.OaAppVersion(
  27. id="v1",
  28. app_id="app1",
  29. version_no=1,
  30. status="published",
  31. is_latest_published="1",
  32. form_schema_json="{}",
  33. flow_tree_json=json.dumps(flow),
  34. advanced_settings_json=json.dumps(advanced),
  35. tenant_no="zkxw",
  36. create_dept_id="",
  37. create_time=self.now,
  38. update_time=self.now,
  39. del_flag=0,
  40. )
  41. )
  42. self.db.add(
  43. self.OaInstance(
  44. id="i1",
  45. app_id="app1",
  46. app_version_id="v1",
  47. title="t",
  48. initiator_user_id=initiator,
  49. initiator_dept_id="d1",
  50. form_data_json="{}",
  51. status="running",
  52. tenant_no="zkxw",
  53. create_dept_id="",
  54. create_time=self.now,
  55. update_time=self.now,
  56. del_flag=0,
  57. )
  58. )
  59. for t in tasks or []:
  60. self.db.add(t)
  61. self.db.commit()
  62. def test_or_sign_sole_initiator_auto_passes_to_end(self):
  63. """开关开:或签唯一审批人=发起人 → 提交推进后自动通过到结束。"""
  64. from app.services.oa_engine import advance_from
  65. flow = {
  66. "id": "s",
  67. "type": "start",
  68. "child": {
  69. "id": "a1",
  70. "type": "approval",
  71. "name": "审批人",
  72. "approverConfig": {
  73. "assigneeType": "users",
  74. "userIds": ["u0"],
  75. "signMode": "or",
  76. },
  77. "child": {"id": "e1", "type": "end"},
  78. },
  79. }
  80. self._seed(
  81. flow=flow,
  82. advanced={"autoPassWhenApproverIsInitiator": True},
  83. )
  84. inst = self.db.query(self.OaInstance).filter_by(id="i1").first()
  85. ver = self.db.query(self.OaAppVersion).filter_by(id="v1").first()
  86. with patch(
  87. "app.services.oa_engine.resolve_assignees",
  88. return_value=(["u0"], {"resolve_source": "users"}),
  89. ), patch(
  90. "app.services.oa_engine._next_task_order_num",
  91. return_value=1,
  92. ):
  93. advance_from(
  94. self.db, inst=inst, ver=ver, from_node=None, user_id="u0"
  95. )
  96. # Session autoflush=False:未 flush 时 query 看不到刚 add 的任务
  97. self.db.flush()
  98. tasks = self.db.query(self.OaTask).filter_by(instance_id="i1").all()
  99. self.assertEqual(len(tasks), 1)
  100. self.assertEqual(tasks[0].status, "approved")
  101. self.assertEqual(tasks[0].comment_text, "系统自动通过")
  102. self.assertEqual(inst.status, "completed")
  103. def test_switch_off_keeps_pending(self):
  104. from app.services.oa_engine import advance_from
  105. flow = {
  106. "id": "s",
  107. "type": "start",
  108. "child": {
  109. "id": "a1",
  110. "type": "approval",
  111. "name": "审批人",
  112. "approverConfig": {
  113. "assigneeType": "users",
  114. "userIds": ["u0"],
  115. "signMode": "or",
  116. },
  117. "child": {"id": "e1", "type": "end"},
  118. },
  119. }
  120. self._seed(flow=flow, advanced={"autoPassWhenApproverIsInitiator": False})
  121. inst = self.db.query(self.OaInstance).filter_by(id="i1").first()
  122. ver = self.db.query(self.OaAppVersion).filter_by(id="v1").first()
  123. with patch(
  124. "app.services.oa_engine.resolve_assignees",
  125. return_value=(["u0"], {"resolve_source": "users"}),
  126. ), patch(
  127. "app.services.oa_engine._next_task_order_num", return_value=1
  128. ):
  129. advance_from(self.db, inst=inst, ver=ver, from_node=None, user_id="u0")
  130. self.db.flush()
  131. t = self.db.query(self.OaTask).filter_by(instance_id="i1").one()
  132. self.assertEqual(t.status, "pending")
  133. self.assertEqual(inst.status, "running")
  134. def test_or_sign_multi_cancels_siblings(self):
  135. from app.services.oa_engine import advance_from
  136. flow = {
  137. "id": "s",
  138. "type": "start",
  139. "child": {
  140. "id": "a1",
  141. "type": "approval",
  142. "name": "审批人",
  143. "approverConfig": {
  144. "assigneeType": "users",
  145. "userIds": ["u0", "u2"],
  146. "signMode": "or",
  147. },
  148. "child": {"id": "e1", "type": "end"},
  149. },
  150. }
  151. self._seed(flow=flow, advanced={"autoPassWhenApproverIsInitiator": True})
  152. inst = self.db.query(self.OaInstance).filter_by(id="i1").first()
  153. ver = self.db.query(self.OaAppVersion).filter_by(id="v1").first()
  154. with patch(
  155. "app.services.oa_engine.resolve_assignees",
  156. return_value=(["u0", "u2"], {"resolve_source": "users"}),
  157. ), patch(
  158. "app.services.oa_engine._next_task_order_num", return_value=1
  159. ):
  160. advance_from(self.db, inst=inst, ver=ver, from_node=None, user_id="u0")
  161. self.db.flush()
  162. by_user = {
  163. t.assignee_user_id: t
  164. for t in self.db.query(self.OaTask).filter_by(instance_id="i1").all()
  165. }
  166. self.assertEqual(by_user["u0"].status, "approved")
  167. self.assertEqual(by_user["u0"].comment_text, "系统自动通过")
  168. self.assertEqual(by_user["u2"].status, "done")
  169. self.assertEqual(inst.status, "completed")
  170. def test_and_sign_only_completes_self(self):
  171. from app.services.oa_engine import advance_from
  172. flow = {
  173. "id": "s",
  174. "type": "start",
  175. "child": {
  176. "id": "a1",
  177. "type": "approval",
  178. "name": "审批人",
  179. "approverConfig": {
  180. "assigneeType": "users",
  181. "userIds": ["u0", "u2"],
  182. "signMode": "and",
  183. },
  184. "child": {"id": "e1", "type": "end"},
  185. },
  186. }
  187. self._seed(flow=flow, advanced={"autoPassWhenApproverIsInitiator": True})
  188. inst = self.db.query(self.OaInstance).filter_by(id="i1").first()
  189. ver = self.db.query(self.OaAppVersion).filter_by(id="v1").first()
  190. with patch(
  191. "app.services.oa_engine.resolve_assignees",
  192. return_value=(["u0", "u2"], {"resolve_source": "users"}),
  193. ), patch(
  194. "app.services.oa_engine._next_task_order_num", return_value=1
  195. ):
  196. advance_from(self.db, inst=inst, ver=ver, from_node=None, user_id="u0")
  197. self.db.flush()
  198. by_user = {
  199. t.assignee_user_id: t
  200. for t in self.db.query(self.OaTask).filter_by(instance_id="i1").all()
  201. }
  202. self.assertEqual(by_user["u0"].status, "approved")
  203. self.assertEqual(by_user["u2"].status, "pending")
  204. self.assertEqual(inst.status, "running")
  205. def test_consecutive_nodes_both_auto_pass(self):
  206. from app.services.oa_engine import advance_from
  207. flow = {
  208. "id": "s",
  209. "type": "start",
  210. "child": {
  211. "id": "a1",
  212. "type": "approval",
  213. "name": "一审",
  214. "approverConfig": {
  215. "assigneeType": "users",
  216. "userIds": ["u0"],
  217. "signMode": "or",
  218. },
  219. "child": {
  220. "id": "a2",
  221. "type": "approval",
  222. "name": "二审",
  223. "approverConfig": {
  224. "assigneeType": "users",
  225. "userIds": ["u0"],
  226. "signMode": "or",
  227. },
  228. "child": {"id": "e1", "type": "end"},
  229. },
  230. },
  231. }
  232. self._seed(flow=flow, advanced={"autoPassWhenApproverIsInitiator": True})
  233. inst = self.db.query(self.OaInstance).filter_by(id="i1").first()
  234. ver = self.db.query(self.OaAppVersion).filter_by(id="v1").first()
  235. with patch(
  236. "app.services.oa_engine.resolve_assignees",
  237. return_value=(["u0"], {"resolve_source": "users"}),
  238. ), patch(
  239. "app.services.oa_engine._next_task_order_num", return_value=1
  240. ):
  241. advance_from(self.db, inst=inst, ver=ver, from_node=None, user_id="u0")
  242. self.db.flush()
  243. tasks = self.db.query(self.OaTask).filter_by(instance_id="i1").all()
  244. self.assertEqual(len(tasks), 2)
  245. self.assertTrue(all(t.status == "approved" for t in tasks))
  246. self.assertTrue(all(t.comment_text == "系统自动通过" for t in tasks))
  247. self.assertEqual(inst.status, "completed")
  248. def test_comment_required_does_not_block_auto_pass(self):
  249. from app.services.oa_engine import advance_from
  250. flow = {
  251. "id": "s",
  252. "type": "start",
  253. "child": {
  254. "id": "a1",
  255. "type": "approval",
  256. "name": "审批人",
  257. "approverConfig": {
  258. "assigneeType": "users",
  259. "userIds": ["u0"],
  260. "signMode": "or",
  261. },
  262. "child": {"id": "e1", "type": "end"},
  263. },
  264. }
  265. self._seed(
  266. flow=flow,
  267. advanced={
  268. "autoPassWhenApproverIsInitiator": True,
  269. "commentRequired": True,
  270. },
  271. )
  272. inst = self.db.query(self.OaInstance).filter_by(id="i1").first()
  273. ver = self.db.query(self.OaAppVersion).filter_by(id="v1").first()
  274. with patch(
  275. "app.services.oa_engine.resolve_assignees",
  276. return_value=(["u0"], {"resolve_source": "users"}),
  277. ), patch(
  278. "app.services.oa_engine._next_task_order_num", return_value=1
  279. ):
  280. advance_from(self.db, inst=inst, ver=ver, from_node=None, user_id="u0")
  281. self.db.flush()
  282. t = self.db.query(self.OaTask).filter_by(instance_id="i1").one()
  283. self.assertEqual(t.status, "approved")
  284. self.assertEqual(inst.status, "completed")
  285. if __name__ == "__main__":
  286. unittest.main()