Skip to content

fix(change_request): make group-scope conflict rules resolve household members - #477

Open
kneckinator wants to merge 2 commits into
19.0from
19.0-476-conflict-mixin-membership-fields
Open

fix(change_request): make group-scope conflict rules resolve household members#477
kneckinator wants to merge 2 commits into
19.0from
19.0-476-conflict-mixin-membership-fields

Conversation

@kneckinator

@kneckinator kneckinator commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Fixes #476.

Problem

ConflictDetectionMixin._get_group_member_ids traversed spp.group.membership records through individual_id and group_id, but the model names its many2ones individual and group (spp_registry/models/group_membership.py). Resolving a household's members therefore crashed instead of returning them:

  • Group registrant: KeyError: 'individual_id' unconditionally — even for an empty group, since mapped() resolves the field name before iterating.
  • Individual registrant: AttributeError on the first non-ended membership — exactly when a group-scope rule has work to do.

Any change request type with an active scope = "group" conflict rule crashed on CR creation for these registrants, making group-scope rules unusable.

Why tests didn't catch it

The one existing test called the method directly on an individual with zero memberships — the single shape that happens to work, since the loop body and mapped() calls never execute.

Fix

  • Rename the three accesses to the real field names (individual, group) — no behavior change beyond un-breaking the traversal.
  • test_group_scope_same_household_members now creates real memberships and asserts both the resolved member set and the end-to-end warning status on the second CR in the household.
  • New test_group_scope_group_registrant covers the group-registrant branch.
  • New test_group_scope_ended_membership_excluded pins that ended memberships are excluded from conflict candidates.

TDD: the three tests were written first and reproduced both exceptions (KeyError: 'individual_id', AttributeError: ... no attribute 'group_id') before the fix.

Verification

  • ./spp t spp_change_request_v2: 0 failed, 0 error(s) of 415 tests (red run before the fix: the 3 new tests errored, 412 passed).
  • pre-commit clean && pre-commit run --all-files: clean for this module (semgrep crashes locally on Python 3.14 — CI is the authoritative run; README regeneration of unrelated drifted modules was reverted).

Found while reviewing #418 (pre-existing on 19.0, out of scope there).

…d members

_get_group_member_ids traversed spp.group.membership through
individual_id and group_id, but the model names its many2ones
individual and group. Resolving a household's members therefore raised
KeyError/AttributeError instead of returning them: a change request
whose type carried an active group-scope conflict rule crashed on
creation for any group registrant, and for any individual registrant
with a live membership — exactly the registrants the rule exists to
check.

The one existing test called the method with a member-less individual,
the single shape that happened to work. Group-scope resolution is now
tested with real memberships in both directions, including that ended
memberships are excluded.

Fixes #476
@codecov

codecov Bot commented Aug 28, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 76.27%. Comparing base (0341801) to head (c2a3817).

Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff           @@
##             19.0     #477   +/-   ##
=======================================
  Coverage   76.26%   76.27%           
=======================================
  Files         662      662           
  Lines       44225    44218    -7     
=======================================
- Hits        33729    33727    -2     
+ Misses      10496    10491    -5     
Flag Coverage Δ
spp_api_v2_change_request 73.37% <ø> (ø)
spp_base_common 91.07% <ø> (ø)
spp_change_request_v2 78.76% <100.00%> (+0.12%) ⬆️
spp_cr_type_assign_program 92.50% <ø> (ø)
spp_dci_demo 94.28% <ø> (ø)
spp_farmer_registry_cr 61.24% <ø> (ø)
spp_farmer_registry_demo 63.39% <ø> (ø)
spp_mis_demo_v2 70.38% <ø> (ø)
spp_programs 67.58% <ø> (ø)
spp_registry 87.79% <ø> (ø)
spp_security 69.56% <ø> (ø)
spp_starter_sp_mis 86.84% <ø> (-2.05%) ⬇️
spp_studio_change_requests 84.85% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
spp_change_request_v2/models/conflict_mixin.py 92.46% <100.00%> (+1.71%) ⬆️

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

_create_household already creates both membership rows, so the ended
membership test can set ended_date through the individual's own
membership instead of querying the pair back by (group, individual).
That drops a query and, more usefully, a second place spelling the
membership field names — the very drift this branch repairs.

Also name the change request under test `cr`, matching the sibling
tests; the `cr1` name dated from a `cr2` that no longer exists.

Restore the README table padding and column widths emitted by CI's
generator: regenerating locally on Python 3.14 re-renders an unrelated
docutils table one column wider than CI's Python 3.11, which failed the
pre-commit check. Only the changelog entry this branch owns remains.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

spp_change_request_v2: group-scope conflict detection crashes — conflict_mixin references nonexistent membership fields individual_id/group_id

1 participant