fix(spp_registry): format individual name on create and write#160
fix(spp_registry): format individual name on create and write#160
Conversation
The name_change onchange only runs in the UI. When creating or updating individuals via XML-RPC, API, or code, the display name was not formatted as "FAMILY_NAME, GIVEN_NAME ADDL_NAME" (uppercase). Extract name formatting into _format_individual_name() helper and call it from create (injects into vals before super) and write (applies after super only when name fields are in vals). Groups are not affected.
There was a problem hiding this comment.
Code Review
This pull request centralizes name formatting for individuals by introducing a _format_individual_name helper and applying it during record creation, updates, and UI changes. The write and create methods are overridden to ensure the name field remains consistent with its constituent parts. Feedback was provided to optimize the write method by injecting the formatted name directly into the update values for single-record operations and to improve correctness when the is_group field is being modified.
| name_updates = {} | ||
| if self._name_fields & vals.keys() and not self.env.context.get("skip_name_format"): | ||
| for rec in self.filtered(lambda r: not r.is_group): | ||
| name_updates[rec.id] = self._format_individual_name( | ||
| vals.get("family_name", rec.family_name), | ||
| vals.get("given_name", rec.given_name), | ||
| vals.get("addl_name", rec.addl_name), | ||
| ) | ||
|
|
||
| res = super().write(vals) | ||
|
|
||
| # Apply computed names after super to avoid interfering with the main write | ||
| if name_updates: | ||
| for rec_id, name in name_updates.items(): | ||
| super(SPPIndividual, self.browse(rec_id).with_context(skip_name_format=True)).write({"name": name}) |
There was a problem hiding this comment.
The write override can be improved for both performance and correctness:
- Performance: For single-record updates (the most common case, such as UI edits), you can inject the formatted name directly into
valsbefore callingsuper().write(). This avoids an extra database write and re-triggering the fullwritemethod logic for each record. - Correctness: The filter
not r.is_groupuses the current database value. Ifis_groupis being changed in the samewritecall (e.g., converting a group to an individual or vice versa), this logic might incorrectly format or skip the name. It is safer to checkvals.get('is_group', r.is_group). - Efficiency: You can store the record object itself in the
name_updatesdictionary to avoid redundantbrowse()calls in the second loop.
name_updates = {}
if self._name_fields & vals.keys() and not self.env.context.get("skip_name_format"):
# Optimization: for single record, inject name into vals to avoid extra write
if len(self) == 1 and not vals.get("is_group", self.is_group):
vals["name"] = self._format_individual_name(
vals.get("family_name", self.family_name),
vals.get("given_name", self.given_name),
vals.get("addl_name", self.addl_name),
)
else:
for rec in self.filtered(lambda r: not vals.get("is_group", r.is_group)):
name_updates[rec] = self._format_individual_name(
vals.get("family_name", rec.family_name),
vals.get("given_name", rec.given_name),
vals.get("addl_name", rec.addl_name),
)
res = super().write(vals)
# Apply computed names after super to avoid interfering with the main write
if name_updates:
for rec, name in name_updates.items():
super(SPPIndividual, rec.with_context(skip_name_format=True)).write({"name": name})
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## 19.0 #160 +/- ##
==========================================
+ Coverage 71.06% 71.19% +0.13%
==========================================
Files 925 931 +6
Lines 54704 54749 +45
==========================================
+ Hits 38876 38980 +104
+ Misses 15828 15769 -59
Flags with carried forward coverage won't be shown. Click here to find out more. 🚀 New features to boost your workflow:
|
Why is this change needed?
When creating or updating individual registrants programmatically (e.g., via API, demo generator, or import), the
namefield was not automatically formatted from name parts (family_name,given_name,addl_name). Thename_changeonchange only fires in the UI, so programmatic operations resulted in registrants with unformatted or missing display names.How was the change implemented?
_format_individual_name()class methodcreate()to inject formatted name into vals before record creation (no extra write needed)write()to recompute name when any name field changesname_changeonchange now delegates to the same helper for consistencyskip_name_formatcontext flag to prevent recursion during the name write-backNew unit tests
N/A — existing tests cover the onchange path; the create/write paths are exercised by the demo generator and API tests.
How to test manually
family_nameandgiven_namebut nonamenamefield should be auto-set to "FAMILY_NAME, GIVEN_NAME" (uppercase)given_namevia write —nameshould update accordinglyRelated links