Skip to content

Commit bd2c5b1

Browse files
committed
revert(change_request): drop head-of-household replacement on Add Member (#871)
The explicit head-of-household replacement feature (commits 908cedc, 1612822, a8c9b05) is deferred to #1006 ("make it configurable to prevent groups without head of household"), which is on hold. Removing it here per QA so #871 ships the Add Member review-page fix without it.
1 parent a8c9b05 commit bd2c5b1

4 files changed

Lines changed: 13 additions & 123 deletions

File tree

spp_change_request_v2/details/add_member.py

Lines changed: 0 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -136,25 +136,6 @@ class SPPCRDetailAddMember(models.Model):
136136
help="True when the urn:openspp:vocab:group-membership-type vocabulary has any active code.",
137137
)
138138

139-
# ──────────────────────────────────────────────────────────────────────
140-
# Head-of-household replacement (OP#871 QA round 1)
141-
# When the target group already has a head and this new member is given the
142-
# Head role, an info banner warns that applying will replace the current
143-
# head. The replacement then happens automatically on apply.
144-
# ──────────────────────────────────────────────────────────────────────
145-
group_has_head = fields.Boolean(
146-
compute="_compute_current_head",
147-
string="Group Already Has a Head",
148-
)
149-
current_head_name = fields.Char(
150-
compute="_compute_current_head",
151-
string="Current Head of Household",
152-
)
153-
selected_role_is_head = fields.Boolean(
154-
compute="_compute_selected_role_is_head",
155-
string="Selected Role Is Head",
156-
)
157-
158139
# ──────────────────────────────────────────────────────────────────────
159140
# Read-only context: existing members of the group (for the spec's
160141
# "display a table with all members" requirement). Computed, not stored.
@@ -221,33 +202,3 @@ def _compute_existing_memberships(self):
221202
rec.existing_membership_ids = Membership.search([("group", "=", group.id), ("status", "=", "active")])
222203
else:
223204
rec.existing_membership_ids = Membership.browse([])
224-
225-
@api.depends("change_request_id", "change_request_id.registrant_id")
226-
def _compute_current_head(self):
227-
Membership = self.env["spp.group.membership"]
228-
head_code = self.env["spp.vocabulary.code"].search(
229-
[
230-
("vocabulary_id.namespace_uri", "=", "urn:openspp:vocab:group-membership-type"),
231-
("code", "=", "head"),
232-
],
233-
limit=1,
234-
)
235-
for rec in self:
236-
head = Membership.browse()
237-
group = rec.change_request_id.registrant_id
238-
if group and group.is_group and head_code:
239-
head = Membership.search(
240-
[
241-
("group", "=", group.id),
242-
("status", "=", "active"),
243-
("membership_type_ids", "=", head_code.id),
244-
],
245-
limit=1,
246-
)
247-
rec.group_has_head = bool(head)
248-
rec.current_head_name = head.individual.name if head else False
249-
250-
@api.depends("membership_type_id")
251-
def _compute_selected_role_is_head(self):
252-
for rec in self:
253-
rec.selected_role_is_head = bool(rec.membership_type_id and rec.membership_type_id.code == "head")

spp_change_request_v2/strategies/add_member.py

Lines changed: 9 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -180,31 +180,20 @@ def _attach_id_docs(self, detail, individual):
180180
def _is_head_role(self, code):
181181
return bool(code and code.code == HEAD_ROLE_CODE)
182182

