Skip to content

Commit 90ba115

Browse files
[UPD] spp_registry_name_suffix: add exclusion groups for mutually exclusive suffixes
Add exclusion_group field to prevent incompatible suffixes from being selected together. Generational suffixes (Jr., Sr., Jra., I-XV) are now in the 'generational' group and cannot coexist. Academic/professional suffixes (PhD, MD, Esq.) have no exclusion group and can be freely combined.
1 parent 462be1d commit 90ba115

File tree

5 files changed

+155
-4
lines changed

5 files changed

+155
-4
lines changed

spp_registry_name_suffix/data/name_suffix_data.xml

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,130 +1,148 @@
11
<?xml version="1.0" encoding="utf-8" ?>
22
<odoo noupdate="1">
33

4-
<!-- Generational Suffixes -->
4+
<!-- Generational Suffixes (mutually exclusive - only one can be used) -->
55
<record id="suffix_jr" model="spp.name.suffix">
66
<field name="name">Jr.</field>
77
<field name="code">JR</field>
88
<field name="sequence">10</field>
9+
<field name="exclusion_group">generational</field>
910
<field name="description">Junior - typically used for a son named after his father</field>
1011
</record>
1112

1213
<record id="suffix_sr" model="spp.name.suffix">
1314
<field name="name">Sr.</field>
1415
<field name="code">SR</field>
1516
<field name="sequence">20</field>
17+
<field name="exclusion_group">generational</field>
1618
<field name="description">Senior - typically used for a father when a son has the same name</field>
1719
</record>
1820

1921
<record id="suffix_jra" model="spp.name.suffix">
2022
<field name="name">Jra.</field>
2123
<field name="code">JRA</field>
2224
<field name="sequence">25</field>
23-
<field name="description">JRA </field>
25+
<field name="exclusion_group">generational</field>
26+
<field name="description">JRA</field>
2427
</record>
2528

2629
<record id="suffix_i" model="spp.name.suffix">
2730
<field name="name">I</field>
2831
<field name="code">I</field>
2932
<field name="sequence">30</field>
33+
<field name="exclusion_group">generational</field>
3034
<field name="description">The First</field>
3135
</record>
3236

3337
<record id="suffix_ii" model="spp.name.suffix">
3438
<field name="name">II</field>
3539
<field name="code">II</field>
3640
<field name="sequence">40</field>
41+
<field name="exclusion_group">generational</field>
3742
<field name="description">The Second</field>
3843
</record>
3944

4045
<record id="suffix_iii" model="spp.name.suffix">
4146
<field name="name">III</field>
4247
<field name="code">III</field>
4348
<field name="sequence">50</field>
49+
<field name="exclusion_group">generational</field>
4450
<field name="description">The Third</field>
4551
</record>
4652

4753
<record id="suffix_iv" model="spp.name.suffix">
4854
<field name="name">IV</field>
4955
<field name="code">IV</field>
5056
<field name="sequence">60</field>
57+
<field name="exclusion_group">generational</field>
5158
<field name="description">The Fourth</field>
5259
</record>
5360

5461
<record id="suffix_v" model="spp.name.suffix">
5562
<field name="name">V</field>
5663
<field name="code">V</field>
5764
<field name="sequence">70</field>
65+
<field name="exclusion_group">generational</field>
5866
<field name="description">The Fifth</field>
5967
</record>
6068

6169
<record id="suffix_vi" model="spp.name.suffix">
6270
<field name="name">VI</field>
6371
<field name="code">VI</field>
6472
<field name="sequence">71</field>
73+
<field name="exclusion_group">generational</field>
6574
<field name="description">The Sixth</field>
6675
</record>
6776

6877
<record id="suffix_vii" model="spp.name.suffix">
6978
<field name="name">VII</field>
7079
<field name="code">VII</field>
7180
<field name="sequence">72</field>
81+
<field name="exclusion_group">generational</field>
7282
<field name="description">The Seventh</field>
7383
</record>
7484

7585
<record id="suffix_viii" model="spp.name.suffix">
7686
<field name="name">VIII</field>
7787
<field name="code">VIII</field>
7888
<field name="sequence">73</field>
89+
<field name="exclusion_group">generational</field>
7990
<field name="description">The Eighth</field>
8091
</record>
8192

8293
<record id="suffix_ix" model="spp.name.suffix">
8394
<field name="name">IX</field>
8495
<field name="code">IX</field>
8596
<field name="sequence">74</field>
97+
<field name="exclusion_group">generational</field>
8698
<field name="description">The Ninth</field>
8799
</record>
88100

89101
<record id="suffix_x" model="spp.name.suffix">
90102
<field name="name">X</field>
91103
<field name="code">X</field>
92104
<field name="sequence">75</field>
105+
<field name="exclusion_group">generational</field>
93106
<field name="description">The Tenth</field>
94107
</record>
95108

