Skip to content

fix(spp_programs): fix deduplication and enrollment interaction bugs#113

Draft
emjay0921 wants to merge 5 commits into19.0from
fix/spp-programs-deduplication-enrollment
Draft

fix(spp_programs): fix deduplication and enrollment interaction bugs#113
emjay0921 wants to merge 5 commits into19.0from
fix/spp-programs-deduplication-enrollment

Conversation

@emjay0921
Copy link
Contributor

@emjay0921 emjay0921 commented Mar 18, 2026

Why is this change needed?

Several bugs in the deduplication and enrollment workflows cause incorrect behavior:

  • Deduplication records generic reasons ("Duplicate individuals", "Duplicate ID Documents") making it hard to identify which member/document caused the duplicate
  • Re-running enrollment overwrites duplicated and exited states, undoing deduplication results
  • The ID deduplication manager uses the deprecated spp.id.type model instead of spp.vocabulary.code
  • No visibility into why a beneficiary was flagged as duplicate
  • Test coverage for deduplication, enrollment, and membership workflows was missing

How was the change implemented?

Bug fixes

  • Detailed duplicate reasons: All deduplication managers now record specific reasons showing which member, ID document, or phone number caused the duplicate
  • Enrollment protection: duplicated and exited states are excluded from bulk enrollment and individual enrollment, preventing accidental state overwrites
  • ID type migration: supported_id_document_type_ids now uses spp.vocabulary.code with domain filter for ID types, and uses .display instead of .name
  • Duplicate reason field: Added computed duplicate_reason field on spp.program.membership shown in the form view when state is duplicated
  • Better dedup summary: Program-level deduplication now reports new vs already-flagged duplicates
  • Individual dedup rewrite: _check_duplicate_by_individual for both ID and phone deduplication now properly groups duplicates by shared value and applies keep-one-enrolled logic

Test coverage

  • Added test_deduplication.py — 30+ tests covering default, ID document, and phone number deduplication (shared members, detailed reasons, keep-one-enrolled, expired IDs, unsupported types, record duplicate)
  • Added test_program_membership.py — 18+ tests covering membership lifecycle (enrollment protection for duplicated/exited, pause/resume/exit, duplicate reason, eligibility verification, deduplication from membership, form view context)
  • Added test_program_enrollment.py — 20+ tests covering program-level enrollment and deduplication workflows (enrollment skips duplicated/exited, dedup summary messages, program model methods)
  • Updated __init__.py to import previously missing test files (test_create_program_wizard_inkind, test_cycle_auto_approve_fund_check)

New unit tests

  • spp_programs/tests/test_deduplication.py
  • spp_programs/tests/test_program_membership.py
  • spp_programs/tests/test_program_enrollment.py

Unit tests executed by the author

All new tests pass. Pre-existing test failures in test_entitlement_amount_cel and test_cycle_auto_approve_fund_check are unrelated.

How to test manually

  1. Create a program with group target type and add beneficiaries with shared members
  2. Run deduplication — verify detailed reasons appear in duplicate records
  3. Run enrollment — verify duplicated/exited members are not re-enrolled
  4. Open a duplicated membership form — verify the duplicate reason alert is visible

Related links

@gemini-code-assist
Copy link

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses several critical bugs within the deduplication and enrollment workflows, aiming to improve data integrity, user clarity, and process robustness. The changes provide more granular insights into why beneficiaries are flagged as duplicates, prevent unintended state changes during enrollment, and modernize the underlying data models for ID types. The overall impact is a more reliable and transparent system for managing program memberships and identifying duplicate entries.

Highlights

  • Enhanced Deduplication Reasons: Deduplication managers now record specific, detailed reasons for duplicates, indicating which member, ID document, or phone number caused the duplication, replacing generic messages.
  • Enrollment State Protection: The enrollment process has been updated to prevent accidental overwriting of 'duplicated' and 'exited' states, ensuring that members in these states are not re-enrolled or dis-enrolled inadvertently.
  • ID Type Model Migration: The ID deduplication manager's 'supported_id_document_type_ids' field has been migrated from the deprecated 'spp.id.type' model to 'spp.vocabulary.code', with an appropriate domain filter for ID types.
  • Visible Duplicate Reason: A new computed field, 'duplicate_reason', has been added to 'spp.program.membership' to display the specific reason for duplication directly on the form view when a member is flagged as 'duplicated'.
  • Improved Deduplication Summary: The program-level deduplication summary now provides a clearer report, distinguishing between newly identified duplicates and those that were already flagged.
  • Refactored Individual Deduplication Logic: The '_check_duplicate_by_individual' methods for both ID and phone deduplication have been rewritten to properly group duplicates by shared values and apply 'keep-one-enrolled' logic more effectively.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@emjay0921 emjay0921 marked this pull request as draft March 18, 2026 03:51