183-
def _head_code(self):
184-
return self.env["spp.vocabulary.code"].search(
185-
[
186-
("vocabulary_id.namespace_uri", "=", "urn:openspp:vocab:group-membership-type"),
187-
("code", "=", HEAD_ROLE_CODE),
188-
],
189-
limit=1,
190-
)
191-
192183
def _demote_existing_head_if_needed(self, detail, group):
193-
"""Demote the group's current head when the user confirmed replacement.
194-
195-
Only runs when ``replace_existing_head`` is set on the CR — making the
196-
new member the head is an explicit action, not a silent side effect of
197-
picking the Head role. "Demote" unlinks the ``head`` code from the
198-
existing membership's ``membership_type_ids``; other roles are kept.
184+
"""When the new member is being added as head, demote any existing
185+
head on the group first so the at-most-one-head invariant holds.
186+
187+
"Demote" means unlinking the ``head`` code from the existing
188+
membership's ``membership_type_ids``. Other roles on the membership
189+
are preserved; if the membership had only the head role, it ends up
190+
with no roles (which is acceptable — the spec doesn't define a
191+
downgrade target).
199192
"""
200-
# Only when the new member is being made head. Demotion is automatic on
201-
# apply; the form warns the user beforehand via the info banner.
202193
if not self._is_head_role(detail.membership_type_id):
203194
return
204195

205-
head_code = self._head_code()
206-
if not head_code:
207-
return
196+
head_code = detail.membership_type_id # already resolved to "head"
208197
Membership = self.env["spp.group.membership"]
209198
existing_head_memberships = Membership.search(
210199
[
@@ -229,8 +218,6 @@ def _create_membership(self, detail, group, individual):
229218
"individual": individual.id,
230219
"start_date": fields.Datetime.now(),
231220
}
232-
# The picked Role determines the new member's role; the replace toggle
233-
# is only a confirmation that gates demoting the current head.
234221
if detail.membership_type_id:
235222
vals["membership_type_ids"] = [Command.link(detail.membership_type_id.id)]
236223
self.env["spp.group.membership"].create(vals)

spp_change_request_v2/tests/test_add_member_strategy.py

Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ def test_adding_head_demotes_existing_head(self):
161161
)
162162
self.assertIn(self.head_kind, old_membership.membership_type_ids)
163163

164-
# Add new member as head — replacement happens automatically on apply.
164+
# Add new member as head.
165165
cr = self._make_cr(
166166
given_name="New",
167167
family_name="Head",
@@ -178,34 +178,6 @@ def test_adding_head_demotes_existing_head(self):
178178
# Old head's membership had `head` removed.
179179
self.assertNotIn(self.head_kind, old_membership.membership_type_ids)
180180

181-
def test_non_head_role_leaves_existing_head(self):
182-
"""Adding a non-head member must not touch the group's existing head."""
183-
if not self.head_kind or not self.member_kind:
184-
self.skipTest("head/member membership-type codes not present")
185-
old_head = self.partner_model.create({"name": "Keep Head", "is_registrant": True, "is_group": False})
186-
old_membership = self.membership_model.create(
187-
{
188-
"group": self.group.id,
189-
"individual": old_head.id,
190-
"start_date": "2020-01-01",
191-
"membership_type_ids": [Command.link(self.head_kind.id)],
192-
}
193-
)
194-
cr = self._make_cr(given_name="Plain", family_name="Member", membership_type_id=self.member_kind.id)
195-
detail = cr.get_detail()
196-
self.assertTrue(detail.group_has_head)
197-
self.assertFalse(detail.selected_role_is_head) # drives the info banner visibility
198-
cr.approval_state = "approved"
199-
cr.action_apply()
200-
new_member = detail.created_individual_id
201-
new_membership = self.membership_model.search(
202-
[("group", "=", self.group.id), ("individual", "=", new_member.id)]
203-
)
204-
self.assertIn(self.member_kind, new_membership.membership_type_ids)
205-
self.assertNotIn(self.head_kind, new_membership.membership_type_ids)
206-
# Existing head is untouched.
207-
self.assertIn(self.head_kind, old_membership.membership_type_ids)
208-
209181
# ──────────────────────────────────────────────────────────────────
210182
# Sub-record attachers
211183
# ──────────────────────────────────────────────────────────────────

spp_change_request_v2/views/detail_add_member_views.xml

Lines changed: 3 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -171,29 +171,9 @@
171171
invisible="not type_requires_head"
172172
>
173173
This Change Request type requires a Head of Household.
174-
Set the new member's <strong>Role</strong> to <em
175-
>Head</em> before applying.
176-
</div>
177-
178-
<!-- Head-of-household replacement notice (OP#871 QA round 1):
179-
shown only when Role is Head and the group already has one. -->
180-
<field name="group_has_head" invisible="1" />
181-
<field name="selected_role_is_head" invisible="1" />
182-
<div
183-
class="alert alert-warning"
184-
role="alert"
185-
invisible="not group_has_head or not selected_role_is_head"
186-
>
187-
<i class="fa fa-exclamation-triangle me-2" />
188-
This group already has a Head of Household
189-
(<field
190-
name="current_head_name"
191-
readonly="1"
192-
nolabel="1"
193-
class="d-inline"
194-
/>).
195-
Applying this change request will make this new member the Head and
196-
remove the Head role from the current head.
174+
If you set the new member's <strong>Role</strong> to <em
175+
>Head</em>, the existing head of the group will be demoted
176+
automatically when the CR is applied.
197177
</div>
198178

199179
<separator string="Existing members (read-only)" />

0 commit comments

Comments
 (0)