@@ -47,23 +47,28 @@ class SPPIndividual(models.Model):
4747 # Membership fields
4848 individual_membership_ids = fields .One2many ("spp.group.membership" , "individual" , "Membership to Groups" )
4949
50+ # Fields that trigger name recomputation
51+ _name_fields = {"family_name" , "given_name" , "addl_name" }
52+
53+ @api .model
54+ def _format_individual_name (self , family_name , given_name , addl_name ):
55+ """Compute display name from individual name parts.
56+
57+ Format: "FAMILY_NAME, GIVEN_NAME ADDL_NAME" (uppercase).
58+ Used by onchange (UI), create, and write to ensure consistent naming.
59+ """
60+ name_vals = [
61+ f"{ family_name } ," if family_name and (given_name or addl_name ) else f"{ family_name } " if family_name else "" ,
62+ given_name ,
63+ addl_name ,
64+ ]
65+ return " " .join (filter (None , name_vals )).upper ()
66+
5067 @api .onchange ("is_group" , "family_name" , "given_name" , "addl_name" )
5168 def name_change (self ):
52- vals = {}
5369 if not self .is_group :
54- name_vals = [
55- f"{ self .family_name } ,"
56- if self .family_name and (self .given_name or self .addl_name )
57- else f"{ self .family_name } "
58- if self .family_name
59- else "" ,
60- self .given_name ,
61- self .addl_name ,
62- ]
63-
64- name = " " .join (filter (None , name_vals ))
65- vals .update ({"name" : name .upper ()})
66- self .update (vals )
70+ name = self ._format_individual_name (self .family_name , self .given_name , self .addl_name )
71+ self .update ({"name" : name })
6772
6873 @api .depends ("birthdate" )
6974 def _compute_calc_age (self ):
@@ -129,8 +134,23 @@ def _recompute_parent_groups(self, records):
129134 self .env .add_to_compute (field , groups )
130135
131136 def write (self , vals ):
132- """Override to invalidate group metrics when demographics change."""
137+ """Override to recompute name and invalidate group metrics."""
138+ # Recompute name if any name field changed (only for non-group individuals)
139+ name_updates = {}
140+ if self ._name_fields & vals .keys () and not self .env .context .get ("skip_name_format" ):
141+ for rec in self .filtered (lambda r : not r .is_group ):
142+ name_updates [rec .id ] = self ._format_individual_name (
143+ vals .get ("family_name" , rec .family_name ),
144+ vals .get ("given_name" , rec .given_name ),
145+ vals .get ("addl_name" , rec .addl_name ),
146+ )
147+
133148 res = super ().write (vals )
149+
150+ # Apply computed names after super to avoid interfering with the main write
151+ if name_updates :
152+ for rec_id , name in name_updates .items ():
153+ super (SPPIndividual , self .browse (rec_id ).with_context (skip_name_format = True )).write ({"name" : name })
134154 self ._recompute_parent_groups (self )
135155
136156 # Fields that affect group-level metrics
@@ -177,6 +197,14 @@ def _invalidate_parent_group_metrics(self, individuals):
177197
178198 @api .model_create_multi
179199 def create (self , vals_list ):
200+ # Compute name from parts before create (injects into vals, no extra write)
201+ for vals in vals_list :
202+ if not vals .get ("is_group" ) and self ._name_fields & vals .keys ():
203+ vals ["name" ] = self ._format_individual_name (
204+ vals .get ("family_name" , "" ),
205+ vals .get ("given_name" , "" ),
206+ vals .get ("addl_name" , "" ),
207+ )
180208 res = super ().create (vals_list )
181209 self ._recompute_parent_groups (res )
182210 return res
0 commit comments