test_party_customer_sync.py 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. """当事人三角色客户同步 / 撞库合并"""
  2. import unittest
  3. from unittest.mock import MagicMock, patch
  4. from app.services.party_customer_sync import (
  5. apply_role_profile_overwrite,
  6. merge_customer_fields,
  7. sync_party_customers,
  8. ROLE_MOTHER,
  9. ROLE_FATHER,
  10. ROLE_NEWBORN,
  11. )
  12. class TestMergeCustomerFields(unittest.TestCase):
  13. def test_fill_empty_only_and_union_tags(self):
  14. target = MagicMock(
  15. customer_name="已有",
  16. phone_no="111",
  17. age=None,
  18. sex="2",
  19. remark="",
  20. customer_tags="vip",
  21. )
  22. source = MagicMock(
  23. phone_no="222",
  24. age=30,
  25. sex="1",
  26. remark="备注A",
  27. customer_tags="a,vip",
  28. )
  29. merge_customer_fields(target, source)
  30. self.assertEqual(target.phone_no, "111") # 不覆盖
  31. self.assertEqual(target.age, 30)
  32. self.assertEqual(target.sex, "1") # 目标为未知才补
  33. self.assertEqual(target.remark, "备注A")
  34. tags = set((target.customer_tags or "").split(","))
  35. self.assertEqual(tags, {"vip", "a"})
  36. self.assertEqual(target.customer_name, "已有")
  37. class TestSyncPartyCustomers(unittest.TestCase):
  38. def test_role_constants(self):
  39. self.assertEqual(ROLE_MOTHER, "mother")
  40. self.assertEqual(ROLE_FATHER, "father")
  41. self.assertEqual(ROLE_NEWBORN, "newborn")
  42. def test_no_name_clears_role_id(self):
  43. db = MagicMock()
  44. party = {
  45. "motherName": "",
  46. "customerId": "old-m",
  47. "fatherName": "",
  48. "fatherCustomerId": "old-f",
  49. }
  50. out = sync_party_customers(db, party, tenant_no="zkxw", user_id="u1")
  51. self.assertEqual(out.get("customer_id") or out.get("customerId"), "")
  52. self.assertEqual(out.get("father_customer_id") or out.get("fatherCustomerId"), "")
  53. def test_three_names_without_id_card_creates_three(self):
  54. db = MagicMock()
  55. created = []
  56. def fake_create(db_, **kwargs):
  57. obj = MagicMock()
  58. obj.id = f"c{len(created)+1}"
  59. created.append(kwargs)
  60. return obj
  61. party = {
  62. "motherName": "妈",
  63. "fatherName": "爸",
  64. "newbornName": "宝",
  65. "newbornSex": "0",
  66. }
  67. with patch("app.services.party_customer_sync.customer_crud.create_customer", side_effect=fake_create):
  68. with patch("app.services.party_customer_sync.find_customer_by_id_card", return_value=None):
  69. with patch("app.services.party_customer_sync.get_customer", return_value=None):
  70. out = sync_party_customers(db, party, tenant_no="zkxw", user_id="u1")
  71. self.assertEqual(len(created), 3)
  72. self.assertTrue(out.get("customer_id") or out.get("customerId"))
  73. self.assertTrue(out.get("father_customer_id") or out.get("fatherCustomerId"))
  74. self.assertTrue(out.get("newborn_customer_id") or out.get("newbornCustomerId"))
  75. sexes = {c["customer_name"]: c.get("sex") for c in created}
  76. self.assertEqual(sexes["妈"], "1")
  77. self.assertEqual(sexes["爸"], "0")
  78. self.assertEqual(sexes["宝"], "0")
  79. def test_snake_case_input_clears_and_keeps_other_fields(self):
  80. db = MagicMock()
  81. party = {
  82. "mother_name": "",
  83. "customer_id": "old-m",
  84. "remark": "keep-me",
  85. }
  86. out = sync_party_customers(db, party, tenant_no="zkxw", user_id="u1")
  87. self.assertEqual(out.get("customer_id"), "")
  88. self.assertEqual(out.get("remark"), "keep-me")
  89. def test_name_and_id_card_keeps_linked_id_when_not_found(self):
  90. db = MagicMock()
  91. linked = MagicMock()
  92. linked.id = "cust-a"
  93. linked.customer_name = "旧名"
  94. linked.phone_no = None
  95. linked.id_card_no = None
  96. party = {
  97. "mother_name": "妈",
  98. "mother_phone": "13900000000",
  99. "mother_id_card": "110101199001011234",
  100. "customer_id": "cust-a",
  101. }
  102. with patch("app.services.party_customer_sync.find_customer_by_id_card", return_value=None):
  103. with patch("app.services.party_customer_sync.get_customer", return_value=linked):
  104. with patch("app.services.party_customer_sync.customer_crud.create_customer") as create:
  105. out = sync_party_customers(db, party, tenant_no="zkxw", user_id="u1")
  106. create.assert_not_called()
  107. self.assertEqual(out.get("customer_id"), "cust-a")
  108. self.assertEqual(linked.customer_name, "妈")
  109. self.assertEqual(linked.phone_no, "13900000000")
  110. self.assertEqual(linked.id_card_no, "110101199001011234")
  111. def test_id_card_collision_merges_and_repoints(self):
  112. db = MagicMock()
  113. customer_a = MagicMock(
  114. id="A",
  115. customer_name="临时",
  116. phone_no="111",
  117. age=None,
  118. sex="2",
  119. remark="",
  120. customer_tags="tmp",
  121. )
  122. customer_b = MagicMock(
  123. id="B",
  124. customer_name="已有",
  125. phone_no="",
  126. age=None,
  127. sex="2",
  128. remark="",
  129. customer_tags="vip",
  130. )
  131. party = {
  132. "motherName": "妈",
  133. "motherIdCard": "110101199001011234",
  134. "customerId": "A",
  135. }
  136. def fake_get(db_, customer_id, tenant_no=None):
  137. mapping = {"A": customer_a, "B": customer_b}
  138. return mapping.get(customer_id)
  139. with patch("app.services.party_customer_sync.find_customer_by_id_card", return_value=customer_b):
  140. with patch("app.services.party_customer_sync.get_customer", side_effect=fake_get):
  141. with patch("app.services.party_customer_sync.repoint_customer_refs") as repoint:
  142. with patch(
  143. "app.services.party_customer_sync.customer_crud.soft_delete_customer"
  144. ) as soft_delete:
  145. out = sync_party_customers(db, party, tenant_no="zkxw", user_id="u1")
  146. self.assertEqual(out.get("customer_id") or out.get("customerId"), "B")
  147. repoint.assert_called_once()
  148. args, kwargs = repoint.call_args
  149. if args and len(args) >= 4:
  150. self.assertEqual(args[1], "A")
  151. self.assertEqual(args[2], "B")
  152. self.assertEqual(args[3], "zkxw")
  153. else:
  154. self.assertEqual(kwargs.get("from_id"), "A")
  155. self.assertEqual(kwargs.get("to_id"), "B")
  156. self.assertEqual(kwargs.get("tenant_no"), "zkxw")
  157. soft_delete.assert_called_once()
  158. sd_args, sd_kwargs = soft_delete.call_args
  159. deleted_id = sd_args[1] if len(sd_args) > 1 else sd_kwargs.get("customer_id")
  160. self.assertEqual(deleted_id, "A")
  161. self.assertEqual(customer_b.phone_no, "111")
  162. self.assertEqual(customer_b.customer_name, "妈")
  163. tags = set((customer_b.customer_tags or "").split(","))
  164. self.assertEqual(tags, {"vip", "tmp"})
  165. class TestApplyRoleProfileOverwrite(unittest.TestCase):
  166. def test_nonempty_overwrites_empty_skips(self):
  167. target = MagicMock(
  168. customer_name="旧名",
  169. phone_no="111",
  170. id_card_no="A",
  171. sex="1",
  172. age=20,
  173. birth_ym="199001",
  174. ethnic="汉族",
  175. email="old@x.com",
  176. address="旧址",
  177. )
  178. apply_role_profile_overwrite(
  179. target,
  180. MagicMock(
  181. customer_name="新名",
  182. phone_no="222",
  183. id_card_no="",
  184. sex="1",
  185. age=None,
  186. birth_ym="199102",
  187. ethnic="",
  188. email="new@x.com",
  189. address=None,
  190. ),
  191. )
  192. self.assertEqual(target.customer_name, "新名")
  193. self.assertEqual(target.phone_no, "222")
  194. self.assertEqual(target.id_card_no, "A")
  195. self.assertEqual(target.birth_ym, "199102")
  196. self.assertEqual(target.ethnic, "汉族")
  197. self.assertEqual(target.email, "new@x.com")
  198. self.assertEqual(target.address, "旧址")
  199. def test_mother_sync_overwrites_extended_fields_on_linked(self):
  200. db = MagicMock()
  201. linked = MagicMock(
  202. id="cust-a",
  203. customer_name="旧名",
  204. phone_no="111",
  205. id_card_no="110101199001011234",
  206. sex="1",
  207. age=30,
  208. birth_ym="199001",
  209. ethnic="回族",
  210. email="old@x.com",
  211. address="旧址",
  212. )
  213. party = {
  214. "mother_name": "妈",
  215. "mother_phone": "13900000000",
  216. "mother_id_card": "110101199001011234",
  217. "mother_birth_ym": "199002",
  218. "mother_ethnic": "汉族",
  219. "mother_email": "new@x.com",
  220. "mother_address": "新址",
  221. "customer_id": "cust-a",
  222. }
  223. with patch("app.services.party_customer_sync.find_customer_by_id_card", return_value=None):
  224. with patch("app.services.party_customer_sync.get_customer", return_value=linked):
  225. with patch("app.services.party_customer_sync.customer_crud.create_customer") as create:
  226. out = sync_party_customers(db, party, tenant_no="zkxw", user_id="u1")
  227. create.assert_not_called()
  228. self.assertEqual(out.get("customer_id"), "cust-a")
  229. self.assertEqual(linked.customer_name, "妈")
  230. self.assertEqual(linked.phone_no, "13900000000")
  231. self.assertEqual(linked.birth_ym, "199002")
  232. self.assertEqual(linked.ethnic, "汉族")
  233. self.assertEqual(linked.email, "new@x.com")
  234. self.assertEqual(linked.address, "新址")