96109
<record id="suffix_xi" model="spp.name.suffix">
97110
<field name="name">XI</field>
98111
<field name="code">XI</field>
99112
<field name="sequence">76</field>
113+
<field name="exclusion_group">generational</field>
100114
<field name="description">The Eleventh</field>
101115
</record>
102116

103117
<record id="suffix_xii" model="spp.name.suffix">
104118
<field name="name">XII</field>
105119
<field name="code">XII</field>
106120
<field name="sequence">77</field>
121+
<field name="exclusion_group">generational</field>
107122
<field name="description">The Twelfth</field>
108123
</record>
109124

110125
<record id="suffix_xiii" model="spp.name.suffix">
111126
<field name="name">XIII</field>
112127
<field name="code">XIII</field>
113128
<field name="sequence">78</field>
129+
<field name="exclusion_group">generational</field>
114130
<field name="description">The Thirteenth</field>
115131
</record>
116132

117133
<record id="suffix_xiv" model="spp.name.suffix">
118134
<field name="name">XIV</field>
119135
<field name="code">XIV</field>
120136
<field name="sequence">79</field>
137+
<field name="exclusion_group">generational</field>
121138
<field name="description">The Fourteenth</field>
122139
</record>
123140

124141
<record id="suffix_xv" model="spp.name.suffix">
125142
<field name="name">XV</field>
126143
<field name="code">XV</field>
127144
<field name="sequence">80</field>
145+
<field name="exclusion_group">generational</field>
128146
<field name="description">The Fifteenth</field>
129147
</record>
130148

spp_registry_name_suffix/models/name_suffix.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ class SPPNameSuffix(models.Model):
3030
string="Description",
3131
help="Additional description or usage notes for this suffix",
3232
)
33+
exclusion_group = fields.Char(
34+
string="Exclusion Group",
35+
help="Suffixes in the same exclusion group cannot be used together. "
36+
"For example, 'generational' for Jr., Sr., I, II, III, etc.",
37+
)
3338

