mirror of
https://gitlab.com/flectra-community/partner-contact.git
synced 2024-12-23 21:01:48 +00:00
458 lines
17 KiB
Python
458 lines
17 KiB
Python
|
# Copyright 2016-2017 Therp BV
|
||
|
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
|
||
|
from datetime import date, datetime, timedelta
|
||
|
|
||
|
from dateutil.relativedelta import relativedelta
|
||
|
|
||
|
from flectra import fields
|
||
|
from flectra.exceptions import ValidationError
|
||
|
|
||
|
from .test_partner_relation_common import TestPartnerRelationCommon
|
||
|
|
||
|
|
||
|
class TestPartnerRelation(TestPartnerRelationCommon):
|
||
|
|
||
|
post_install = True
|
||
|
|
||
|
def test_selection_name_search(self):
|
||
|
"""Test wether we can find type selection on reverse name."""
|
||
|
selection_types = self.selection_model.name_search(
|
||
|
name=self.selection_person2company.name
|
||
|
)
|
||
|
self.assertTrue(selection_types)
|
||
|
self.assertTrue(
|
||
|
(self.selection_person2company.id, self.selection_person2company.name)
|
||
|
in selection_types
|
||
|
)
|
||
|
|
||
|
def test_self_allowed(self):
|
||
|
"""Test creation of relation to same partner when type allows."""
|
||
|
type_allow = self.type_model.create(
|
||
|
{
|
||
|
"name": "allow",
|
||
|
"name_inverse": "allow_inverse",
|
||
|
"contact_type_left": "p",
|
||
|
"contact_type_right": "p",
|
||
|
"allow_self": True,
|
||
|
}
|
||
|
)
|
||
|
self.assertTrue(type_allow)
|
||
|
reflexive_relation = self.relation_model.create(
|
||
|
{
|
||
|
"type_id": type_allow.id,
|
||
|
"left_partner_id": self.partner_01_person.id,
|
||
|
"right_partner_id": self.partner_01_person.id,
|
||
|
}
|
||
|
)
|
||
|
self.assertTrue(reflexive_relation)
|
||
|
|
||
|
def test_self_disallowed(self):
|
||
|
"""Test creating relation to same partner when disallowed.
|
||
|
|
||
|
Attempt to create a relation of a partner to the same partner should
|
||
|
raise an error when the type of relation explicitly disallows this.
|
||
|
"""
|
||
|
type_disallow = self.type_model.create(
|
||
|
{
|
||
|
"name": "disallow",
|
||
|
"name_inverse": "disallow_inverse",
|
||
|
"contact_type_left": "p",
|
||
|
"contact_type_right": "p",
|
||
|
"allow_self": False,
|
||
|
}
|
||
|
)
|
||
|
self.assertTrue(type_disallow)
|
||
|
with self.assertRaises(ValidationError):
|
||
|
self.relation_model.create(
|
||
|
{
|
||
|
"type_id": type_disallow.id,
|
||
|
"left_partner_id": self.partner_01_person.id,
|
||
|
"right_partner_id": self.partner_01_person.id,
|
||
|
}
|
||
|
)
|
||
|
|
||
|
def test_self_disallowed_after_self_relation_created(self):
|
||
|
"""Test that allow_self can not be true if a reflexive relation already exists.
|
||
|
|
||
|
If at least one reflexive relation exists for the given type,
|
||
|
reflexivity can not be disallowed.
|
||
|
"""
|
||
|
type_allow = self.type_model.create(
|
||
|
{
|
||
|
"name": "allow",
|
||
|
"name_inverse": "allow_inverse",
|
||
|
"contact_type_left": "p",
|
||
|
"contact_type_right": "p",
|
||
|
"allow_self": True,
|
||
|
}
|
||
|
)
|
||
|
self.assertTrue(type_allow)
|
||
|
reflexive_relation = self.relation_model.create(
|
||
|
{
|
||
|
"type_id": type_allow.id,
|
||
|
"left_partner_id": self.partner_01_person.id,
|
||
|
"right_partner_id": self.partner_01_person.id,
|
||
|
}
|
||
|
)
|
||
|
self.assertTrue(reflexive_relation)
|
||
|
with self.assertRaises(ValidationError):
|
||
|
type_allow.allow_self = False
|
||
|
|
||
|
def test_self_disallowed_with_delete_invalid_relations(self):
|
||
|
"""Test handle_invalid_onchange delete with allow_self disabled.
|
||
|
|
||
|
When deactivating allow_self, if handle_invalid_onchange is set
|
||
|
to delete, then existing reflexive relations are deleted.
|
||
|
|
||
|
Non reflexive relations are not modified.
|
||
|
"""
|
||
|
type_allow = self.type_model.create(
|
||
|
{
|
||
|
"name": "allow",
|
||
|
"name_inverse": "allow_inverse",
|
||
|
"contact_type_left": "p",
|
||
|
"contact_type_right": "p",
|
||
|
"allow_self": True,
|
||
|
"handle_invalid_onchange": "delete",
|
||
|
}
|
||
|
)
|
||
|
reflexive_relation = self.relation_model.create(
|
||
|
{
|
||
|
"type_id": type_allow.id,
|
||
|
"left_partner_id": self.partner_01_person.id,
|
||
|
"right_partner_id": self.partner_01_person.id,
|
||
|
}
|
||
|
)
|
||
|
normal_relation = self.relation_model.create(
|
||
|
{
|
||
|
"type_id": type_allow.id,
|
||
|
"left_partner_id": self.partner_01_person.id,
|
||
|
"right_partner_id": self.partner_04_volunteer.id,
|
||
|
}
|
||
|
)
|
||
|
|
||
|
type_allow.allow_self = False
|
||
|
self.assertFalse(reflexive_relation.exists())
|
||
|
self.assertTrue(normal_relation.exists())
|
||
|
|
||
|
def test_self_disallowed_with_end_invalid_relations(self):
|
||
|
"""Test handle_invalid_onchange delete with allow_self disabled.
|
||
|
|
||
|
When deactivating allow_self, if handle_invalid_onchange is set
|
||
|
to end, then active reflexive relations are ended.
|
||
|
|
||
|
Non reflexive relations are not modified.
|
||
|
|
||
|
Reflexive relations with an end date prior to the current date
|
||
|
are not modified.
|
||
|
"""
|
||
|
type_allow = self.type_model.create(
|
||
|
{
|
||
|
"name": "allow",
|
||
|
"name_inverse": "allow_inverse",
|
||
|
"contact_type_left": "p",
|
||
|
"contact_type_right": "p",
|
||
|
"allow_self": True,
|
||
|
"handle_invalid_onchange": "end",
|
||
|
}
|
||
|
)
|
||
|
reflexive_relation = self.relation_model.create(
|
||
|
{
|
||
|
"type_id": type_allow.id,
|
||
|
"left_partner_id": self.partner_01_person.id,
|
||
|
"right_partner_id": self.partner_01_person.id,
|
||
|
"date_start": "2000-01-02",
|
||
|
}
|
||
|
)
|
||
|
past_reflexive_relation = self.relation_model.create(
|
||
|
{
|
||
|
"type_id": type_allow.id,
|
||
|
"left_partner_id": self.partner_01_person.id,
|
||
|
"right_partner_id": self.partner_01_person.id,
|
||
|
"date_end": "2000-01-01",
|
||
|
}
|
||
|
)
|
||
|
normal_relation = self.relation_model.create(
|
||
|
{
|
||
|
"type_id": type_allow.id,
|
||
|
"left_partner_id": self.partner_01_person.id,
|
||
|
"right_partner_id": self.partner_04_volunteer.id,
|
||
|
}
|
||
|
)
|
||
|
|
||
|
type_allow.allow_self = False
|
||
|
self.assertEqual(reflexive_relation.date_end, fields.Date.today())
|
||
|
self.assertEqual(past_reflexive_relation.date_end, date(2000, 1, 1))
|
||
|
self.assertFalse(normal_relation.date_end)
|
||
|
|
||
|
def test_self_disallowed_with_future_reflexive_relation(self):
|
||
|
"""Test future reflexive relations are deleted.
|
||
|
|
||
|
If handle_invalid_onchange is set to end, then deactivating
|
||
|
reflexivity will delete invalid relations in the future.
|
||
|
"""
|
||
|
type_allow = self.type_model.create(
|
||
|
{
|
||
|
"name": "allow",
|
||
|
"name_inverse": "allow_inverse",
|
||
|
"contact_type_left": "p",
|
||
|
"contact_type_right": "p",
|
||
|
"allow_self": True,
|
||
|
"handle_invalid_onchange": "end",
|
||
|
}
|
||
|
)
|
||
|
future_reflexive_relation = self.relation_model.create(
|
||
|
{
|
||
|
"type_id": type_allow.id,
|
||
|
"left_partner_id": self.partner_01_person.id,
|
||
|
"right_partner_id": self.partner_01_person.id,
|
||
|
"date_start": datetime.now() + timedelta(1),
|
||
|
}
|
||
|
)
|
||
|
type_allow.allow_self = False
|
||
|
self.assertFalse(future_reflexive_relation.exists())
|
||
|
|
||
|
def test_self_default(self):
|
||
|
"""Test default not to allow relation with same partner.
|
||
|
|
||
|
Attempt to create a relation of a partner to the same partner
|
||
|
raise an error when the type of relation does not explicitly allow
|
||
|
this.
|
||
|
"""
|
||
|
type_default = self.type_model.create(
|
||
|
{
|
||
|
"name": "default",
|
||
|
"name_inverse": "default_inverse",
|
||
|
"contact_type_left": "p",
|
||
|
"contact_type_right": "p",
|
||
|
}
|
||
|
)
|
||
|
self.assertTrue(type_default)
|
||
|
with self.assertRaises(ValidationError):
|
||
|
self.relation_model.create(
|
||
|
{
|
||
|
"type_id": type_default.id,
|
||
|
"left_partner_id": self.partner_01_person.id,
|
||
|
"right_partner_id": self.partner_01_person.id,
|
||
|
}
|
||
|
)
|
||
|
|
||
|
def test_self_mixed(self):
|
||
|
"""Test creation of relation with wrong types.
|
||
|
|
||
|
Trying to create a relation between partners with an inappropiate
|
||
|
type should raise an error.
|
||
|
"""
|
||
|
with self.assertRaises(ValidationError):
|
||
|
self.relation_model.create(
|
||
|
{
|
||
|
"type_id": self.type_company2person.id,
|
||
|
"left_partner_id": self.partner_01_person.id,
|
||
|
"right_partner_id": self.partner_02_company.id,
|
||
|
}
|
||
|
)
|
||
|
|
||
|
def test_symmetric(self):
|
||
|
"""Test creating symmetric relation."""
|
||
|
# Start out with non symmetric relation:
|
||
|
type_symmetric = self.type_model.create(
|
||
|
{
|
||
|
"name": "not yet symmetric",
|
||
|
"name_inverse": "the other side of not symmetric",
|
||
|
"is_symmetric": False,
|
||
|
"contact_type_left": False,
|
||
|
"contact_type_right": "p",
|
||
|
}
|
||
|
)
|
||
|
# not yet symmetric relation should result in two records in
|
||
|
# selection:
|
||
|
selection_symmetric = self.selection_model.search(
|
||
|
[("type_id", "=", type_symmetric.id)]
|
||
|
)
|
||
|
self.assertEqual(len(selection_symmetric), 2)
|
||
|
# Now change to symmetric and test name and inverse name:
|
||
|
type_symmetric.write({"name": "sym", "is_symmetric": True})
|
||
|
self.assertEqual(type_symmetric.is_symmetric, True)
|
||
|
self.assertEqual(type_symmetric.name_inverse, type_symmetric.name)
|
||
|
self.assertEqual(
|
||
|
type_symmetric.contact_type_right, type_symmetric.contact_type_left
|
||
|
)
|
||
|
# now update the database:
|
||
|
type_symmetric.write(
|
||
|
{
|
||
|
"name": type_symmetric.name,
|
||
|
"is_symmetric": type_symmetric.is_symmetric,
|
||
|
"name_inverse": type_symmetric.name_inverse,
|
||
|
"contact_type_right": type_symmetric.contact_type_right,
|
||
|
}
|
||
|
)
|
||
|
type_symmetric.flush()
|
||
|
# symmetric relation should result in only one record in
|
||
|
# selection:
|
||
|
selection_symmetric = self.selection_model.search(
|
||
|
[("type_id", "=", type_symmetric.id)]
|
||
|
)
|
||
|
self.assertEqual(len(selection_symmetric), 1)
|
||
|
relation = self.relation_all_model.create(
|
||
|
{
|
||
|
"type_selection_id": selection_symmetric.id,
|
||
|
"this_partner_id": self.partner_02_company.id,
|
||
|
"other_partner_id": self.partner_01_person.id,
|
||
|
}
|
||
|
)
|
||
|
partners = self.partner_model.search(
|
||
|
[("search_relation_type_id", "=", relation.type_selection_id.id)]
|
||
|
)
|
||
|
self.assertTrue(self.partner_01_person in partners)
|
||
|
self.assertTrue(self.partner_02_company in partners)
|
||
|
|
||
|
def test_category_domain(self):
|
||
|
"""Test check on category in relations."""
|
||
|
# Check on left side:
|
||
|
with self.assertRaises(ValidationError):
|
||
|
self.relation_model.create(
|
||
|
{
|
||
|
"type_id": self.type_ngo2volunteer.id,
|
||
|
"left_partner_id": self.partner_02_company.id,
|
||
|
"right_partner_id": self.partner_04_volunteer.id,
|
||
|
}
|
||
|
)
|
||
|
# Check on right side:
|
||
|
with self.assertRaises(ValidationError):
|
||
|
self.relation_model.create(
|
||
|
{
|
||
|
"type_id": self.type_ngo2volunteer.id,
|
||
|
"left_partner_id": self.partner_03_ngo.id,
|
||
|
"right_partner_id": self.partner_01_person.id,
|
||
|
}
|
||
|
)
|
||
|
|
||
|
def test_relation_type_change(self):
|
||
|
"""Test change in relation type conditions."""
|
||
|
# First create a relation type having no particular conditions.
|
||
|
(
|
||
|
type_school2student,
|
||
|
school2student,
|
||
|
school2student_inverse,
|
||
|
) = self._create_relation_type_selection(
|
||
|
{"name": "school has student", "name_inverse": "studies at school"}
|
||
|
)
|
||
|
# Second create relations based on those conditions.
|
||
|
partner_school = self.partner_model.create(
|
||
|
{"name": "Test School", "is_company": True, "ref": "TS"}
|
||
|
)
|
||
|
partner_bart = self.partner_model.create(
|
||
|
{"name": "Bart Simpson", "is_company": False, "ref": "BS"}
|
||
|
)
|
||
|
partner_lisa = self.partner_model.create(
|
||
|
{"name": "Lisa Simpson", "is_company": False, "ref": "LS"}
|
||
|
)
|
||
|
relation_school2bart = self.relation_all_model.create(
|
||
|
{
|
||
|
"this_partner_id": partner_school.id,
|
||
|
"type_selection_id": school2student.id,
|
||
|
"other_partner_id": partner_bart.id,
|
||
|
}
|
||
|
)
|
||
|
self.assertTrue(relation_school2bart)
|
||
|
relation_school2lisa = self.relation_all_model.create(
|
||
|
{
|
||
|
"this_partner_id": partner_school.id,
|
||
|
"type_selection_id": school2student.id,
|
||
|
"other_partner_id": partner_lisa.id,
|
||
|
}
|
||
|
)
|
||
|
self.assertTrue(relation_school2lisa)
|
||
|
relation_bart2lisa = self.relation_all_model.create(
|
||
|
{
|
||
|
"this_partner_id": partner_bart.id,
|
||
|
"type_selection_id": school2student.id,
|
||
|
"other_partner_id": partner_lisa.id,
|
||
|
}
|
||
|
)
|
||
|
self.assertTrue(relation_bart2lisa)
|
||
|
# Third creata a category and make it a condition for the
|
||
|
# relation type.
|
||
|
# - Test restriction
|
||
|
# - Test ignore
|
||
|
category_student = self.category_model.create({"name": "Student"})
|
||
|
with self.assertRaises(ValidationError):
|
||
|
type_school2student.write({"partner_category_right": category_student.id})
|
||
|
self.assertFalse(type_school2student.partner_category_right.id)
|
||
|
type_school2student.write(
|
||
|
{
|
||
|
"handle_invalid_onchange": "ignore",
|
||
|
"partner_category_right": category_student.id,
|
||
|
}
|
||
|
)
|
||
|
self.assertEqual(
|
||
|
type_school2student.partner_category_right.id, category_student.id
|
||
|
)
|
||
|
# Fourth make company type a condition for left partner
|
||
|
# - Test ending
|
||
|
# - Test deletion
|
||
|
partner_bart.write({"category_id": [(4, category_student.id)]})
|
||
|
partner_lisa.write({"category_id": [(4, category_student.id)]})
|
||
|
# Future student to be deleted by end action:
|
||
|
partner_homer = self.partner_model.create(
|
||
|
{
|
||
|
"name": "Homer Simpson",
|
||
|
"is_company": False,
|
||
|
"ref": "HS",
|
||
|
"category_id": [(4, category_student.id)],
|
||
|
}
|
||
|
)
|
||
|
relation_lisa2homer = self.relation_all_model.create(
|
||
|
{
|
||
|
"this_partner_id": partner_lisa.id,
|
||
|
"type_selection_id": school2student.id,
|
||
|
"other_partner_id": partner_homer.id,
|
||
|
"date_start": date.today() + relativedelta(months=+6),
|
||
|
}
|
||
|
)
|
||
|
self.assertTrue(relation_lisa2homer)
|
||
|
type_school2student.write(
|
||
|
{"handle_invalid_onchange": "end", "contact_type_left": "c"}
|
||
|
)
|
||
|
self.assertEqual(relation_bart2lisa.date_end, fields.Date.today())
|
||
|
self.assertFalse(relation_lisa2homer.exists())
|
||
|
type_school2student.write(
|
||
|
{
|
||
|
"handle_invalid_onchange": "delete",
|
||
|
"contact_type_left": "c",
|
||
|
"contact_type_right": "p",
|
||
|
}
|
||
|
)
|
||
|
self.assertFalse(relation_bart2lisa.exists())
|
||
|
|
||
|
def test_relation_type_unlink(self):
|
||
|
"""Test delete of relation type, including deleting relations."""
|
||
|
# First create a relation type having restrict particular conditions.
|
||
|
type_model = self.env["res.partner.relation.type"]
|
||
|
relation_model = self.env["res.partner.relation"]
|
||
|
partner_model = self.env["res.partner"]
|
||
|
type_school2student = type_model.create(
|
||
|
{
|
||
|
"name": "school has student",
|
||
|
"name_inverse": "studies at school",
|
||
|
"handle_invalid_onchange": "delete",
|
||
|
}
|
||
|
)
|
||
|
# Second create relation based on those conditions.
|
||
|
partner_school = partner_model.create(
|
||
|
{"name": "Test School", "is_company": True, "ref": "TS"}
|
||
|
)
|
||
|
partner_bart = partner_model.create(
|
||
|
{"name": "Bart Simpson", "is_company": False, "ref": "BS"}
|
||
|
)
|
||
|
relation_school2bart = relation_model.create(
|
||
|
{
|
||
|
"left_partner_id": partner_school.id,
|
||
|
"type_id": type_school2student.id,
|
||
|
"right_partner_id": partner_bart.id,
|
||
|
}
|
||
|
)
|
||
|
# Delete type. Relations with type should also cease to exist:
|
||
|
type_school2student.unlink()
|
||
|
self.assertFalse(relation_school2bart.exists())
|