@codecov
Copy link

codecov bot commented Mar 18, 2026

Codecov Report

❌ Patch coverage is 92.55319% with 7 lines in your changes missing coverage. Please review.
✅ Project coverage is 70.85%. Comparing base (1a91296) to head (895e9e4).

Files with missing lines Patch % Lines
..._programs/models/managers/deduplication_manager.py 92.42% 5 Missing ⚠️
spp_programs/models/programs.py 84.61% 2 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             19.0     #113      +/-   ##
==========================================
+ Coverage   70.14%   70.85%   +0.70%     
==========================================
  Files         739      783      +44     
  Lines       43997    46652    +2655     
==========================================
+ Hits        30863    33055    +2192     
- Misses      13134    13597     +463     
Flag Coverage Δ
spp_api_v2 79.96% <ø> (ø)
spp_api_v2_change_request 66.66% <ø> (ø)
spp_api_v2_cycles 71.12% <ø> (ø)
spp_api_v2_data 64.41% <ø> (ø)
spp_api_v2_entitlements 70.19% <ø> (ø)
spp_api_v2_gis 71.52% <ø> (ø)
spp_api_v2_products 66.27% <ø> (ø)
spp_api_v2_service_points 70.94% <ø> (ø)
spp_api_v2_simulation 71.12% <ø> (ø)
spp_api_v2_vocabulary 57.26% <ø> (ø)
spp_audit 64.19% <ø> (ø)
spp_base_common 90.26% <ø> (ø)
spp_case_entitlements 97.61% <ø> (ø)
spp_case_programs 97.14% <ø> (ø)
spp_cel_event 85.11% <ø> (?)
spp_claim_169 58.11% <ø> (?)
spp_dci_client_dr 55.87% <ø> (?)
spp_dci_client_ibr 60.17% <ø> (?)
spp_programs 51.84% <92.55%> (+6.32%) ⬆️
spp_security 66.66% <ø> (ø)

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

Files with missing lines Coverage Δ
spp_programs/models/managers/program_manager.py 65.83% <100.00%> (+21.66%) ⬆️
spp_programs/models/program_membership.py 81.13% <100.00%> (+49.15%) ⬆️
spp_programs/models/programs.py 81.63% <84.61%> (+22.62%) ⬆️
..._programs/models/managers/deduplication_manager.py 84.65% <92.42%> (+63.70%) ⬆️

... and 45 files 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.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request provides significant improvements to the deduplication and enrollment workflows. The changes address several bugs, including providing more detailed reasons for duplicates, preventing state overwrites during enrollment, and migrating to the correct model for ID types. The refactoring of the individual deduplication logic for both IDs and phone numbers is a major improvement in correctness and clarity. The UI changes also enhance user visibility into duplicate records. I have one suggestion to improve performance by avoiding a potential N+1 query issue.

Comment on lines 330 to +339
individual_id_docs = {}
# Check ID Documents of each individual
reg_id_to_partner = {}
for i in individuals:
for x in i.reg_ids:
if x.id_type_id in self.supported_id_document_type_ids and (
(not x.expiry_date) or x.expiry_date > date.today()
):
id_doc_id_with_id_type_and_value = {x.id: x.id_type_id.name + "-" + x.value}
individual_id_docs.update(id_doc_id_with_id_type_and_value)
doc_key = x.id_type_id.display + "-" + x.value
individual_id_docs[x.id] = doc_key
reg_id_to_partner[x.id] = i.id

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This nested loop over individuals and their reg_ids can cause N+1 query performance issues, especially with a large number of beneficiaries. It's more efficient to fetch all relevant spp.registry.id records in a single search and then process them.

Suggested change
individual_id_docs = {}
# Check ID Documents of each individual
reg_id_to_partner = {}
for i in individuals:
for x in i.reg_ids:
if x.id_type_id in self.supported_id_document_type_ids and (
(not x.expiry_date) or x.expiry_date > date.today()
):
id_doc_id_with_id_type_and_value = {x.id: x.id_type_id.name + "-" + x.value}
individual_id_docs.update(id_doc_id_with_id_type_and_value)
doc_key = x.id_type_id.display + "-" + x.value
individual_id_docs[x.id] = doc_key
reg_id_to_partner[x.id] = i.id
individual_id_docs = {}
reg_id_to_partner = {}
all_reg_ids = self.env["spp.registry.id"].search([
("partner_id", "in", individuals.ids),
("id_type_id", "in", self.supported_id_document_type_ids.ids),
])
for x in all_reg_ids:
if (not x.expiry_date) or x.expiry_date > date.today():
doc_key = f"{x.id_type_id.display}-{x.value}"
individual_id_docs[x.id] = doc_key
reg_id_to_partner[x.id] = x.partner_id.id

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.

1 participant