3439
_sql_constraints = [
3540
(

spp_registry_name_suffix/models/res_partner.py

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
from odoo import api, fields, models
1+
from odoo import _, api, fields, models
2+
from odoo.exceptions import ValidationError
23

34

45
class ResPartner(models.Model):
@@ -10,9 +11,37 @@ class ResPartner(models.Model):
1011
column1="partner_id",
1112
column2="suffix_id",
1213
string="Suffixes",
13-
help="Name suffixes such as Jr., Sr., III, IV, PhD, MD, etc.",
14+
help="Name suffixes",
1415
)
1516

17+
@api.constrains("suffix_ids")
18+
def _check_suffix_exclusion_groups(self):
19+
"""Validate that no two suffixes from the same exclusion group are selected."""
20+
for record in self:
21+
if not record.suffix_ids:
22+
continue
23+
# Get suffixes that have an exclusion group
24+
suffixes_with_groups = record.suffix_ids.filtered(lambda s: s.exclusion_group)
25+
# Group by exclusion_group
26+
groups = {}
27+
for suffix in suffixes_with_groups:
28+
group = suffix.exclusion_group
29+
if group not in groups:
30+
groups[group] = []
31+
groups[group].append(suffix.name)
32+
# Check for conflicts
33+
for group, suffix_names in groups.items():
34+
if len(suffix_names) > 1:
35+
raise ValidationError(
36+
_(
37+
"The following suffixes cannot be used together "
38+
"as they belong to the same exclusion group '%(group)s': "
39+
"%(suffixes)s",
40+
group=group,
41+
suffixes=", ".join(suffix_names),
42+
)
43+
)
44+
1645
@api.onchange("is_group", "family_name", "given_name", "addl_name", "suffix_ids")
1746
def name_change(self):
1847
"""Extend name change to include suffixes for individuals."""

spp_registry_name_suffix/tests/test_name_suffix.py

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from odoo.exceptions import ValidationError
12
from odoo.tests import tagged
23
from odoo.tests.common import TransactionCase
34

@@ -16,6 +17,8 @@ def setUpClass(cls):
1617

1718
# Use existing suffixes from data file
1819
cls.suffix_jr = cls.env.ref("spp_registry_name_suffix.suffix_jr")
20+
cls.suffix_sr = cls.env.ref("spp_registry_name_suffix.suffix_sr")
21+
cls.suffix_iii = cls.env.ref("spp_registry_name_suffix.suffix_iii")
1922
cls.suffix_phd = cls.env.ref("spp_registry_name_suffix.suffix_phd")
2023

2124
def test_01_suffix_model_creation(self):
@@ -240,3 +243,89 @@ def test_13_name_get_multiple_records(self):
240243
result_ids = [r[0] for r in result]
241244
self.assertIn(self.suffix_jr.id, result_ids)
242245
self.assertIn(self.suffix_phd.id, result_ids)
246+
247+
def test_14_exclusion_group_set_on_generational_suffixes(self):
248+
"""Test that generational suffixes have exclusion group set."""
249+
self.assertEqual(
250+
self.suffix_jr.exclusion_group,
251+
"generational",
252+
"Jr. should have generational exclusion group",
253+
)
254+
self.assertEqual(
255+
self.suffix_sr.exclusion_group,
256+
"generational",
257+
"Sr. should have generational exclusion group",
258+
)
259+
self.assertEqual(
260+
self.suffix_iii.exclusion_group,
261+
"generational",
262+
"III should have generational exclusion group",
263+
)
264+
self.assertFalse(
265+
self.suffix_phd.exclusion_group,
266+
"PhD should not have an exclusion group",
267+
)
268+
269+
def test_15_exclusion_group_prevents_conflicting_suffixes(self):
270+
"""Test that two suffixes from same exclusion group cannot be selected."""
271+
individual = self.env["res.partner"].create(
272+
{
273+
"name": "Temp",
274+
"family_name": "Doe",
275+
"given_name": "John",
276+
"is_registrant": True,
277+
"is_group": False,
278+
}
279+
)
280+
# Try to add both Jr. and Sr. (both in 'generational' group)
281+
with self.assertRaises(ValidationError) as context:
282+
individual.write({"suffix_ids": [(6, 0, [self.suffix_jr.id, self.suffix_sr.id])]})
283+
self.assertIn("generational", str(context.exception))
284+
285+
def test_16_exclusion_group_allows_different_groups(self):
286+
"""Test that suffixes from different groups can be selected together."""
287+
# Jr. (generational) + PhD (no group) should work
288+
individual = self.env["res.partner"].create(
289+
{
290+
"name": "Temp",
291+
"family_name": "Smith",
292+
"given_name": "Jane",
293+
"suffix_ids": [(6, 0, [self.suffix_jr.id, self.suffix_phd.id])],
294+
"is_registrant": True,
295+
"is_group": False,
296+
}
297+
)
298+
self.assertEqual(len(individual.suffix_ids), 2)
299+
individual.name_change()
300+
self.assertEqual(individual.name, "SMITH, JANE, JR., PHD")
301+
302+
def test_17_exclusion_group_roman_numerals_conflict(self):
303+
"""Test that two roman numeral suffixes cannot be selected together."""
304+
individual = self.env["res.partner"].create(
305+
{
306+
"name": "Temp",
307+
"family_name": "King",
308+
"given_name": "Henry",
309+
"is_registrant": True,
310+
"is_group": False,
311+
}
312+
)
313+
suffix_iv = self.env.ref("spp_registry_name_suffix.suffix_iv")
314+
# Try to add both III and IV (both in 'generational' group)
315+
with self.assertRaises(ValidationError):
316+
individual.write({"suffix_ids": [(6, 0, [self.suffix_iii.id, suffix_iv.id])]})
317+
318+
def test_18_exclusion_group_jr_and_roman_numeral_conflict(self):
319+
"""Test that Jr. and roman numerals cannot be selected together."""
320+
individual = self.env["res.partner"].create(
321+
{
322+
"name": "Temp",
323+
"family_name": "Windsor",
324+
"given_name": "Charles",
325+
"is_registrant": True,
326+
"is_group": False,
327+
}
328+
)
329+
# Try to add Jr. and III (both in 'generational' group)
330+
with self.assertRaises(ValidationError):
331+
individual.write({"suffix_ids": [(6, 0, [self.suffix_jr.id, self.suffix_iii.id])]})

spp_registry_name_suffix/views/name_suffix_views.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
<field name="sequence" widget="handle" />
1111
<field name="name" />
1212
<field name="code" />
13+
<field name="exclusion_group" optional="show" />
1314
<field name="active" />
1415
</tree>
1516
</field>
@@ -29,6 +30,7 @@
2930
</group>
3031
<group name="right_group">
3132
<field name="sequence" />
33+
<field name="exclusion_group" />
3234
</group>
3335
</group>
3436
<group name="description_group">
@@ -47,8 +49,16 @@
4749
<search>
4850
<field name="name" />
4951
<field name="code" />
52+
<field name="exclusion_group" />
5053
<filter name="filter_active" string="Active" domain="[('active', '=', True)]" />
5154
<filter name="filter_inactive" string="Archived" domain="[('active', '=', False)]" />
55+
<group expand="0" string="Group By">
56+
<filter
57+
name="groupby_exclusion"
58+
string="Exclusion Group"
59+
context="{'group_by': 'exclusion_group'}"
60+
/>
61+
</group>
5262
</search>
5363
</field>
5464
</record>

0 commit comments

Comments
 (0)