You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

366 lines
12 KiB

  1. # -*- coding: utf-8 -*-
  2. # Copyright 2019 ACSONE SA/NV
  3. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
  4. import mock
  5. from contextlib import contextmanager
  6. import odoo.tests.common as common
  7. class TestOnchangeHelper(common.TransactionCase):
  8. def setUp(self):
  9. super(TestOnchangeHelper, self).setUp()
  10. self.Category = self.env["test_onchange_helper.category"]
  11. self.Message = self.env["test_onchange_helper.message"]
  12. self.Discussion = self.env["test_onchange_helper.discussion"]
  13. self.maxDiff = None
  14. @contextmanager
  15. def assertNoOrmWrite(self, model):
  16. with mock.patch.object(
  17. model.__class__, "create"
  18. ) as mocked_create, mock.patch.object(
  19. model.__class__, "write"
  20. ) as mocked_write:
  21. yield
  22. mocked_create.assert_not_called()
  23. mocked_write.assert_not_called()
  24. def test_play_onhanges_no_recompute(self):
  25. # play_onchanges must not trigger recomputes except if an onchange
  26. # method access a computed field.
  27. # changing 'discussion' should recompute 'name'
  28. values = {"name": "Cat Name"}
  29. self.env.invalidate_all()
  30. with self.assertNoOrmWrite(self.Category):
  31. result = self.Category.play_onchanges(values, ["name"])
  32. self.assertNotIn("computed_display_name", result)
  33. def test_play_onchanges_many2one_new_record(self):
  34. root = self.Category.create({"name": "root"})
  35. values = {"name": "test", "parent": root.id}
  36. self.env.invalidate_all()
  37. with self.assertNoOrmWrite(self.Category):
  38. result = self.Category.play_onchanges(values, "parent")
  39. self.assertIn("root_categ", result)
  40. self.assertEqual(result["root_categ"], root.id)
  41. def test_play_onchanges_many2one_existing_record(self):
  42. root = self.Category.create({"name": "root"})
  43. values = {"name": "test", "parent": root.id}
  44. self.env.invalidate_all()
  45. with self.assertNoOrmWrite(self.Category):
  46. result = self.Category.play_onchanges(values, "parent")
  47. self.assertIn("root_categ", result)
  48. self.assertEqual(result["root_categ"], root.id)
  49. # create child catefory
  50. values.update(result)
  51. child = self.Category.create(values)
  52. self.assertEqual(root.id, child.root_categ.id)
  53. # since the parent is set to False and the root_categ
  54. values = {"parent": False}
  55. self.env.invalidate_all()
  56. with self.assertNoOrmWrite(child):
  57. result = child.play_onchanges(values, "parent")
  58. self.assertIn("root_categ", result)
  59. self.assertEqual(result["root_categ"], False)
  60. def test_play_onchange_one2many_new_record(self):
  61. """ test the effect of play_onchanges() on one2many fields on new
  62. record"""
  63. BODY = "What a beautiful day!"
  64. USER = self.env.user
  65. # create an independent message
  66. message = self.Message.create({"body": BODY})
  67. # modify discussion name
  68. values = {
  69. "name": "Foo",
  70. "categories": [],
  71. "moderator": False,
  72. "participants": [],
  73. "messages": [
  74. (4, message.id),
  75. (
  76. 0,
  77. 0,
  78. {
  79. "name": "[%s] %s" % ("", USER.name),
  80. "body": BODY,
  81. "author": USER.id,
  82. "important": False,
  83. },
  84. ),
  85. ],
  86. }
  87. self.env.invalidate_all()
  88. with self.assertNoOrmWrite(self.Discussion):
  89. result = self.Discussion.play_onchanges(values, "name")
  90. self.assertIn("messages", result)
  91. self.assertItemsEqual(
  92. result["messages"],
  93. [
  94. (5,),
  95. (
  96. 1,
  97. message.id,
  98. {
  99. "name": "[%s] %s" % ("Foo", USER.name),
  100. "body": "not last dummy message",
  101. "author": message.author.id,
  102. "important": message.important,
  103. },
  104. ),
  105. (
  106. 0,
  107. 0,
  108. {
  109. "name": "[%s] %s" % ("Foo", USER.name),
  110. "body": "not last dummy message",
  111. "author": USER.id,
  112. "important": False,
  113. },
  114. ),
  115. (
  116. 0,
  117. 0,
  118. {
  119. "name": "[%s] %s" % ("Foo", USER.name),
  120. "body": "dummy message",
  121. "author": USER.id,
  122. "important": True,
  123. },
  124. ),
  125. ],
  126. )
  127. self.assertIn("important_messages", result)
  128. self.assertItemsEqual(
  129. result["important_messages"],
  130. [
  131. (5,),
  132. (
  133. 0,
  134. 0,
  135. {
  136. "author": USER.id,
  137. "body": "dummy message",
  138. "important": True,
  139. },
  140. ),
  141. ],
  142. )
  143. def test_play_onchange_one2many_existing_record(self):
  144. """ test the effect of play_onchanges() on one2many fields on existing
  145. record"""
  146. BODY = "What a beautiful day!"
  147. USER = self.env.user
  148. # create an independent message
  149. message = self.Message.create({"body": BODY})
  150. # modify discussion name
  151. values = {
  152. "name": "Foo",
  153. "categories": [],
  154. "moderator": False,
  155. "participants": [],
  156. "messages": [
  157. (4, message.id),
  158. (
  159. 0,
  160. 0,
  161. {
  162. "name": "[%s] %s" % ("", USER.name),
  163. "body": BODY,
  164. "author": USER.id,
  165. "important": False,
  166. },
  167. ),
  168. ],
  169. }
  170. discussion = self.Discussion.create(values)
  171. values = {"name": "New foo"}
  172. with self.assertNoOrmWrite(discussion):
  173. result = discussion.play_onchanges(values, "name")
  174. self.assertIn("messages", result)
  175. self.assertItemsEqual(
  176. result["messages"],
  177. [
  178. (5,),
  179. (
  180. 1,
  181. discussion.messages[0].id,
  182. {
  183. "name": "[%s] %s" % ("New foo", USER.name),
  184. "body": "not last dummy message",
  185. "author": message.author.id,
  186. "important": message.important,
  187. "discussion": discussion.id,
  188. },
  189. ),
  190. (
  191. 1,
  192. discussion.messages[1].id,
  193. {
  194. "name": "[%s] %s" % ("New foo", USER.name),
  195. "body": "not last dummy message",
  196. "author": USER.id,
  197. "important": False,
  198. "discussion": discussion.id,
  199. },
  200. ),
  201. (
  202. 0,
  203. 0,
  204. {
  205. "name": "[%s] %s" % ("New foo", USER.name),
  206. "body": "dummy message",
  207. "author": USER.id,
  208. "important": True,
  209. "discussion": discussion.id,
  210. },
  211. ),
  212. ],
  213. )
  214. self.assertIn("important_messages", result)
  215. self.assertItemsEqual(
  216. result["important_messages"],
  217. [
  218. (5,),
  219. (
  220. 0,
  221. 0,
  222. {
  223. "author": USER.id,
  224. "body": "dummy message",
  225. "important": True,
  226. "discussion": discussion.id,
  227. },
  228. ),
  229. ],
  230. )
  231. def test_onchange_specific(self):
  232. """test that only the id is added if a new item is added to an
  233. existing relation"""
  234. discussion = self.env.ref("test_onchange_helper.discussion_demo_0")
  235. demo = self.env.ref("base.user_demo")
  236. # first remove demo user from participants
  237. discussion.participants -= demo
  238. self.assertNotIn(demo, discussion.participants)
  239. # check that demo_user is added to participants when set as moderator
  240. values = {
  241. "name": discussion.name,
  242. "moderator": demo.id,
  243. }
  244. self.env.invalidate_all()
  245. with self.assertNoOrmWrite(discussion):
  246. result = discussion.play_onchanges(values, "moderator")
  247. self.assertIn("participants", result)
  248. self.assertTrue(discussion.participants)
  249. self.assertItemsEqual(
  250. result["participants"],
  251. [(5,)] + [(4, user.id) for user in discussion.participants + demo],
  252. )
  253. def test_onchange_one2many_value(self):
  254. """ test that the values provided for a one2many field inside are used
  255. by the play_onchanges """
  256. discussion = self.env.ref("test_onchange_helper.discussion_demo_0")
  257. demo = self.env.ref("base.user_demo")
  258. self.assertEqual(len(discussion.messages), 3)
  259. messages = [(4, msg.id) for msg in discussion.messages]
  260. messages[0] = (1, messages[0][1], {"body": "test onchange"})
  261. values = {
  262. "name": discussion.name,
  263. "moderator": demo.id,
  264. "categories": [(4, cat.id) for cat in discussion.categories],
  265. "messages": messages,
  266. "participants": [(4, usr.id) for usr in discussion.participants],
  267. }
  268. with self.assertNoOrmWrite(discussion):
  269. result = discussion.play_onchanges(values, "messages")
  270. self.assertIn("message_concat", result)
  271. self.assertEqual(
  272. result["message_concat"],
  273. "\n".join(
  274. ["%s:%s" % (m.name, m.body) for m in discussion.messages]
  275. ),
  276. )
  277. def test_onchange_one2many_line(self):
  278. """ test that changes on a field used as first position into the
  279. related path of a related field will trigger the onchange also on the
  280. related field """
  281. partner = self.env.ref("base.res_partner_1")
  282. multi = self.env["test_onchange_helper.multi"].create(
  283. {"partner": partner.id}
  284. )
  285. line = multi.lines.create({"multi": multi.id})
  286. values = multi._convert_to_write(
  287. {key: multi[key] for key in ("partner", "lines")}
  288. )
  289. self.assertEqual(
  290. values, {"partner": partner.id, "lines": [(6, 0, [line.id])]}
  291. )
  292. # modify 'partner'
  293. # -> set 'partner' on all lines
  294. # -> recompute 'name' (related on partner)
  295. # -> set 'name' on all lines
  296. partner = self.env.ref("base.res_partner_2")
  297. values = {
  298. "partner": partner.id,
  299. "lines": [
  300. (6, 0, [line.id]),
  301. (0, 0, {"name": False, "partner": False}),
  302. ],
  303. }
  304. self.env.invalidate_all()
  305. with self.assertNoOrmWrite(multi):
  306. result = multi.play_onchanges(values, "partner")
  307. self.assertEqual(
  308. result,
  309. {
  310. "name": partner.name,
  311. "lines": [
  312. (5,),
  313. (
  314. 1,
  315. line.id,
  316. {
  317. "name": partner.name,
  318. "partner": partner.id,
  319. "multi": multi.id,
  320. },
  321. ),
  322. (
  323. 0,
  324. 0,
  325. {
  326. "name": partner.name,
  327. "partner": partner.id,
  328. "multi": multi.id,
  329. },
  330. ),
  331. ],
  332. },
  333. )