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.

457 lines
17 KiB

  1. # Copyright 2016-2017 Therp BV
  2. # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
  3. from datetime import date, datetime, timedelta
  4. from dateutil.relativedelta import relativedelta
  5. from odoo import fields
  6. from odoo.exceptions import ValidationError
  7. from .test_partner_relation_common import TestPartnerRelationCommon
  8. class TestPartnerRelation(TestPartnerRelationCommon):
  9. post_install = True
  10. def test_selection_name_search(self):
  11. """Test wether we can find type selection on reverse name."""
  12. selection_types = self.selection_model.name_search(
  13. name=self.selection_person2company.name
  14. )
  15. self.assertTrue(selection_types)
  16. self.assertTrue(
  17. (self.selection_person2company.id, self.selection_person2company.name)
  18. in selection_types
  19. )
  20. def test_self_allowed(self):
  21. """Test creation of relation to same partner when type allows."""
  22. type_allow = self.type_model.create(
  23. {
  24. "name": "allow",
  25. "name_inverse": "allow_inverse",
  26. "contact_type_left": "p",
  27. "contact_type_right": "p",
  28. "allow_self": True,
  29. }
  30. )
  31. self.assertTrue(type_allow)
  32. reflexive_relation = self.relation_model.create(
  33. {
  34. "type_id": type_allow.id,
  35. "left_partner_id": self.partner_01_person.id,
  36. "right_partner_id": self.partner_01_person.id,
  37. }
  38. )
  39. self.assertTrue(reflexive_relation)
  40. def test_self_disallowed(self):
  41. """Test creating relation to same partner when disallowed.
  42. Attempt to create a relation of a partner to the same partner should
  43. raise an error when the type of relation explicitly disallows this.
  44. """
  45. type_disallow = self.type_model.create(
  46. {
  47. "name": "disallow",
  48. "name_inverse": "disallow_inverse",
  49. "contact_type_left": "p",
  50. "contact_type_right": "p",
  51. "allow_self": False,
  52. }
  53. )
  54. self.assertTrue(type_disallow)
  55. with self.assertRaises(ValidationError):
  56. self.relation_model.create(
  57. {
  58. "type_id": type_disallow.id,
  59. "left_partner_id": self.partner_01_person.id,
  60. "right_partner_id": self.partner_01_person.id,
  61. }
  62. )
  63. def test_self_disallowed_after_self_relation_created(self):
  64. """Test that allow_self can not be true if a reflexive relation already exists.
  65. If at least one reflexive relation exists for the given type,
  66. reflexivity can not be disallowed.
  67. """
  68. type_allow = self.type_model.create(
  69. {
  70. "name": "allow",
  71. "name_inverse": "allow_inverse",
  72. "contact_type_left": "p",
  73. "contact_type_right": "p",
  74. "allow_self": True,
  75. }
  76. )
  77. self.assertTrue(type_allow)
  78. reflexive_relation = self.relation_model.create(
  79. {
  80. "type_id": type_allow.id,
  81. "left_partner_id": self.partner_01_person.id,
  82. "right_partner_id": self.partner_01_person.id,
  83. }
  84. )
  85. self.assertTrue(reflexive_relation)
  86. with self.assertRaises(ValidationError):
  87. type_allow.allow_self = False
  88. def test_self_disallowed_with_delete_invalid_relations(self):
  89. """Test handle_invalid_onchange delete with allow_self disabled.
  90. When deactivating allow_self, if handle_invalid_onchange is set
  91. to delete, then existing reflexive relations are deleted.
  92. Non reflexive relations are not modified.
  93. """
  94. type_allow = self.type_model.create(
  95. {
  96. "name": "allow",
  97. "name_inverse": "allow_inverse",
  98. "contact_type_left": "p",
  99. "contact_type_right": "p",
  100. "allow_self": True,
  101. "handle_invalid_onchange": "delete",
  102. }
  103. )
  104. reflexive_relation = self.relation_model.create(
  105. {
  106. "type_id": type_allow.id,
  107. "left_partner_id": self.partner_01_person.id,
  108. "right_partner_id": self.partner_01_person.id,
  109. }
  110. )
  111. normal_relation = self.relation_model.create(
  112. {
  113. "type_id": type_allow.id,
  114. "left_partner_id": self.partner_01_person.id,
  115. "right_partner_id": self.partner_04_volunteer.id,
  116. }
  117. )
  118. type_allow.allow_self = False
  119. self.assertFalse(reflexive_relation.exists())
  120. self.assertTrue(normal_relation.exists())
  121. def test_self_disallowed_with_end_invalid_relations(self):
  122. """Test handle_invalid_onchange delete with allow_self disabled.
  123. When deactivating allow_self, if handle_invalid_onchange is set
  124. to end, then active reflexive relations are ended.
  125. Non reflexive relations are not modified.
  126. Reflexive relations with an end date prior to the current date
  127. are not modified.
  128. """
  129. type_allow = self.type_model.create(
  130. {
  131. "name": "allow",
  132. "name_inverse": "allow_inverse",
  133. "contact_type_left": "p",
  134. "contact_type_right": "p",
  135. "allow_self": True,
  136. "handle_invalid_onchange": "end",
  137. }
  138. )
  139. reflexive_relation = self.relation_model.create(
  140. {
  141. "type_id": type_allow.id,
  142. "left_partner_id": self.partner_01_person.id,
  143. "right_partner_id": self.partner_01_person.id,
  144. "date_start": "2000-01-02",
  145. }
  146. )
  147. past_reflexive_relation = self.relation_model.create(
  148. {
  149. "type_id": type_allow.id,
  150. "left_partner_id": self.partner_01_person.id,
  151. "right_partner_id": self.partner_01_person.id,
  152. "date_end": "2000-01-01",
  153. }
  154. )
  155. normal_relation = self.relation_model.create(
  156. {
  157. "type_id": type_allow.id,
  158. "left_partner_id": self.partner_01_person.id,
  159. "right_partner_id": self.partner_04_volunteer.id,
  160. }
  161. )
  162. type_allow.allow_self = False
  163. self.assertEqual(reflexive_relation.date_end, fields.Date.today())
  164. self.assertEqual(past_reflexive_relation.date_end, date(2000, 1, 1))
  165. self.assertFalse(normal_relation.date_end)
  166. def test_self_disallowed_with_future_reflexive_relation(self):
  167. """Test future reflexive relations are deleted.
  168. If handle_invalid_onchange is set to end, then deactivating
  169. reflexivity will delete invalid relations in the future.
  170. """
  171. type_allow = self.type_model.create(
  172. {
  173. "name": "allow",
  174. "name_inverse": "allow_inverse",
  175. "contact_type_left": "p",
  176. "contact_type_right": "p",
  177. "allow_self": True,
  178. "handle_invalid_onchange": "end",
  179. }
  180. )
  181. future_reflexive_relation = self.relation_model.create(
  182. {
  183. "type_id": type_allow.id,
  184. "left_partner_id": self.partner_01_person.id,
  185. "right_partner_id": self.partner_01_person.id,
  186. "date_start": datetime.now() + timedelta(1),
  187. }
  188. )
  189. type_allow.allow_self = False
  190. self.assertFalse(future_reflexive_relation.exists())
  191. def test_self_default(self):
  192. """Test default not to allow relation with same partner.
  193. Attempt to create a relation of a partner to the same partner
  194. raise an error when the type of relation does not explicitly allow
  195. this.
  196. """
  197. type_default = self.type_model.create(
  198. {
  199. "name": "default",
  200. "name_inverse": "default_inverse",
  201. "contact_type_left": "p",
  202. "contact_type_right": "p",
  203. }
  204. )
  205. self.assertTrue(type_default)
  206. with self.assertRaises(ValidationError):
  207. self.relation_model.create(
  208. {
  209. "type_id": type_default.id,
  210. "left_partner_id": self.partner_01_person.id,
  211. "right_partner_id": self.partner_01_person.id,
  212. }
  213. )
  214. def test_self_mixed(self):
  215. """Test creation of relation with wrong types.
  216. Trying to create a relation between partners with an inappropiate
  217. type should raise an error.
  218. """
  219. with self.assertRaises(ValidationError):
  220. self.relation_model.create(
  221. {
  222. "type_id": self.type_company2person.id,
  223. "left_partner_id": self.partner_01_person.id,
  224. "right_partner_id": self.partner_02_company.id,
  225. }
  226. )
  227. def test_symmetric(self):
  228. """Test creating symmetric relation."""
  229. # Start out with non symmetric relation:
  230. type_symmetric = self.type_model.create(
  231. {
  232. "name": "not yet symmetric",
  233. "name_inverse": "the other side of not symmetric",
  234. "is_symmetric": False,
  235. "contact_type_left": False,
  236. "contact_type_right": "p",
  237. }
  238. )
  239. # not yet symmetric relation should result in two records in
  240. # selection:
  241. selection_symmetric = self.selection_model.search(
  242. [("type_id", "=", type_symmetric.id)]
  243. )
  244. self.assertEqual(len(selection_symmetric), 2)
  245. # Now change to symmetric and test name and inverse name:
  246. type_symmetric.write({"name": "sym", "is_symmetric": True})
  247. self.assertEqual(type_symmetric.is_symmetric, True)
  248. self.assertEqual(type_symmetric.name_inverse, type_symmetric.name)
  249. self.assertEqual(
  250. type_symmetric.contact_type_right, type_symmetric.contact_type_left
  251. )
  252. # now update the database:
  253. type_symmetric.write(
  254. {
  255. "name": type_symmetric.name,
  256. "is_symmetric": type_symmetric.is_symmetric,
  257. "name_inverse": type_symmetric.name_inverse,
  258. "contact_type_right": type_symmetric.contact_type_right,
  259. }
  260. )
  261. type_symmetric.flush()
  262. # symmetric relation should result in only one record in
  263. # selection:
  264. selection_symmetric = self.selection_model.search(
  265. [("type_id", "=", type_symmetric.id)]
  266. )
  267. self.assertEqual(len(selection_symmetric), 1)
  268. relation = self.relation_all_model.create(
  269. {
  270. "type_selection_id": selection_symmetric.id,
  271. "this_partner_id": self.partner_02_company.id,
  272. "other_partner_id": self.partner_01_person.id,
  273. }
  274. )
  275. partners = self.partner_model.search(
  276. [("search_relation_type_id", "=", relation.type_selection_id.id)]
  277. )
  278. self.assertTrue(self.partner_01_person in partners)
  279. self.assertTrue(self.partner_02_company in partners)
  280. def test_category_domain(self):
  281. """Test check on category in relations."""
  282. # Check on left side:
  283. with self.assertRaises(ValidationError):
  284. self.relation_model.create(
  285. {
  286. "type_id": self.type_ngo2volunteer.id,
  287. "left_partner_id": self.partner_02_company.id,
  288. "right_partner_id": self.partner_04_volunteer.id,
  289. }
  290. )
  291. # Check on right side:
  292. with self.assertRaises(ValidationError):
  293. self.relation_model.create(
  294. {
  295. "type_id": self.type_ngo2volunteer.id,
  296. "left_partner_id": self.partner_03_ngo.id,
  297. "right_partner_id": self.partner_01_person.id,
  298. }
  299. )
  300. def test_relation_type_change(self):
  301. """Test change in relation type conditions."""
  302. # First create a relation type having no particular conditions.
  303. (
  304. type_school2student,
  305. school2student,
  306. school2student_inverse,
  307. ) = self._create_relation_type_selection(
  308. {"name": "school has student", "name_inverse": "studies at school"}
  309. )
  310. # Second create relations based on those conditions.
  311. partner_school = self.partner_model.create(
  312. {"name": "Test School", "is_company": True, "ref": "TS"}
  313. )
  314. partner_bart = self.partner_model.create(
  315. {"name": "Bart Simpson", "is_company": False, "ref": "BS"}
  316. )
  317. partner_lisa = self.partner_model.create(
  318. {"name": "Lisa Simpson", "is_company": False, "ref": "LS"}
  319. )
  320. relation_school2bart = self.relation_all_model.create(
  321. {
  322. "this_partner_id": partner_school.id,
  323. "type_selection_id": school2student.id,
  324. "other_partner_id": partner_bart.id,
  325. }
  326. )
  327. self.assertTrue(relation_school2bart)
  328. relation_school2lisa = self.relation_all_model.create(
  329. {
  330. "this_partner_id": partner_school.id,
  331. "type_selection_id": school2student.id,
  332. "other_partner_id": partner_lisa.id,
  333. }
  334. )
  335. self.assertTrue(relation_school2lisa)
  336. relation_bart2lisa = self.relation_all_model.create(
  337. {
  338. "this_partner_id": partner_bart.id,
  339. "type_selection_id": school2student.id,
  340. "other_partner_id": partner_lisa.id,
  341. }
  342. )
  343. self.assertTrue(relation_bart2lisa)
  344. # Third creata a category and make it a condition for the
  345. # relation type.
  346. # - Test restriction
  347. # - Test ignore
  348. category_student = self.category_model.create({"name": "Student"})
  349. with self.assertRaises(ValidationError):
  350. type_school2student.write({"partner_category_right": category_student.id})
  351. self.assertFalse(type_school2student.partner_category_right.id)
  352. type_school2student.write(
  353. {
  354. "handle_invalid_onchange": "ignore",
  355. "partner_category_right": category_student.id,
  356. }
  357. )
  358. self.assertEqual(
  359. type_school2student.partner_category_right.id, category_student.id
  360. )
  361. # Fourth make company type a condition for left partner
  362. # - Test ending
  363. # - Test deletion
  364. partner_bart.write({"category_id": [(4, category_student.id)]})
  365. partner_lisa.write({"category_id": [(4, category_student.id)]})
  366. # Future student to be deleted by end action:
  367. partner_homer = self.partner_model.create(
  368. {
  369. "name": "Homer Simpson",
  370. "is_company": False,
  371. "ref": "HS",
  372. "category_id": [(4, category_student.id)],
  373. }
  374. )
  375. relation_lisa2homer = self.relation_all_model.create(
  376. {
  377. "this_partner_id": partner_lisa.id,
  378. "type_selection_id": school2student.id,
  379. "other_partner_id": partner_homer.id,
  380. "date_start": date.today() + relativedelta(months=+6),
  381. }
  382. )
  383. self.assertTrue(relation_lisa2homer)
  384. type_school2student.write(
  385. {"handle_invalid_onchange": "end", "contact_type_left": "c"}
  386. )
  387. self.assertEqual(relation_bart2lisa.date_end, fields.Date.today())
  388. self.assertFalse(relation_lisa2homer.exists())
  389. type_school2student.write(
  390. {
  391. "handle_invalid_onchange": "delete",
  392. "contact_type_left": "c",
  393. "contact_type_right": "p",
  394. }
  395. )
  396. self.assertFalse(relation_bart2lisa.exists())
  397. def test_relation_type_unlink(self):
  398. """Test delete of relation type, including deleting relations."""
  399. # First create a relation type having restrict particular conditions.
  400. type_model = self.env["res.partner.relation.type"]
  401. relation_model = self.env["res.partner.relation"]
  402. partner_model = self.env["res.partner"]
  403. type_school2student = type_model.create(
  404. {
  405. "name": "school has student",
  406. "name_inverse": "studies at school",
  407. "handle_invalid_onchange": "delete",
  408. }
  409. )
  410. # Second create relation based on those conditions.
  411. partner_school = partner_model.create(
  412. {"name": "Test School", "is_company": True, "ref": "TS"}
  413. )
  414. partner_bart = partner_model.create(
  415. {"name": "Bart Simpson", "is_company": False, "ref": "BS"}
  416. )
  417. relation_school2bart = relation_model.create(
  418. {
  419. "left_partner_id": partner_school.id,
  420. "type_id": type_school2student.id,
  421. "right_partner_id": partner_bart.id,
  422. }
  423. )
  424. # Delete type. Relations with type should also cease to exist:
  425. type_school2student.unlink()
  426. self.assertFalse(relation_school2bart.exists())