test_oa_receive_account.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. import json
  2. import unittest
  3. from datetime import datetime
  4. from sqlalchemy import create_engine
  5. from sqlalchemy.orm import sessionmaker
  6. from app.crud import oa_receive_account as ora_crud
  7. from app.models.base import Base
  8. from app.models.models import OaApp, OaAppVersion, OaReceiveAccount, SysUser
  9. from app.services.oa_engine import OaEngineError, start_instance
  10. from app.services.oa_receive_account import (
  11. normalize_account_fields,
  12. validate_receive_account_form_values,
  13. validate_receive_account_ownership,
  14. )
  15. class TestNormalizeAccountFields(unittest.TestCase):
  16. def test_alipay_clears_bank(self):
  17. ok, err, data = normalize_account_fields(
  18. {
  19. "accountType": "alipay",
  20. "accountName": "张三",
  21. "accountNo": "13800138000",
  22. "bankName": "工行",
  23. "bankProvince": "安徽",
  24. "bankCity": "合肥",
  25. "bankBranch": "xx支行",
  26. "remark": "r",
  27. }
  28. )
  29. self.assertTrue(ok)
  30. self.assertIsNone(err)
  31. self.assertEqual(data["account_type"], "alipay")
  32. self.assertEqual(data["account_name"], "张三")
  33. self.assertEqual(data["bank_name"], "")
  34. self.assertEqual(data["bank_province"], "")
  35. self.assertEqual(data["bank_city"], "")
  36. self.assertEqual(data["bank_branch"], "")
  37. def test_personal_requires_bank(self):
  38. ok, err, _ = normalize_account_fields(
  39. {
  40. "accountType": "personal",
  41. "accountName": "李四",
  42. "accountNo": "6222",
  43. }
  44. )
  45. self.assertFalse(ok)
  46. self.assertTrue(err)
  47. def test_personal_ok(self):
  48. ok, err, data = normalize_account_fields(
  49. {
  50. "accountType": "personal",
  51. "accountName": "李四",
  52. "accountNo": "6222",
  53. "bankName": "工行",
  54. "bankProvince": "安徽省",
  55. "bankCity": "合肥市",
  56. "bankBranch": "长江中路支行",
  57. }
  58. )
  59. self.assertTrue(ok)
  60. self.assertEqual(data["bank_name"], "工行")
  61. class TestValidateReceiveAccountFormValues(unittest.TestCase):
  62. def test_ok_array(self):
  63. schema = {
  64. "rule": [
  65. {
  66. "type": "receiveAccount",
  67. "field": "acc",
  68. "title": "收款账户",
  69. "$required": True,
  70. }
  71. ]
  72. }
  73. data = {
  74. "acc": [
  75. {
  76. "id": "1",
  77. "accountType": "alipay",
  78. "accountName": "张三",
  79. "accountNo": "138",
  80. "bankName": "",
  81. "bankProvince": "",
  82. "bankCity": "",
  83. "bankBranch": "",
  84. "remark": "",
  85. }
  86. ]
  87. }
  88. self.assertEqual(validate_receive_account_form_values(schema, data), [])
  89. def test_required_empty(self):
  90. schema = {
  91. "rule": [
  92. {"type": "receiveAccount", "field": "acc", "title": "收款账户", "$required": True}
  93. ]
  94. }
  95. errs = validate_receive_account_form_values(schema, {"acc": []})
  96. self.assertTrue(any("收款账户" in e for e in errs))
  97. def test_invalid_item(self):
  98. schema = {"rule": [{"type": "receiveAccount", "field": "acc"}]}
  99. errs = validate_receive_account_form_values(schema, {"acc": [{"id": "1"}]})
  100. self.assertTrue(len(errs) >= 1)
  101. def _snap(account_id: str) -> dict:
  102. return {
  103. "id": account_id,
  104. "accountType": "alipay",
  105. "accountName": "张三",
  106. "accountNo": "13800138000",
  107. "bankName": "",
  108. "bankProvince": "",
  109. "bankCity": "",
  110. "bankBranch": "",
  111. "remark": "",
  112. }
  113. def _schema(*fields: str) -> dict:
  114. return {
  115. "rule": [
  116. {"type": "receiveAccount", "field": field, "title": field}
  117. for field in fields
  118. ]
  119. }
  120. class TestReceiveAccountOwner(unittest.TestCase):
  121. def setUp(self):
  122. self.engine = create_engine("sqlite:///:memory:")
  123. Base.metadata.create_all(
  124. self.engine,
  125. tables=[
  126. OaReceiveAccount.__table__,
  127. SysUser.__table__,
  128. OaApp.__table__,
  129. OaAppVersion.__table__,
  130. ],
  131. )
  132. self.db = sessionmaker(bind=self.engine, autoflush=False, autocommit=False)()
  133. now = datetime(2026, 10, 21, 10, 0, 0)
  134. self.db.add_all(
  135. [
  136. SysUser(id="me", user_no="me", nick_name="我自己", password="", tenant_no="zkxw", create_dept_id="", create_time=now, update_time=now, del_flag=0),
  137. SysUser(id="other", user_no="other", nick_name="别人", password="", tenant_no="zkxw", create_dept_id="", create_time=now, update_time=now, del_flag=0),
  138. ]
  139. )
  140. self.db.add_all(
  141. [
  142. self._account("mine", "me", 0),
  143. self._account("padded", " me ", 0),
  144. self._account("theirs", "other", 0),
  145. self._account("blank", "", 0),
  146. self._account("none", None, 0),
  147. self._account("gone", "me", 1),
  148. self._account("other-tenant", "me", 0, tenant_no="other"),
  149. ]
  150. )
  151. self.db.commit()
  152. def tearDown(self):
  153. self.db.close()
  154. def _account(self, account_id, create_by, del_flag, tenant_no="zkxw"):
  155. now = datetime(2026, 10, 21, 10, 0, 0)
  156. return OaReceiveAccount(
  157. id=account_id,
  158. account_type="alipay",
  159. account_name=account_id,
  160. account_no=account_id,
  161. tenant_no=tenant_no,
  162. create_by=create_by,
  163. create_dept_id="",
  164. create_time=now,
  165. update_time=now,
  166. del_flag=del_flag,
  167. )
  168. def test_options_only_current_owner(self):
  169. ids = {row["id"] for row in ora_crud.list_options(self.db, tenant_no="zkxw", user_id="me")}
  170. self.assertEqual(ids, {"padded", "mine"})
  171. def test_list_keeps_every_tenant_account_and_owner_name(self):
  172. rows, total = ora_crud.list_accounts(self.db, tenant_no="zkxw", page=1, page_size=20)
  173. by_id = {row["id"]: row for row in rows}
  174. self.assertEqual(total, 5)
  175. self.assertEqual(by_id["mine"]["ownerUserId"], "me")
  176. self.assertEqual(by_id["mine"]["ownerName"], "我自己")
  177. self.assertEqual(by_id["padded"]["ownerUserId"], "me")
  178. self.assertEqual(by_id["padded"]["ownerName"], "我自己")
  179. self.assertEqual(by_id["blank"]["ownerUserId"], "")
  180. self.assertEqual(by_id["blank"]["ownerName"], "—")
  181. self.assertEqual(by_id["none"]["ownerName"], "—")
  182. self.assertNotIn("gone", by_id)
  183. self.assertNotIn("other-tenant", by_id)
  184. def test_missing_user_still_selectable_with_dash_name(self):
  185. self.db.add(self._account("orphan", "ghost", 0))
  186. self.db.commit()
  187. ids = [row["id"] for row in ora_crud.list_options(self.db, tenant_no="zkxw", user_id="ghost")]
  188. self.assertEqual(ids, ["orphan"])
  189. row = ora_crud.get_account(self.db, "orphan", "zkxw")
  190. self.assertEqual(row["ownerUserId"], "ghost")
  191. self.assertEqual(row["ownerName"], "—")
  192. def test_create_sets_current_user_as_owner(self):
  193. created = ora_crud.create_account(
  194. self.db,
  195. tenant_no="zkxw",
  196. user_id="me",
  197. dept_id="",
  198. fields={
  199. "account_type": "alipay",
  200. "account_name": "新户",
  201. "account_no": "6222",
  202. "bank_name": "",
  203. "bank_province": "",
  204. "bank_city": "",
  205. "bank_branch": "",
  206. "remark": "",
  207. },
  208. )
  209. self.assertEqual(created["ownerUserId"], "me")
  210. self.assertEqual(created["ownerName"], "我自己")
  211. ids = {row["id"] for row in ora_crud.list_options(self.db, tenant_no="zkxw", user_id="me")}
  212. self.assertIn(created["id"], ids)
  213. def test_update_does_not_change_owner(self):
  214. ora_crud.update_account(
  215. self.db,
  216. "mine",
  217. "zkxw",
  218. "other",
  219. {
  220. "account_type": "alipay",
  221. "account_name": "已改户名",
  222. "account_no": "mine",
  223. "bank_name": "",
  224. "bank_province": "",
  225. "bank_city": "",
  226. "bank_branch": "",
  227. "remark": "",
  228. },
  229. )
  230. row = self.db.query(OaReceiveAccount).filter(OaReceiveAccount.id == "mine").one()
  231. self.assertEqual(row.create_by, "me")
  232. self.assertEqual(row.account_name, "已改户名")
  233. def test_ownership_accepts_own_account(self):
  234. errs = validate_receive_account_ownership(
  235. self.db,
  236. _schema("acc"),
  237. {"acc": [_snap("mine")]},
  238. tenant_no="zkxw",
  239. initiator_user_id="me",
  240. )
  241. self.assertEqual(errs, [])
  242. def test_ownership_rejects_in_schema_order(self):
  243. errs = validate_receive_account_ownership(
  244. self.db,
  245. _schema("first", "second"),
  246. {"first": [_snap("mine")], "second": [_snap("theirs"), _snap("blank")]},
  247. tenant_no="zkxw",
  248. initiator_user_id="me",
  249. )
  250. self.assertEqual(errs, ["「second」不属于当前用户"])
  251. def test_ownership_error_messages(self):
  252. cases = [
  253. ("gone", "「acc」不存在或已删除"),
  254. ("other-tenant", "「acc」不存在或已删除"),
  255. ("missing", "「acc」不存在或已删除"),
  256. ("theirs", "「acc」不属于当前用户"),
  257. ("blank", "「acc」不属于当前用户"),
  258. ("none", "「acc」不属于当前用户"),
  259. ]
  260. for account_id, message in cases:
  261. errs = validate_receive_account_ownership(
  262. self.db,
  263. _schema("acc"),
  264. {"acc": [_snap(account_id)]},
  265. tenant_no="zkxw",
  266. initiator_user_id="me",
  267. )
  268. self.assertEqual(errs, [message], account_id)
  269. def _published_app(self):
  270. now = datetime(2026, 10, 21, 10, 0, 0)
  271. schema = {"rule": [{"type": "receiveAccount", "field": "acc", "title": "收款账户"}]}
  272. self.db.add(
  273. OaApp(
  274. id="app1",
  275. app_name="付款",
  276. initiator_scope="all",
  277. admin_scope="all_oa_admin",
  278. status="1",
  279. tenant_no="zkxw",
  280. create_dept_id="",
  281. create_time=now,
  282. update_time=now,
  283. del_flag=0,
  284. )
  285. )
  286. self.db.add(
  287. OaAppVersion(
  288. id="v1",
  289. app_id="app1",
  290. version_no=1,
  291. status="published",
  292. is_latest_published="1",
  293. form_schema_json=json.dumps(schema, ensure_ascii=False),
  294. flow_tree_json="{}",
  295. advanced_settings_json="{}",
  296. tenant_no="zkxw",
  297. create_dept_id="",
  298. create_time=now,
  299. update_time=now,
  300. del_flag=0,
  301. )
  302. )
  303. self.db.commit()
  304. def test_start_instance_rejects_other_owner_before_flow(self):
  305. self._published_app()
  306. with self.assertRaises(OaEngineError) as ctx:
  307. start_instance(
  308. self.db,
  309. tenant_no="zkxw",
  310. app_id="app1",
  311. initiator_user_id="me",
  312. form_data={"acc": [_snap("theirs")]},
  313. )
  314. self.assertEqual(str(ctx.exception), "「收款账户」不属于当前用户")
  315. if __name__ == "__main__":
  316. unittest.main()