feat(spp_programs): show cycle compliance status on registrant form#152
feat(spp_programs): show cycle compliance status on registrant form#152
Conversation
Add visibility into cycle-level compliance status from the registrant form. Users can now see at a glance which programs have non-compliant cycles and drill down to the specific cycle details. - Add latest_cycle_state computed field on spp.program.membership showing the most recent cycle state per registrant+program - Add "View Cycles" button on program membership rows opening filtered cycle memberships for that registrant+program - Add compliance_criteria computed field on spp.cycle.membership showing the program CEL expression when non-compliant - Add cycle_membership_count and non_compliant_cycle_count on res.partner with smart button - Add Non-Compliant filter to cycle membership search view Closes #860
There was a problem hiding this comment.
Code Review
This pull request introduces features to track and display compliance information and cycle membership status for registrants. Key additions include a compliance_criteria field on cycle memberships, a latest_cycle_state field on program memberships, and new statistics and action buttons on the registrant view. A performance issue was identified in the _compute_latest_cycle_state method, where an N+1 query pattern should be replaced with a batch query using _read_group to improve efficiency.
| def _compute_latest_cycle_state(self): | ||
| """Get the latest cycle membership state per registrant+program.""" | ||
| if not self: | ||
| return | ||
|
|
||
| for rec in self: | ||
| rec.latest_cycle_state = False | ||
|
|
||
| # Batch query: find latest cycle membership for each program membership | ||
| CycleMembership = self.env["spp.cycle.membership"] | ||
| for rec in self: | ||
| cycle_mem = CycleMembership.search( | ||
| [ | ||
| ("partner_id", "=", rec.partner_id.id), | ||
| ("cycle_id.program_id", "=", rec.program_id.id), | ||
| ], | ||
| order="id desc", | ||
| limit=1, | ||
| ) | ||
| if cycle_mem: | ||
| rec.latest_cycle_state = cycle_mem.state |
There was a problem hiding this comment.
The current implementation of _compute_latest_cycle_state performs a search() inside a loop, which leads to an N+1 query pattern. This will significantly degrade performance when viewing a list of program memberships (e.g., on the registrant form). It is highly recommended to use _read_group to fetch the latest cycle membership IDs for all records in the batch in a single query.
def _compute_latest_cycle_state(self):
"""Get the latest cycle membership state per registrant+program."""
for rec in self:
rec.latest_cycle_state = False
if not self:
return
# Batch query: find latest cycle membership ID for each partner+program pair
data = self.env["spp.cycle.membership"]._read_group(
domain=[
("partner_id", "in", self.partner_id.ids),
("cycle_id.program_id", "in", self.program_id.ids),
],
groupby=["partner_id", "cycle_id.program_id"],
aggregates=["id:max"],
)
# Map (partner_id, program_id) -> latest_membership_id
latest_ids = {(p.id, prog.id): max_id for p, prog, max_id in data}
# Fetch states for all latest memberships in one query
mems = self.env["spp.cycle.membership"].browse(latest_ids.values())
mem_states = {m.id: m.state for m in mems}
for rec in self:
mem_id = latest_ids.get((rec.partner_id.id, rec.program_id.id))
if mem_id:
rec.latest_cycle_state = mem_states.get(mem_id)
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## 19.0 #152 +/- ##
==========================================
- Coverage 71.06% 71.05% -0.02%
==========================================
Files 925 925
Lines 54704 54774 +70
==========================================
+ Hits 38876 38919 +43
- Misses 15828 15855 +27
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 a registrant is marked as non-compliant on a cycle membership, there is no way to see this from the registrant form. Users have to navigate through programs and cycles manually to find compliance issues. This makes it difficult to quickly identify beneficiaries with compliance problems.
How was the change implemented?
Latest Cycle Status column on program membership list
latest_cycle_statecomputed field onspp.program.membershipthat reads the most recent cycle membership state per registrant+programView Cycles button
Compliance Criteria column on cycle membership list
compliance_criteriacomputed field onspp.cycle.membershipper_capita_income < poverty_line)Smart button on registrant form
cycle_membership_countandnon_compliant_cycle_countonres.partnerSearch filter
New unit tests
N/A — existing 579 spp_programs tests all pass (0 failures)
Unit tests executed by the author
How to test manually
Related links