fix(spp_change_request_v2): let the registrant prefill through the freeze on an empty repaired detail (#443) - #536
Open
gonzalesedwin1123 wants to merge 5 commits into
Open
gonzalesedwin1123 wants to merge 5 commits into
gonzalesedwin1123 wants to merge 5 commits into
Conversation
…eeze on an empty repaired detail _ensure_detail() may bind a new detail row to a submitted request (19.0.3.1.10), but the registrant prefill that follows is a write to the mapped fields, and _assert_content_editable refused it whenever the request type carries field mappings, which every shipped Edit Individual / Edit Group type does through spp_cr_types_base. The module's own test type had no mappings, so the repair passed in isolation and failed on every real deployment. The freeze now accepts exactly one write past submission: the registrant's current values onto a detail that holds no proposed content yet. It is recognised by shape, not caller, so RPC cannot claim it; a detail already carrying a proposal stays frozen even for the registrant's own value, and any other value is refused on an empty detail too. The frozen-detail tests give the test types the shipped mappings so module CI exercises this shape, and cover Edit Group. Refs #443
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## 19.0 #536 +/- ##
=======================================
Coverage 76.91% 76.91%
=======================================
Files 704 704
Lines 45774 45784 +10
=======================================
+ Hits 35205 35216 +11
+ Misses 10569 10568 -1
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
…() and keep the freeze absolute Review round: the write-path carve-out validated the no-op claim against the prefill mapping while apply uses the apply mapping (target_field, transform), and its precondition (a detail with no protected content) is exactly the shape of an approved clear-every- field proposal, so it left a one-write slot on such details. _ensure_detail now passes _prefill_values() into the detail's create(), so the repair performs no post-submit write at all and _assert_content_editable keeps no exemption. _prefill_values() returns ids for record values, since create() does not accept recordsets where write() did. Tests assert the repaired detail arrives populated and that every post-submit write to a mapped field, the registrant's own value included, stays refused. Refs #443
This was referenced Sep 21, 2026
…opose a change Second review round. The rebuilt row applies nothing only as far as the prefill covers the apply mapping; a value the prefill declines to offer (a legacy future date of birth, or a mapping added later for a field the prefill does not know) would be applied as clearing that field, and the frozen row could not be corrected. A field-mapping request repaired after submission is now checked with the strategy's preview and refused with an explicit message when anything would change; a post-submit repair is logged. Tests apply a repaired request end to end and cover the future-birthdate refusal, the draft-time drop, a Many2one prefill value, and pin the test mappings against the shipped ones. Refs #443
Member
Author
|
Full-stack evidence: SP-MIS
|
gonzalesedwin1123
marked this pull request as ready for review
September 21, 2026 06:37
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #443 (weekly ci-full red). Second per-module PR; a real production bug, not a test problem.
Problem
19.0.3.1.10 let
_ensure_detail()bind a new detail row to a submitted change request (a request that never got one, or lost it, could not be opened from any context). The binding now passes, but_ensure_detail()then callsdetail.prefill_from_registrant(), which writes the registrant's current values to the mapped fields, and_assert_content_editablerefuses that write whenever the request type hasfield_mappingmappings. A fresh detail holdsFalseeverywhere, the prefill values are truthy, so "differs" →UserError: This change request has already been submitted for approval, so its proposed changes are locked.Every shipped Edit Individual / Edit Group type carries those mappings through
spp_cr_types_base, which every real deployment installs. The module's own test type is built fromCR_TYPE_DEFSwith no mappings, so the protected set was just{field_to_modify}andTestFrozenDetailBindingpassed in per-module CI while failing in the weekly SP-MIS stack.action_open_detail/action_goto_detailsboth go through_ensure_detail(), so on a real deployment the repair 19.0.3.1.10 set out to do still does not work.Fix
The prefill moves into the detail's
create():_ensure_detailbuilds the row with_prefill_values()(the registrant's current values for the prefill mapping, record values as ids) already in the create vals. The repair therefore performs no post-submit write at all, and_assert_content_editablekeeps no exemption: every write to a mapped field on a submitted request, the registrant's own value included, stays refused.Why the row must be prefilled rather than left empty:
field_mapping.applywrites empties on purpose ("a user may be intentionally clearing a field"), so approving an empty repaired detail would clear every mapped field on the registrant. Prefilled, it equals what the registrant already holds andapplyskips it.The first draft of this PR opened a shape-based carve-out in the freeze instead ("empty detail + registrant's own values"). The adversarial review showed it validated the no-op claim against the prefill mapping while
applyuses the apply mapping (target_field,transform), and that its precondition is exactly the shape of an approved clear-every-field proposal, so it left a one-write slot. The create-time prefill removes that surface entirely.prefill_from_registrant()keeps its public behaviour (a write, therefore subject to the freeze on submitted requests).spp_change_request_v219.0.3.1.16 → 19.0.3.1.17 + HISTORY. README/index.html left for CI's generator.Tests (
tests/test_frozen_detail_binding.py, 6 → 11)The class now gives the test-created
edit_individual/edit_grouptypes the mappingsspp_cr_types_baseships, so module CI exercises the real shape. Against the unfixed model that alone turns the repair tests red in module-only mode. New:prefill_from_registrant()on an empty submitted detail is refused (the freeze keeps no exemption);Evidence: with
-i spp_change_request_v2,spp_cr_types_base --test-tags /spp_change_request_v2:TestFrozenDetailBindingthe class went from0 failed, 2 error(s) of 6to0 failed, 0 error(s) of 11; module suite0 failed, 0 error(s) of 425 tests. All pre-commit hooks pass. An SP-MIS ci-full is dispatched on this branch; its failing set should lose exactly the twoTestFrozenDetailBindingerrors against the 2026-09-20 baseline.Pre-existing gaps found in review (not this PR's scope; filed separately)
spp.change.request.registrant_idis not among the fields frozen on submit;create()on detail models is unguarded, so a populated row can be crafted and bound to a detail-less submitted request through the 19.0.3.1.10 binding rule; Studio-generated detail models inherit neither the freeze nor the prefill.